Initial commit
This commit is contained in:
commit
c3d80f0c61
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
||||
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@ -0,0 +1,36 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Use a base image with node and build essentials
|
||||
FROM node:20-buster
|
||||
|
||||
# Install dependencies and Bun
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl git unzip && \
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
|
||||
# Add Bun to PATH
|
||||
ENV PATH="/root/.bun/bin:${PATH}"
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files first
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies using bun
|
||||
RUN bun install
|
||||
|
||||
# Copy the rest of the application code
|
||||
COPY . .
|
||||
|
||||
# Create logs directory
|
||||
RUN mkdir -p /app/logs && chmod 777 /app/logs
|
||||
|
||||
# Expose port 5002 explicitly
|
||||
EXPOSE 5002
|
||||
|
||||
# Set environment variables
|
||||
ENV LOG_PATH=/app/logs
|
||||
|
||||
# Use npm/bun scripts to run the server
|
||||
CMD ["bun", "run", "dev-server-bun"]
|
||||
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Node/Bun server example
|
||||
|
||||
This is a simple example of how to integrate tldraw's sync engine with a Node server or a Bun server.
|
||||
|
||||
Run `yarn dev-node` or `yarn dev-bun` in this folder to start the server + client.
|
||||
0
bunfig.toml
Normal file
0
bunfig.toml
Normal file
39
package.json
Normal file
39
package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev-server-bun": "bun --watch ./src/server/server.bun.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@fastify/cors": "^9.0.1",
|
||||
"@fastify/websocket": "^10.0.1",
|
||||
"@tldraw/sync": "3.6.1",
|
||||
"@tldraw/sync-core": "3.6.1",
|
||||
"@tldraw/tlschema": "3.6.1",
|
||||
"@vitejs/plugin-react-swc": "^3.7.0",
|
||||
"axios": "^1.7.2",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"fastify": "^4.28.1",
|
||||
"itty-router": "^5.0.17",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.24.1",
|
||||
"socket.io-client": "^4.8.0",
|
||||
"unfurl.js": "^6.4.0",
|
||||
"vite": "^5.3.3",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.6",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.11.0",
|
||||
"@types/ws": "^8.5.10",
|
||||
"concurrently": "^8.2.2",
|
||||
"lazyrepo": "0.0.0-alpha.27",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
2
src/axiosConfig.ts
Normal file
2
src/axiosConfig.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import axios from 'axios';
|
||||
export default axios;
|
||||
46
src/logger.ts
Normal file
46
src/logger.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const LOG_DIR = process.env.LOG_PATH || './logs';
|
||||
const LOG_FILE = join(LOG_DIR, 'server.log');
|
||||
|
||||
// Ensure log directory exists
|
||||
if (!existsSync(LOG_DIR)) {
|
||||
mkdirSync(LOG_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
type LogLevel = 'INFO' | 'ERROR' | 'DEBUG' | 'WARN';
|
||||
|
||||
function formatLog(level: LogLevel, message: string, data?: any): string {
|
||||
const timestamp = new Date().toISOString();
|
||||
const dataStr = data ? `\n${JSON.stringify(data, null, 2)}` : '';
|
||||
return `[${timestamp}] [${level}] ${message}${dataStr}\n`;
|
||||
}
|
||||
|
||||
export const logger = {
|
||||
info: (message: string, data?: any) => {
|
||||
const log = formatLog('INFO', message, data);
|
||||
console.log(log);
|
||||
Bun.write(LOG_FILE, log, { append: true });
|
||||
},
|
||||
|
||||
error: (message: string, data?: any) => {
|
||||
const log = formatLog('ERROR', message, data);
|
||||
console.error(log);
|
||||
Bun.write(LOG_FILE, log, { append: true });
|
||||
},
|
||||
|
||||
debug: (message: string, data?: any) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const log = formatLog('DEBUG', message, data);
|
||||
console.debug(log);
|
||||
Bun.write(LOG_FILE, log, { append: true });
|
||||
}
|
||||
},
|
||||
|
||||
warn: (message: string, data?: any) => {
|
||||
const log = formatLog('WARN', message, data);
|
||||
console.warn(log);
|
||||
Bun.write(LOG_FILE, log, { append: true });
|
||||
}
|
||||
};
|
||||
53
src/server/assets.ts
Normal file
53
src/server/assets.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { mkdir, readFile, writeFile } from 'fs/promises'
|
||||
import { join, resolve } from 'path'
|
||||
import { Readable } from 'stream'
|
||||
import crypto from 'crypto'
|
||||
|
||||
// We are just using the filesystem to store assets
|
||||
const DIR = resolve('./.assets')
|
||||
|
||||
function hashFileName(fileName: string): string {
|
||||
return crypto.createHash('md5').update(fileName).digest('hex')
|
||||
}
|
||||
|
||||
export async function storeAsset(id: string, stream: Readable) {
|
||||
console.log(`Storing asset with ID: ${id}`)
|
||||
console.log(`Asset directory: ${DIR}`)
|
||||
|
||||
try {
|
||||
await mkdir(DIR, { recursive: true })
|
||||
console.log(`Directory created or already exists: ${DIR}`)
|
||||
|
||||
const hashedFileName = hashFileName(id)
|
||||
const filePath = join(DIR, hashedFileName)
|
||||
console.log(`Writing file to path: ${filePath}`)
|
||||
|
||||
const chunks = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
const buffer = Buffer.concat(chunks)
|
||||
|
||||
await writeFile(filePath, buffer)
|
||||
console.log(`Asset stored successfully with ID: ${id}`)
|
||||
} catch (error) {
|
||||
console.error(`Error storing asset with ID: ${id}`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadAsset(id: string) {
|
||||
console.log(`Loading asset with ID: ${id}`)
|
||||
const hashedFileName = hashFileName(id)
|
||||
const filePath = join(DIR, hashedFileName)
|
||||
console.log(`Reading file from path: ${filePath}`)
|
||||
|
||||
try {
|
||||
const data = await readFile(filePath)
|
||||
console.log(`Asset loaded successfully with ID: ${id}`)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error(`Error loading asset with ID: ${id}`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
121
src/server/rooms.ts
Normal file
121
src/server/rooms.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { RoomSnapshot, TLSocketRoom } from '@tldraw/sync-core'
|
||||
import { TLStoreSchema } from '@tldraw/tlschema'
|
||||
import { mkdir, readFile, writeFile } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { TLSchema, TLStore, TLStoreOptions } from 'tldraw'
|
||||
import { logger } from './../logger'
|
||||
|
||||
// For this example we're just saving data to the local filesystem
|
||||
const DIR = './.rooms'
|
||||
async function readSnapshotIfExists(roomId: string) {
|
||||
try {
|
||||
const data = await readFile(join(DIR, roomId))
|
||||
const snapshot = JSON.parse(data.toString()) ?? undefined;
|
||||
logger.info(`📥 Loaded snapshot for room: ${roomId}`);
|
||||
return snapshot;
|
||||
} catch (e) {
|
||||
logger.warn(`⚠️ No existing snapshot found for room: ${roomId}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSnapshot(roomId: string, snapshot: RoomSnapshot) {
|
||||
try {
|
||||
await mkdir(DIR, { recursive: true });
|
||||
await writeFile(join(DIR, roomId), JSON.stringify(snapshot));
|
||||
logger.info(`💾 Saved snapshot for room: ${roomId}`);
|
||||
} catch (error) {
|
||||
logger.error(`❌ Failed to save snapshot for room: ${roomId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// We'll keep an in-memory map of rooms and their data
|
||||
interface RoomState {
|
||||
room: TLSocketRoom<any, void>
|
||||
id: string
|
||||
needsPersist: boolean
|
||||
lastActivity: number
|
||||
connectedSessions: Set<string>
|
||||
}
|
||||
|
||||
const rooms = new Map<string, RoomState>()
|
||||
|
||||
// Very simple mutex using promise chaining, to avoid race conditions
|
||||
// when loading rooms. In production you probably want one mutex per room
|
||||
// to avoid unnecessary blocking!
|
||||
let mutex = Promise.resolve<null | Error>(null)
|
||||
|
||||
export async function makeOrLoadRoom(
|
||||
roomId: string,
|
||||
schema: TLSchema,
|
||||
options?: Partial<TLStoreOptions>
|
||||
): Promise<TLSocketRoom<any, void>> {
|
||||
mutex = mutex
|
||||
.then(async () => {
|
||||
if (rooms.has(roomId)) {
|
||||
const roomState = await rooms.get(roomId)!
|
||||
if (!roomState.room.isClosed()) {
|
||||
logger.info(`🔄 Using existing room: ${roomId}`);
|
||||
return null // all good
|
||||
}
|
||||
}
|
||||
logger.info(`🔄 Loading room: ${roomId}`);
|
||||
const initialSnapshot = await readSnapshotIfExists(roomId)
|
||||
|
||||
const roomState: RoomState = {
|
||||
needsPersist: false,
|
||||
id: roomId,
|
||||
lastActivity: Date.now(),
|
||||
connectedSessions: new Set(),
|
||||
room: new TLSocketRoom({
|
||||
initialSnapshot,
|
||||
schema: schema,
|
||||
onSessionRemoved(room, args) {
|
||||
logger.info(`👋 Client disconnected from room: ${roomId}`);
|
||||
roomState.connectedSessions.delete(args.sessionId);
|
||||
roomState.lastActivity = Date.now();
|
||||
if (args.numSessionsRemaining === 0) {
|
||||
logger.info(`🔒 Closing empty room: ${roomId}`);
|
||||
room.close();
|
||||
}
|
||||
},
|
||||
onDataChange() {
|
||||
roomState.needsPersist = true;
|
||||
roomState.lastActivity = Date.now();
|
||||
}
|
||||
}),
|
||||
}
|
||||
rooms.set(roomId, roomState)
|
||||
return null // all good
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`❌ Error making/loading room: ${roomId}`);
|
||||
// return errors as normal values to avoid stopping the mutex chain
|
||||
return error
|
||||
})
|
||||
|
||||
const err = await mutex
|
||||
if (err) throw err
|
||||
return rooms.get(roomId)!.room
|
||||
}
|
||||
|
||||
// Do persistence on a regular interval.
|
||||
// In production you probably want a smarter system with throttling.
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const roomState of rooms.values()) {
|
||||
if (roomState.needsPersist) {
|
||||
// persist room
|
||||
roomState.needsPersist = false;
|
||||
logger.info(`💾 Saving snapshot for room: ${roomState.id}`);
|
||||
saveSnapshot(roomState.id, roomState.room.getCurrentSnapshot());
|
||||
}
|
||||
if (roomState.room.isClosed()) {
|
||||
logger.info(`🗑️ Deleting closed room: ${roomState.id}`);
|
||||
rooms.delete(roomState.id);
|
||||
} else if (now - roomState.lastActivity > 30 * 60 * 1000) { // 30 minutes inactivity
|
||||
logger.info(`⏰ Closing inactive room: ${roomState.id}`);
|
||||
roomState.room.close();
|
||||
}
|
||||
}
|
||||
}, 2000)
|
||||
199
src/server/schema.ts
Normal file
199
src/server/schema.ts
Normal file
@ -0,0 +1,199 @@
|
||||
import { createTLSchema, defaultShapeSchemas, defaultBindingSchemas } from '@tldraw/tlschema'
|
||||
import { ccBindingProps, ccShapeProps } from '../utils/tldraw/cc-base/cc-props'
|
||||
import { ccBindingMigrations, ccShapeMigrations } from '../utils/tldraw/cc-base/cc-migrations'
|
||||
import { ccGraphShapeProps } from '../utils/tldraw/cc-base/cc-graph-props'
|
||||
import { ccGraphMigrations } from '../utils/tldraw/cc-base/cc-graph-migrations'
|
||||
|
||||
export const server_schema_default = createTLSchema({
|
||||
shapes: {
|
||||
...defaultShapeSchemas,
|
||||
'cc-base': {
|
||||
props: ccShapeProps.base,
|
||||
migrations: ccShapeMigrations.base,
|
||||
},
|
||||
'cc-live-transcription': {
|
||||
props: ccShapeProps.liveTranscription,
|
||||
migrations: ccShapeMigrations.liveTranscription,
|
||||
},
|
||||
'cc-calendar': {
|
||||
props: ccShapeProps.calendar,
|
||||
migrations: ccShapeMigrations.calendar,
|
||||
},
|
||||
'cc-settings': {
|
||||
props: ccShapeProps.settings,
|
||||
migrations: ccShapeMigrations.settings,
|
||||
},
|
||||
'cc-slideshow': {
|
||||
props: ccShapeProps.slideshow,
|
||||
migrations: ccShapeMigrations.slideshow,
|
||||
},
|
||||
'cc-slide': {
|
||||
props: ccShapeProps.slide,
|
||||
migrations: ccShapeMigrations.slide,
|
||||
},
|
||||
'cc-web-browser': {
|
||||
props: ccShapeProps.webBrowser,
|
||||
migrations: ccShapeMigrations.webBrowser,
|
||||
},
|
||||
'cc-search': {
|
||||
props: ccShapeProps.search,
|
||||
migrations: ccShapeMigrations.search,
|
||||
},
|
||||
'cc-youtube-embed': {
|
||||
props: ccShapeProps['cc-youtube-embed'],
|
||||
migrations: ccShapeMigrations['cc-youtube-embed'],
|
||||
},
|
||||
// Graph shapes
|
||||
'cc-user-node': {
|
||||
props: ccGraphShapeProps['cc-user-node'],
|
||||
migrations: ccGraphMigrations['cc-user-node'],
|
||||
},
|
||||
'cc-teacher-node': {
|
||||
props: ccGraphShapeProps['cc-teacher-node'],
|
||||
migrations: ccGraphMigrations['cc-teacher-node'],
|
||||
},
|
||||
'cc-student-node': {
|
||||
props: ccGraphShapeProps['cc-student-node'],
|
||||
migrations: ccGraphMigrations['cc-student-node'],
|
||||
},
|
||||
'cc-calendar-node': {
|
||||
props: ccGraphShapeProps['cc-calendar-node'],
|
||||
migrations: ccGraphMigrations['cc-calendar-node'],
|
||||
},
|
||||
'cc-calendar-year-node': {
|
||||
props: ccGraphShapeProps['cc-calendar-year-node'],
|
||||
migrations: ccGraphMigrations['cc-calendar-year-node'],
|
||||
},
|
||||
'cc-calendar-month-node': {
|
||||
props: ccGraphShapeProps['cc-calendar-month-node'],
|
||||
migrations: ccGraphMigrations['cc-calendar-month-node'],
|
||||
},
|
||||
'cc-calendar-week-node': {
|
||||
props: ccGraphShapeProps['cc-calendar-week-node'],
|
||||
migrations: ccGraphMigrations['cc-calendar-week-node'],
|
||||
},
|
||||
'cc-calendar-day-node': {
|
||||
props: ccGraphShapeProps['cc-calendar-day-node'],
|
||||
migrations: ccGraphMigrations['cc-calendar-day-node'],
|
||||
},
|
||||
'cc-calendar-time-chunk-node': {
|
||||
props: ccGraphShapeProps['cc-calendar-time-chunk-node'],
|
||||
migrations: ccGraphMigrations['cc-calendar-time-chunk-node'],
|
||||
},
|
||||
'cc-school-node': {
|
||||
props: ccGraphShapeProps['cc-school-node'],
|
||||
migrations: ccGraphMigrations['cc-school-node'],
|
||||
},
|
||||
'cc-department-node': {
|
||||
props: ccGraphShapeProps['cc-department-node'],
|
||||
migrations: ccGraphMigrations['cc-department-node'],
|
||||
},
|
||||
'cc-room-node': {
|
||||
props: ccGraphShapeProps['cc-room-node'],
|
||||
migrations: ccGraphMigrations['cc-room-node'],
|
||||
},
|
||||
'cc-subject-class-node': {
|
||||
props: ccGraphShapeProps['cc-subject-class-node'],
|
||||
migrations: ccGraphMigrations['cc-subject-class-node'],
|
||||
},
|
||||
'cc-pastoral-structure-node': {
|
||||
props: ccGraphShapeProps['cc-pastoral-structure-node'],
|
||||
migrations: ccGraphMigrations['cc-pastoral-structure-node'],
|
||||
},
|
||||
'cc-year-group-node': {
|
||||
props: ccGraphShapeProps['cc-year-group-node'],
|
||||
migrations: ccGraphMigrations['cc-year-group-node'],
|
||||
},
|
||||
'cc-curriculum-structure-node': {
|
||||
props: ccGraphShapeProps['cc-curriculum-structure-node'],
|
||||
migrations: ccGraphMigrations['cc-curriculum-structure-node'],
|
||||
},
|
||||
'cc-key-stage-node': {
|
||||
props: ccGraphShapeProps['cc-key-stage-node'],
|
||||
migrations: ccGraphMigrations['cc-key-stage-node'],
|
||||
},
|
||||
'cc-key-stage-syllabus-node': {
|
||||
props: ccGraphShapeProps['cc-key-stage-syllabus-node'],
|
||||
migrations: ccGraphMigrations['cc-key-stage-syllabus-node'],
|
||||
},
|
||||
'cc-year-group-syllabus-node': {
|
||||
props: ccGraphShapeProps['cc-year-group-syllabus-node'],
|
||||
migrations: ccGraphMigrations['cc-year-group-syllabus-node'],
|
||||
},
|
||||
'cc-subject-node': {
|
||||
props: ccGraphShapeProps['cc-subject-node'],
|
||||
migrations: ccGraphMigrations['cc-subject-node'],
|
||||
},
|
||||
'cc-topic-node': {
|
||||
props: ccGraphShapeProps['cc-topic-node'],
|
||||
migrations: ccGraphMigrations['cc-topic-node'],
|
||||
},
|
||||
'cc-topic-lesson-node': {
|
||||
props: ccGraphShapeProps['cc-topic-lesson-node'],
|
||||
migrations: ccGraphMigrations['cc-topic-lesson-node'],
|
||||
},
|
||||
'cc-learning-statement-node': {
|
||||
props: ccGraphShapeProps['cc-learning-statement-node'],
|
||||
migrations: ccGraphMigrations['cc-learning-statement-node'],
|
||||
},
|
||||
'cc-science-lab-node': {
|
||||
props: ccGraphShapeProps['cc-science-lab-node'],
|
||||
migrations: ccGraphMigrations['cc-science-lab-node'],
|
||||
},
|
||||
'cc-teacher-timetable-node': {
|
||||
props: ccGraphShapeProps['cc-teacher-timetable-node'],
|
||||
migrations: ccGraphMigrations['cc-teacher-timetable-node'],
|
||||
},
|
||||
'cc-timetable-lesson-node': {
|
||||
props: ccGraphShapeProps['cc-timetable-lesson-node'],
|
||||
migrations: ccGraphMigrations['cc-timetable-lesson-node'],
|
||||
},
|
||||
'cc-planned-lesson-node': {
|
||||
props: ccGraphShapeProps['cc-planned-lesson-node'],
|
||||
migrations: ccGraphMigrations['cc-planned-lesson-node'],
|
||||
},
|
||||
'cc-school-timetable-node': {
|
||||
props: ccGraphShapeProps['cc-school-timetable-node'],
|
||||
migrations: ccGraphMigrations['cc-school-timetable-node'],
|
||||
},
|
||||
'cc-academic-year-node': {
|
||||
props: ccGraphShapeProps['cc-academic-year-node'],
|
||||
migrations: ccGraphMigrations['cc-academic-year-node'],
|
||||
},
|
||||
'cc-academic-term-node': {
|
||||
props: ccGraphShapeProps['cc-academic-term-node'],
|
||||
migrations: ccGraphMigrations['cc-academic-term-node'],
|
||||
},
|
||||
'cc-academic-week-node': {
|
||||
props: ccGraphShapeProps['cc-academic-week-node'],
|
||||
migrations: ccGraphMigrations['cc-academic-week-node'],
|
||||
},
|
||||
'cc-academic-day-node': {
|
||||
props: ccGraphShapeProps['cc-academic-day-node'],
|
||||
migrations: ccGraphMigrations['cc-academic-day-node'],
|
||||
},
|
||||
'cc-academic-period-node': {
|
||||
props: ccGraphShapeProps['cc-academic-period-node'],
|
||||
migrations: ccGraphMigrations['cc-academic-period-node'],
|
||||
},
|
||||
'cc-registration-period-node': {
|
||||
props: ccGraphShapeProps['cc-registration-period-node'],
|
||||
migrations: ccGraphMigrations['cc-registration-period-node'],
|
||||
},
|
||||
'cc-user-teacher-timetable-node': {
|
||||
props: ccGraphShapeProps['cc-user-teacher-timetable-node'],
|
||||
migrations: ccGraphMigrations['cc-user-teacher-timetable-node'],
|
||||
},
|
||||
'cc-user-timetable-lesson-node': {
|
||||
props: ccGraphShapeProps['cc-user-timetable-lesson-node'],
|
||||
migrations: ccGraphMigrations['cc-user-timetable-lesson-node'],
|
||||
},
|
||||
},
|
||||
bindings: {
|
||||
...defaultBindingSchemas,
|
||||
'cc-slide-layout': {
|
||||
props: ccBindingProps['cc-slide-layout'],
|
||||
migrations: ccBindingMigrations['cc-slide-layout'],
|
||||
},
|
||||
},
|
||||
})
|
||||
199
src/server/server.bun.ts
Normal file
199
src/server/server.bun.ts
Normal file
@ -0,0 +1,199 @@
|
||||
// External imports
|
||||
import { TLSocketRoom } from '@tldraw/sync-core'
|
||||
import { IRequest, Router, RouterType, cors, json } from 'itty-router'
|
||||
import { Readable } from 'stream'
|
||||
import {
|
||||
createTLStore,
|
||||
} from 'tldraw'
|
||||
// Internal imports
|
||||
import { loadAsset, storeAsset } from './assets'
|
||||
import { makeOrLoadRoom } from './rooms'
|
||||
import { unfurl } from './unfurl'
|
||||
import { server_schema_default } from './schema'
|
||||
import { logger } from './../logger'
|
||||
|
||||
// Add debug logging for environment variables
|
||||
logger.info('Environment variables:', {
|
||||
PORT: process.env.PORT,
|
||||
PORT_TLDRAW_SYNC: process.env.PORT_TLDRAW_SYNC,
|
||||
NODE_ENV: process.env.NODE_ENV
|
||||
});
|
||||
|
||||
// Be explicit about port precedence
|
||||
const PORT = process.env.PORT_TLDRAW_SYNC || 5002
|
||||
|
||||
// Log the port being used
|
||||
logger.info(`Using port: ${PORT}`)
|
||||
|
||||
const { corsify, preflight } = cors({ origin: '*' })
|
||||
|
||||
const router: RouterType<IRequest, any, any> = Router()
|
||||
.all('*', preflight)
|
||||
|
||||
.get(`/connect/:roomId`, async (req) => {
|
||||
const {roomId} = req.params
|
||||
const {sessionId} = req.query
|
||||
logger.info(`Connecting to room: ${roomId}, session: ${sessionId}`)
|
||||
server.upgrade(req, { data: { roomId, sessionId } })
|
||||
return new Response(null, { status: 101 })
|
||||
})
|
||||
|
||||
.put(`/uploads/:id`, async (req) => {
|
||||
const {id} = req.params;
|
||||
logger.info(`Received upload request for ID: ${id}`);
|
||||
|
||||
try {
|
||||
const buffer = await req.arrayBuffer(); // Directly convert the incoming request body to an ArrayBuffer
|
||||
const stream = Readable.from(Buffer.from(buffer)); // Convert ArrayBuffer to Node.js Readable Stream
|
||||
|
||||
await storeAsset(id, stream);
|
||||
const response = new Response(JSON.stringify({ ok: true }), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
},
|
||||
status: 200
|
||||
}); // TODO: Unsafe, change
|
||||
logger.info(`Upload successful for ID: ${id}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
logger.error(`Error storing asset with ID: ${id}`, error);
|
||||
return new Response('Internal Server Error', { status: 500 });
|
||||
}
|
||||
})
|
||||
|
||||
.get(`/uploads/:id`, async (req) => {
|
||||
const id = (req.params as any).id as string
|
||||
logger.info(`Received request to load asset with ID: ${id}`)
|
||||
try {
|
||||
const asset = await loadAsset(id)
|
||||
const response = new Response(asset)
|
||||
response.headers.set('Access-Control-Allow-Origin', '*') // TODO: Unsafe, change
|
||||
logger.info(`Asset loaded successfully for ID: ${id}`)
|
||||
return response
|
||||
} catch (error) {
|
||||
logger.error(`Error loading asset with ID: ${id}`, error)
|
||||
return new Response('Internal Server Error', { status: 500 })
|
||||
}
|
||||
})
|
||||
|
||||
.get(`/unfurl`, async (req) => {
|
||||
const url = (req.query as any).url as string
|
||||
logger.info(`Received unfurl request for URL: ${url}`)
|
||||
try {
|
||||
const data = await unfurl(url)
|
||||
const response = json(data)
|
||||
response.headers.set('Access-Control-Allow-Origin', '*') // TODO: Unsafe, change
|
||||
logger.info(`Unfurling successful for URL: ${url}`)
|
||||
return response
|
||||
} catch (error) {
|
||||
logger.error(`Error unfurling URL: ${url}`, error)
|
||||
return new Response('Internal Server Error', { status: 500 })
|
||||
}
|
||||
})
|
||||
|
||||
.all('*', (req) => {
|
||||
logger.info(`Received request for unknown route: ${req.url}`);
|
||||
const response = new Response('Not found', { status: 404 });
|
||||
response.headers.set('Access-Control-Allow-Origin', '*'); // TODO: Unsafe, change
|
||||
return response;
|
||||
})
|
||||
|
||||
const server = Bun.serve<{ room?: TLSocketRoom<any, void>; sessionId: string; roomId: string }>({
|
||||
port: parseInt(PORT as string), // Ensure it's parsed as a number
|
||||
fetch(req) {
|
||||
try {
|
||||
logger.info(`Server started on port: ${PORT}`) // Add explicit port logging
|
||||
logger.info('Received request: ', req.url)
|
||||
return router.fetch(req).then(corsify)
|
||||
} catch (e) {
|
||||
logger.error('Error handling request: ', e)
|
||||
return new Response('Something went wrong', {
|
||||
status: 500,
|
||||
})
|
||||
}
|
||||
},
|
||||
websocket: {
|
||||
async open(socket) {
|
||||
logger.debug(`WebSocket connection attempt for room: ${socket.data.roomId}`, {
|
||||
sessionId: socket.data.sessionId
|
||||
});
|
||||
try {
|
||||
const { sessionId, roomId } = socket.data;
|
||||
if (!sessionId || !roomId) {
|
||||
logger.error('Missing sessionId or roomId in WebSocket connection data', {
|
||||
sessionId,
|
||||
roomId
|
||||
});
|
||||
socket.close(4000, 'Missing data');
|
||||
return;
|
||||
}
|
||||
logger.info(`WebSocket opened for room: ${roomId}, session: ${sessionId}`);
|
||||
const room = await makeOrLoadRoom(roomId, server_schema_default);
|
||||
if (!room) {
|
||||
logger.error('Failed to create or load room', {
|
||||
roomId,
|
||||
sessionId
|
||||
});
|
||||
socket.close(4001, 'Failed to load room');
|
||||
return;
|
||||
}
|
||||
room.handleSocketConnect({ sessionId, socket });
|
||||
socket.data.room = room;
|
||||
logger.info(`Successfully connected to room: ${roomId}`, {
|
||||
sessionId,
|
||||
roomId
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error during WebSocket open:', error);
|
||||
socket.close(1011, 'Internal error');
|
||||
}
|
||||
},
|
||||
async message(ws, message) {
|
||||
try {
|
||||
logger.debug(`WebSocket message for session: ${ws.data.sessionId}`, {
|
||||
message,
|
||||
roomId: ws.data.roomId
|
||||
});
|
||||
if (!ws.data.room) {
|
||||
logger.error('No room found for WebSocket message', {
|
||||
sessionId: ws.data.sessionId,
|
||||
roomId: ws.data.roomId
|
||||
});
|
||||
ws.close(4002, 'No room found');
|
||||
return;
|
||||
}
|
||||
ws.data.room.handleSocketMessage(ws.data.sessionId, message);
|
||||
} catch (error) {
|
||||
logger.error('Error handling WebSocket message:', error);
|
||||
ws.close(1011, 'Message handling error');
|
||||
}
|
||||
},
|
||||
drain(ws) {
|
||||
logger.info(`WebSocket drain for session: ${ws.data.sessionId}`, {
|
||||
roomId: ws.data.roomId
|
||||
});
|
||||
ws.close();
|
||||
},
|
||||
close(ws) {
|
||||
logger.info(`WebSocket closed for session: ${ws.data.sessionId}`, {
|
||||
roomId: ws.data.roomId
|
||||
});
|
||||
if (ws.data.room) {
|
||||
ws.data.room.handleSocketClose(ws.data.sessionId);
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Add explicit logging of the server configuration
|
||||
logger.info('Server configuration:', {
|
||||
port: server.port,
|
||||
hostname: server.hostname
|
||||
})
|
||||
|
||||
logger.info(`Listening for connections on URL: ${server.url}`)
|
||||
|
||||
logger.info(`Listening on localhost:${PORT}`)
|
||||
|
||||
logger.info(`Server: ${server}`)
|
||||
14
src/server/unfurl.ts
Normal file
14
src/server/unfurl.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import _unfurl from 'unfurl.js'
|
||||
|
||||
export async function unfurl(url: string) {
|
||||
const { title, description, open_graph, twitter_card, favicon } = await _unfurl.unfurl(url)
|
||||
|
||||
const image = open_graph?.images?.[0]?.url || twitter_card?.images?.[0]?.url
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
favicon,
|
||||
}
|
||||
}
|
||||
310
src/types/graph_node_types.ts
Normal file
310
src/types/graph_node_types.ts
Normal file
@ -0,0 +1,310 @@
|
||||
export interface BaseNodeInterface {
|
||||
w: number;
|
||||
h: number;
|
||||
color: string;
|
||||
__primarylabel__: string;
|
||||
unique_id: string;
|
||||
path: string;
|
||||
created: string;
|
||||
merged: string;
|
||||
}
|
||||
|
||||
// Users
|
||||
export interface UserNodeInterface extends BaseNodeInterface
|
||||
{
|
||||
user_id: string;
|
||||
user_type: string;
|
||||
user_name: string;
|
||||
user_email: string;
|
||||
worker_node_data: string;
|
||||
}
|
||||
|
||||
export interface DeveloperNodeInterface extends BaseNodeInterface{
|
||||
user_id: string;
|
||||
user_type: string;
|
||||
user_name: string;
|
||||
user_email: string;
|
||||
}
|
||||
|
||||
export interface TeacherNodeInterface extends BaseNodeInterface{
|
||||
teacher_code: string;
|
||||
teacher_name_formal: string;
|
||||
teacher_email: string;
|
||||
worker_db_name: string;
|
||||
}
|
||||
|
||||
export interface StudentNodeInterface extends BaseNodeInterface{
|
||||
student_code: string;
|
||||
student_name_formal: string;
|
||||
student_email: string;
|
||||
worker_db_name: string;
|
||||
}
|
||||
|
||||
export interface StandardUserNodeInterface extends BaseNodeInterface{
|
||||
user_id: string;
|
||||
user_type: string;
|
||||
user_name: string;
|
||||
user_email: string;
|
||||
}
|
||||
|
||||
export interface SchoolAdminNodeInterface extends BaseNodeInterface {
|
||||
user_id: string;
|
||||
user_type: string;
|
||||
user_name: string;
|
||||
user_email: string;
|
||||
}
|
||||
|
||||
// Calendar
|
||||
export interface CalendarNodeInterface extends BaseNodeInterface {
|
||||
name: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
export interface CalendarYearNodeInterface extends BaseNodeInterface {
|
||||
year: string;
|
||||
}
|
||||
|
||||
export interface CalendarMonthNodeInterface extends BaseNodeInterface {
|
||||
year: string;
|
||||
month: string;
|
||||
month_name: string;
|
||||
}
|
||||
|
||||
export interface CalendarWeekNodeInterface extends BaseNodeInterface {
|
||||
start_date: string;
|
||||
week_number: string;
|
||||
iso_week: string;
|
||||
}
|
||||
|
||||
export interface CalendarDayNodeInterface extends BaseNodeInterface {
|
||||
date: string;
|
||||
day_of_week: string;
|
||||
iso_day: string;
|
||||
}
|
||||
|
||||
export interface CalendarTimeChunkNodeInterface extends BaseNodeInterface {
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
// School
|
||||
export interface SchoolNodeInterface extends BaseNodeInterface
|
||||
{
|
||||
school_name: string;
|
||||
school_website: string;
|
||||
school_uuid: string;
|
||||
}
|
||||
|
||||
export interface DepartmentNodeInterface extends BaseNodeInterface
|
||||
{
|
||||
department_name: string;
|
||||
}
|
||||
|
||||
export interface RoomNodeInterface extends BaseNodeInterface {
|
||||
room_code: string;
|
||||
room_name: string;
|
||||
}
|
||||
|
||||
export interface SubjectClassNodeInterface extends BaseNodeInterface
|
||||
{
|
||||
subject_class_code: string;
|
||||
year_group: string;
|
||||
subject: string;
|
||||
subject_code: string;
|
||||
}
|
||||
|
||||
// Curriculum
|
||||
export interface PastoralStructureNodeInterface extends BaseNodeInterface {
|
||||
// No additional properties
|
||||
}
|
||||
|
||||
export interface YearGroupNodeInterface extends BaseNodeInterface {
|
||||
year_group: string;
|
||||
year_group_name: string;
|
||||
}
|
||||
|
||||
export interface CurriculumStructureNodeInterface extends BaseNodeInterface {
|
||||
}
|
||||
|
||||
export interface KeyStageNodeInterface extends BaseNodeInterface {
|
||||
key_stage_name: string;
|
||||
key_stage: string;
|
||||
}
|
||||
|
||||
export interface KeyStageSyllabusNodeInterface extends BaseNodeInterface {
|
||||
ks_syllabus_id: string;
|
||||
ks_syllabus_name: string;
|
||||
ks_syllabus_key_stage: string;
|
||||
ks_syllabus_subject: string;
|
||||
ks_syllabus_subject_code: string;
|
||||
}
|
||||
|
||||
export interface YearGroupSyllabusNodeInterface extends BaseNodeInterface {
|
||||
yr_syllabus_id: string;
|
||||
yr_syllabus_name: string;
|
||||
yr_syllabus_year_group: string;
|
||||
yr_syllabus_subject: string;
|
||||
yr_syllabus_subject_code: string;
|
||||
}
|
||||
|
||||
|
||||
export interface SubjectNodeInterface extends BaseNodeInterface {
|
||||
subject_code: string;
|
||||
subject_name: string;
|
||||
}
|
||||
|
||||
|
||||
export interface TopicNodeInterface extends BaseNodeInterface {
|
||||
topic_id: string;
|
||||
topic_title: string;
|
||||
total_number_of_lessons_for_topic: string;
|
||||
topic_type: string;
|
||||
topic_assessment_type: string;
|
||||
|
||||
}
|
||||
|
||||
export interface TopicLessonNodeInterface extends BaseNodeInterface {
|
||||
topic_lesson_id: string;
|
||||
topic_lesson_title: string;
|
||||
topic_lesson_type: string;
|
||||
topic_lesson_length: string;
|
||||
topic_lesson_suggested_activities: string;
|
||||
topic_lesson_skills_learned: string;
|
||||
topic_lesson_weblinks: string;
|
||||
}
|
||||
|
||||
|
||||
export interface LearningStatementNodeInterface extends BaseNodeInterface {
|
||||
lesson_learning_statement_id: string;
|
||||
lesson_learning_statement: string;
|
||||
lesson_learning_statement_type: string;
|
||||
}
|
||||
|
||||
export interface ScienceLabNodeInterface extends BaseNodeInterface {
|
||||
science_lab_id: string;
|
||||
science_lab_title: string;
|
||||
science_lab_summary: string;
|
||||
science_lab_requirements: string;
|
||||
science_lab_procedure: string;
|
||||
science_lab_safety: string;
|
||||
science_lab_weblinks: string;
|
||||
}
|
||||
|
||||
|
||||
// School Timetable
|
||||
export interface SchoolTimetableNodeInterface extends BaseNodeInterface {
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
export interface AcademicYearNodeInterface extends BaseNodeInterface {
|
||||
year: string;
|
||||
}
|
||||
|
||||
export interface AcademicTermNodeInterface extends BaseNodeInterface {
|
||||
term_name: string;
|
||||
term_number: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
export interface AcademicTermBreakNodeInterface extends BaseNodeInterface {
|
||||
term_break_name: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
export interface AcademicWeekNodeInterface extends BaseNodeInterface {
|
||||
academic_week_number: string;
|
||||
start_date: string;
|
||||
week_type: string;
|
||||
}
|
||||
|
||||
export interface HolidayWeekNodeInterface extends BaseNodeInterface {
|
||||
start_date: string;
|
||||
}
|
||||
|
||||
export interface AcademicDayNodeInterface extends BaseNodeInterface {
|
||||
academic_day: string;
|
||||
date: string;
|
||||
day_of_week: string;
|
||||
day_type: string;
|
||||
}
|
||||
|
||||
export interface OffTimetableDayNodeInterface extends BaseNodeInterface {
|
||||
date: string;
|
||||
day_of_week: string;
|
||||
}
|
||||
|
||||
export interface StaffDayNodeInterface extends BaseNodeInterface {
|
||||
date: string;
|
||||
day_of_week: string;
|
||||
}
|
||||
|
||||
export interface HolidayDayNodeInterface extends BaseNodeInterface {
|
||||
date: string;
|
||||
day_of_week: string;
|
||||
}
|
||||
|
||||
export interface AcademicPeriodNodeInterface extends BaseNodeInterface {
|
||||
name: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
period_code: string;
|
||||
}
|
||||
|
||||
export interface RegistrationPeriodNodeInterface extends BaseNodeInterface {
|
||||
name: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
period_code: string;
|
||||
}
|
||||
|
||||
export interface BreakPeriodNodeInterface extends BaseNodeInterface {
|
||||
name: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
export interface OffTimetablePeriodNodeInterface extends BaseNodeInterface {
|
||||
name: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
// Teacher timetable
|
||||
export interface TeacherTimetableNodeInterface extends BaseNodeInterface {
|
||||
}
|
||||
|
||||
export interface TimetableLessonNodeInterface extends BaseNodeInterface {
|
||||
subject_class: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
period_code: string;
|
||||
}
|
||||
|
||||
export interface PlannedLessonNodeInterface extends BaseNodeInterface {
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
period_code: string;
|
||||
subject_class: string;
|
||||
year_group: string;
|
||||
subject: string;
|
||||
teacher_code: string;
|
||||
planning_status: string;
|
||||
topic_code?: string | null | undefined;
|
||||
topic_name?: string | null | undefined;
|
||||
lesson_code?: string | null | undefined;
|
||||
lesson_name?: string | null | undefined;
|
||||
learning_statement_codes?: string | null | undefined;
|
||||
learning_statements?: string | null | undefined;
|
||||
learning_resource_codes?: string | null | undefined;
|
||||
learning_resources?: string | null | undefined;
|
||||
}
|
||||
12
src/types/graph_relationship_types.ts
Normal file
12
src/types/graph_relationship_types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export interface BaseRelationshipInterface {
|
||||
__relationshiptype__: string;
|
||||
source: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
// General
|
||||
export interface GeneralRelationshipInterface extends BaseRelationshipInterface {
|
||||
__relationshiptype__: string;
|
||||
source: string;
|
||||
target: string;
|
||||
}
|
||||
32
src/utils/tldraw/cc-base/cc-graph-migrations.ts
Normal file
32
src/utils/tldraw/cc-base/cc-graph-migrations.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { ccGraphShapeProps } from './cc-graph-props'
|
||||
import { createShapePropsMigrationIds, createShapePropsMigrationSequence } from 'tldraw'
|
||||
import { CCGraphShape, GraphShapeType } from './cc-graph-types'
|
||||
|
||||
// Helper function to create version IDs for a shape type
|
||||
const createVersions = (shapeType: GraphShapeType) => {
|
||||
return createShapePropsMigrationIds(shapeType, {
|
||||
Initial: 1 // All shapes start at version 1 as required by TLDraw
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function to create a migration sequence for a shape
|
||||
const createMigrationSequence = (shapeType: GraphShapeType) => {
|
||||
const versions = createVersions(shapeType)
|
||||
return createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: versions.Initial,
|
||||
up: (props: CCGraphShape['props']) => {
|
||||
// Initial version - no changes needed
|
||||
return props
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
// Create migrations for all graph shapes
|
||||
export const ccGraphMigrations = Object.keys(ccGraphShapeProps).reduce((acc, shapeType) => ({
|
||||
...acc,
|
||||
[shapeType]: createMigrationSequence(shapeType as GraphShapeType)
|
||||
}), {} as Record<GraphShapeType, ReturnType<typeof createShapePropsMigrationSequence>>)
|
||||
656
src/utils/tldraw/cc-base/cc-graph-props.ts
Normal file
656
src/utils/tldraw/cc-base/cc-graph-props.ts
Normal file
@ -0,0 +1,656 @@
|
||||
import { T, TLBinding, TLShapeId } from 'tldraw'
|
||||
import { baseShapeProps } from './cc-props'
|
||||
import { ShapeState } from './cc-graph-types'
|
||||
|
||||
// State props validation
|
||||
const stateProps = T.object({
|
||||
parentId: T.optional(T.string.nullable()),
|
||||
isPageChild: T.optional(T.boolean.nullable()),
|
||||
hasChildren: T.optional(T.boolean.nullable()),
|
||||
bindings: T.optional(T.arrayOf(T.object({})).nullable())
|
||||
})
|
||||
|
||||
// Base props for all nodes
|
||||
const graphBaseProps = {
|
||||
...baseShapeProps,
|
||||
__primarylabel__: T.string,
|
||||
unique_id: T.string,
|
||||
path: T.string,
|
||||
created: T.string,
|
||||
merged: T.string,
|
||||
state: T.optional(stateProps.nullable()),
|
||||
defaultComponent: T.optional(T.boolean.nullable())
|
||||
}
|
||||
|
||||
// Props for specific node types
|
||||
export const ccGraphShapeProps = {
|
||||
'cc-user-node': {
|
||||
...graphBaseProps,
|
||||
user_name: T.string,
|
||||
user_email: T.string,
|
||||
user_type: T.string,
|
||||
user_id: T.string,
|
||||
worker_node_data: T.string,
|
||||
},
|
||||
'cc-teacher-node': {
|
||||
...graphBaseProps,
|
||||
teacher_code: T.string,
|
||||
teacher_name_formal: T.string,
|
||||
teacher_email: T.string,
|
||||
user_db_name: T.string,
|
||||
worker_db_name: T.string,
|
||||
},
|
||||
'cc-student-node': {
|
||||
...graphBaseProps,
|
||||
student_code: T.string,
|
||||
student_name_formal: T.string,
|
||||
student_email: T.string,
|
||||
worker_db_name: T.string,
|
||||
},
|
||||
'cc-calendar-node': {
|
||||
...graphBaseProps,
|
||||
name: T.string,
|
||||
calendar_type: T.string,
|
||||
calendar_name: T.string,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
},
|
||||
'cc-calendar-year-node': {
|
||||
...graphBaseProps,
|
||||
year: T.string,
|
||||
},
|
||||
'cc-calendar-month-node': {
|
||||
...graphBaseProps,
|
||||
year: T.string,
|
||||
month: T.string,
|
||||
month_name: T.string,
|
||||
},
|
||||
'cc-calendar-week-node': {
|
||||
...graphBaseProps,
|
||||
start_date: T.string,
|
||||
week_number: T.string,
|
||||
iso_week: T.string,
|
||||
},
|
||||
'cc-calendar-day-node': {
|
||||
...graphBaseProps,
|
||||
date: T.string,
|
||||
day_of_week: T.string,
|
||||
iso_day: T.string,
|
||||
},
|
||||
'cc-calendar-time-chunk-node': {
|
||||
...graphBaseProps,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
},
|
||||
'cc-school-node': {
|
||||
...graphBaseProps,
|
||||
school_uuid: T.string,
|
||||
school_name: T.string,
|
||||
school_website: T.string,
|
||||
},
|
||||
'cc-department-node': {
|
||||
...graphBaseProps,
|
||||
department_name: T.string,
|
||||
},
|
||||
'cc-room-node': {
|
||||
...graphBaseProps,
|
||||
room_code: T.string,
|
||||
room_name: T.string,
|
||||
},
|
||||
'cc-subject-class-node': {
|
||||
...graphBaseProps,
|
||||
subject_class_code: T.string,
|
||||
year_group: T.string,
|
||||
subject: T.string,
|
||||
subject_code: T.string,
|
||||
},
|
||||
'cc-pastoral-structure-node': {
|
||||
...graphBaseProps,
|
||||
},
|
||||
'cc-year-group-node': {
|
||||
...graphBaseProps,
|
||||
year_group: T.string,
|
||||
year_group_name: T.string,
|
||||
},
|
||||
'cc-curriculum-structure-node': {
|
||||
...graphBaseProps,
|
||||
},
|
||||
'cc-key-stage-node': {
|
||||
...graphBaseProps,
|
||||
key_stage_name: T.string,
|
||||
key_stage: T.string,
|
||||
},
|
||||
'cc-key-stage-syllabus-node': {
|
||||
...graphBaseProps,
|
||||
ks_syllabus_id: T.string,
|
||||
ks_syllabus_name: T.string,
|
||||
ks_syllabus_key_stage: T.string,
|
||||
ks_syllabus_subject: T.string,
|
||||
ks_syllabus_subject_code: T.string,
|
||||
},
|
||||
'cc-year-group-syllabus-node': {
|
||||
...graphBaseProps,
|
||||
yr_syllabus_id: T.string,
|
||||
yr_syllabus_name: T.string,
|
||||
yr_syllabus_year_group: T.string,
|
||||
yr_syllabus_subject: T.string,
|
||||
yr_syllabus_subject_code: T.string,
|
||||
},
|
||||
'cc-subject-node': {
|
||||
...graphBaseProps,
|
||||
subject_code: T.string,
|
||||
subject_name: T.string,
|
||||
},
|
||||
'cc-topic-node': {
|
||||
...graphBaseProps,
|
||||
topic_id: T.string,
|
||||
topic_title: T.string,
|
||||
total_number_of_lessons_for_topic: T.string,
|
||||
topic_type: T.string,
|
||||
topic_assessment_type: T.string,
|
||||
},
|
||||
'cc-topic-lesson-node': {
|
||||
...graphBaseProps,
|
||||
topic_lesson_id: T.string,
|
||||
topic_lesson_title: T.string,
|
||||
topic_lesson_type: T.string,
|
||||
topic_lesson_length: T.string,
|
||||
topic_lesson_skills_learned: T.string,
|
||||
topic_lesson_suggested_activities: T.string,
|
||||
topic_lesson_weblinks: T.string,
|
||||
},
|
||||
'cc-learning-statement-node': {
|
||||
...graphBaseProps,
|
||||
lesson_learning_statement_id: T.string,
|
||||
lesson_learning_statement: T.string,
|
||||
lesson_learning_statement_type: T.string,
|
||||
},
|
||||
'cc-science-lab-node': {
|
||||
...graphBaseProps,
|
||||
science_lab_id: T.string,
|
||||
science_lab_title: T.string,
|
||||
science_lab_summary: T.string,
|
||||
science_lab_requirements: T.string,
|
||||
science_lab_procedure: T.string,
|
||||
science_lab_safety: T.string,
|
||||
science_lab_weblinks: T.string,
|
||||
},
|
||||
'cc-teacher-timetable-node': {
|
||||
...graphBaseProps,
|
||||
teacher_id: T.string,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
},
|
||||
'cc-timetable-lesson-node': {
|
||||
...graphBaseProps,
|
||||
subject_class: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
},
|
||||
'cc-planned-lesson-node': {
|
||||
...graphBaseProps,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
subject_class: T.string,
|
||||
year_group: T.string,
|
||||
subject: T.string,
|
||||
teacher_code: T.string,
|
||||
planning_status: T.string,
|
||||
topic_code: T.string,
|
||||
topic_name: T.string,
|
||||
lesson_code: T.string,
|
||||
lesson_name: T.string,
|
||||
learning_statement_codes: T.string,
|
||||
learning_statements: T.string,
|
||||
learning_resource_codes: T.string,
|
||||
learning_resources: T.string,
|
||||
},
|
||||
'cc-school-timetable-node': {
|
||||
...graphBaseProps,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
},
|
||||
'cc-academic-year-node': {
|
||||
...graphBaseProps,
|
||||
year: T.string,
|
||||
},
|
||||
'cc-academic-term-node': {
|
||||
...graphBaseProps,
|
||||
term_name: T.string,
|
||||
term_number: T.string,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
},
|
||||
'cc-academic-week-node': {
|
||||
...graphBaseProps,
|
||||
academic_week_number: T.string,
|
||||
start_date: T.string,
|
||||
week_type: T.string,
|
||||
},
|
||||
'cc-academic-day-node': {
|
||||
...graphBaseProps,
|
||||
academic_day: T.string,
|
||||
date: T.string,
|
||||
day_of_week: T.string,
|
||||
day_type: T.string,
|
||||
},
|
||||
'cc-academic-period-node': {
|
||||
...graphBaseProps,
|
||||
name: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
},
|
||||
'cc-registration-period-node': {
|
||||
...graphBaseProps,
|
||||
name: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
},
|
||||
'cc-department-structure-node': {
|
||||
...graphBaseProps,
|
||||
department_structure_type: T.string,
|
||||
},
|
||||
'cc-user-teacher-timetable-node': {
|
||||
...graphBaseProps,
|
||||
school_db_name: T.string,
|
||||
school_timetable_id: T.string,
|
||||
},
|
||||
'cc-user-timetable-lesson-node': {
|
||||
...graphBaseProps,
|
||||
subject_class: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
school_db_name: T.string,
|
||||
school_period_id: T.string,
|
||||
},
|
||||
} as const
|
||||
|
||||
// Default props getters
|
||||
export const getDefaultBaseProps = () => ({
|
||||
w: 200 as number,
|
||||
h: 200 as number,
|
||||
headerColor: '#3e6589' as string,
|
||||
backgroundColor: '#f0f0f0' as string,
|
||||
title: 'Untitled' as string,
|
||||
isLocked: false as boolean,
|
||||
unique_id: '' as string,
|
||||
path: '' as string,
|
||||
created: '' as string,
|
||||
merged: '' as string,
|
||||
state: {
|
||||
parentId: null as TLShapeId | null,
|
||||
isPageChild: true as boolean | null,
|
||||
hasChildren: null as boolean | null,
|
||||
bindings: null as TLBinding[] | null
|
||||
} as ShapeState | null,
|
||||
defaultComponent: true as boolean | null
|
||||
})
|
||||
|
||||
export const getDefaultCCUserNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
__primarylabel__: 'User',
|
||||
user_name: '',
|
||||
user_email: '',
|
||||
user_type: '',
|
||||
user_id: '',
|
||||
worker_node_data: ''
|
||||
})
|
||||
|
||||
export const getDefaultCCTeacherNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
__primarylabel__: 'Teacher',
|
||||
teacher_code: '',
|
||||
teacher_name_formal: '',
|
||||
teacher_email: '',
|
||||
user_db_name: '',
|
||||
worker_db_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCStudentNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
__primarylabel__: 'Student',
|
||||
student_code: '',
|
||||
student_name_formal: '',
|
||||
student_email: '',
|
||||
worker_db_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Calendar',
|
||||
__primarylabel__: 'Calendar',
|
||||
name: '',
|
||||
calendar_type: '',
|
||||
calendar_name: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarYearNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Calendar Year',
|
||||
__primarylabel__: 'Calendar Year',
|
||||
year: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarMonthNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Calendar Month',
|
||||
__primarylabel__: 'Calendar Month',
|
||||
year: '',
|
||||
month: '',
|
||||
month_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarWeekNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Calendar Week',
|
||||
__primarylabel__: 'Calendar Week',
|
||||
start_date: '',
|
||||
week_number: '',
|
||||
iso_week: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarDayNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Calendar Day',
|
||||
__primarylabel__: 'Calendar Day',
|
||||
date: '',
|
||||
day_of_week: '',
|
||||
iso_day: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarTimeChunkNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Calendar Time Chunk',
|
||||
__primarylabel__: 'Calendar Time Chunk',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCSchoolNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'School',
|
||||
__primarylabel__: 'School',
|
||||
school_uuid: '',
|
||||
school_name: '',
|
||||
school_website: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCDepartmentNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Department',
|
||||
__primarylabel__: 'Department',
|
||||
department_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCRoomNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Room',
|
||||
__primarylabel__: 'Room',
|
||||
room_code: '',
|
||||
room_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCSubjectClassNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Subject Class',
|
||||
__primarylabel__: 'Subject Class',
|
||||
subject_class_code: '',
|
||||
year_group: '',
|
||||
subject: '',
|
||||
subject_code: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCPastoralStructureNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Pastoral Structure',
|
||||
__primarylabel__: 'Pastoral Structure',
|
||||
pastoral_structure_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCYearGroupNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Year Group',
|
||||
__primarylabel__: 'Year Group',
|
||||
year_group: '',
|
||||
year_group_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCCurriculumStructureNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Curriculum Structure',
|
||||
__primarylabel__: 'Curriculum Structure',
|
||||
curriculum_structure_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCKeyStageNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Key Stage',
|
||||
__primarylabel__: 'Key Stage',
|
||||
key_stage_name: '',
|
||||
key_stage: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCKeyStageSyllabusNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Key Stage Syllabus',
|
||||
__primarylabel__: 'Key Stage Syllabus',
|
||||
ks_syllabus_id: '',
|
||||
ks_syllabus_name: '',
|
||||
ks_syllabus_key_stage: '',
|
||||
ks_syllabus_subject: '',
|
||||
ks_syllabus_subject_code: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCYearGroupSyllabusNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Year Group Syllabus',
|
||||
__primarylabel__: 'Year Group Syllabus',
|
||||
yr_syllabus_id: '',
|
||||
yr_syllabus_name: '',
|
||||
yr_syllabus_year_group: '',
|
||||
yr_syllabus_subject: '',
|
||||
yr_syllabus_subject_code: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCSubjectNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Subject',
|
||||
__primarylabel__: 'Subject',
|
||||
subject_code: '',
|
||||
subject_name: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCTopicNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Topic',
|
||||
__primarylabel__: 'Topic',
|
||||
topic_id: '',
|
||||
topic_title: '',
|
||||
total_number_of_lessons_for_topic: '',
|
||||
topic_type: '',
|
||||
topic_assessment_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCTopicLessonNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Topic Lesson',
|
||||
__primarylabel__: 'Topic Lesson',
|
||||
topic_lesson_id: '',
|
||||
topic_lesson_title: '',
|
||||
topic_lesson_type: '',
|
||||
topic_lesson_length: '',
|
||||
topic_lesson_skills_learned: '',
|
||||
topic_lesson_suggested_activities: '',
|
||||
topic_lesson_weblinks: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCLearningStatementNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Learning Statement',
|
||||
__primarylabel__: 'Learning Statement',
|
||||
lesson_learning_statement_id: '',
|
||||
lesson_learning_statement: '',
|
||||
lesson_learning_statement_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCScienceLabNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Science Lab',
|
||||
__primarylabel__: 'Science Lab',
|
||||
science_lab_id: '',
|
||||
science_lab_title: '',
|
||||
science_lab_summary: '',
|
||||
science_lab_requirements: '',
|
||||
science_lab_procedure: '',
|
||||
science_lab_safety: '',
|
||||
science_lab_weblinks: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCTeacherTimetableNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Teacher Timetable',
|
||||
__primarylabel__: 'Teacher Timetable',
|
||||
teacher_id: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCTimetableLessonNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Timetable Lesson',
|
||||
__primarylabel__: 'Timetable Lesson',
|
||||
subject_class: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCPlannedLessonNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Planned Lesson',
|
||||
__primarylabel__: 'Planned Lesson',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
subject_class: '',
|
||||
year_group: '',
|
||||
subject: '',
|
||||
teacher_code: '',
|
||||
planning_status: '',
|
||||
topic_code: '',
|
||||
topic_name: '',
|
||||
lesson_code: '',
|
||||
lesson_name: '',
|
||||
learning_statement_codes: '',
|
||||
learning_statements: '',
|
||||
learning_resource_codes: '',
|
||||
learning_resources: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCSchoolTimetableNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'School Timetable',
|
||||
__primarylabel__: 'School Timetable',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCAcademicYearNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Academic Year',
|
||||
__primarylabel__: 'Academic Year',
|
||||
year: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCAcademicTermNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Academic Term',
|
||||
__primarylabel__: 'Academic Term',
|
||||
term_name: '',
|
||||
term_number: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCAcademicWeekNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Academic Week',
|
||||
__primarylabel__: 'Academic Week',
|
||||
academic_week_number: '',
|
||||
start_date: '',
|
||||
week_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCAcademicDayNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Academic Day',
|
||||
__primarylabel__: 'Academic Day',
|
||||
academic_day: '',
|
||||
date: '',
|
||||
day_of_week: '',
|
||||
day_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCAcademicPeriodNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Academic Period',
|
||||
__primarylabel__: 'Academic Period',
|
||||
name: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCRegistrationPeriodNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Registration Period',
|
||||
__primarylabel__: 'Registration Period',
|
||||
name: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCDepartmentStructureNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'Department Structure',
|
||||
__primarylabel__: 'DepartmentStructure',
|
||||
department_structure_type: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCUserTeacherTimetableNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'User Teacher Timetable',
|
||||
__primarylabel__: 'UserTeacherTimetable',
|
||||
school_db_name: '',
|
||||
school_timetable_id: '',
|
||||
})
|
||||
|
||||
export const getDefaultCCUserTimetableLessonNodeProps = () => ({
|
||||
...getDefaultBaseProps(),
|
||||
title: 'User Timetable Lesson',
|
||||
__primarylabel__: 'UserTimetableLesson',
|
||||
subject_class: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
school_db_name: '',
|
||||
school_period_id: '',
|
||||
})
|
||||
169
src/utils/tldraw/cc-base/cc-graph-styles.ts
Normal file
169
src/utils/tldraw/cc-base/cc-graph-styles.ts
Normal file
@ -0,0 +1,169 @@
|
||||
// Shared styles for all nodes
|
||||
export const SHARED_NODE_STYLES = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column' as const,
|
||||
padding: '8px',
|
||||
gap: '4px',
|
||||
backgroundColor: 'var(--color-muted)',
|
||||
color: 'var(--color-text)',
|
||||
borderRadius: '4px',
|
||||
minWidth: '150px',
|
||||
},
|
||||
header: {
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold' as const,
|
||||
marginBottom: '4px',
|
||||
color: 'var(--color-text)',
|
||||
},
|
||||
property: {
|
||||
label: {
|
||||
fontSize: '12px',
|
||||
color: 'var(--color-text-2)',
|
||||
marginRight: '4px',
|
||||
fontWeight: '500' as const,
|
||||
},
|
||||
value: {
|
||||
fontSize: '12px',
|
||||
color: 'var(--color-text)',
|
||||
fontWeight: '200' as const,
|
||||
},
|
||||
wrapper: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
},
|
||||
},
|
||||
error: {
|
||||
container: {
|
||||
padding: '8px',
|
||||
backgroundColor: 'var(--color-error)',
|
||||
color: 'white',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px',
|
||||
},
|
||||
message: {
|
||||
fontWeight: 'bold' as const,
|
||||
},
|
||||
details: {
|
||||
marginTop: '4px',
|
||||
opacity: 0.8,
|
||||
}
|
||||
},
|
||||
defaultComponent: {
|
||||
container: {
|
||||
display: 'flex',
|
||||
gap: '8px',
|
||||
marginBottom: '8px',
|
||||
},
|
||||
button: {
|
||||
padding: '4px 8px',
|
||||
fontSize: '12px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: 'var(--color-muted-2)',
|
||||
color: 'var(--color-text)',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--color-muted-3)',
|
||||
}
|
||||
}
|
||||
}
|
||||
} as const
|
||||
|
||||
// Color themes for different node types
|
||||
export const NODE_THEMES = {
|
||||
calendar: {
|
||||
headerColor: '#0066cc',
|
||||
backgroundColor: '#e6f0ff',
|
||||
},
|
||||
academic: {
|
||||
headerColor: '#008000',
|
||||
backgroundColor: '#e6ffe6',
|
||||
},
|
||||
curriculum: {
|
||||
headerColor: '#ff8c00',
|
||||
backgroundColor: '#fff3e6',
|
||||
},
|
||||
pastoral: {
|
||||
headerColor: '#8a2be2',
|
||||
backgroundColor: '#f5e6ff',
|
||||
},
|
||||
people: {
|
||||
headerColor: '#cc0000',
|
||||
backgroundColor: '#ffe6e6',
|
||||
},
|
||||
resource: {
|
||||
headerColor: '#cccc00',
|
||||
backgroundColor: '#fffff0',
|
||||
},
|
||||
} as const
|
||||
|
||||
// Node type to theme mapping
|
||||
export const NODE_TYPE_THEMES: Record<string, keyof typeof NODE_THEMES> = {
|
||||
// Calendar nodes
|
||||
'cc-calendar-node': 'calendar',
|
||||
'cc-calendar-year-node': 'calendar',
|
||||
'cc-calendar-month-node': 'calendar',
|
||||
'cc-calendar-week-node': 'calendar',
|
||||
'cc-calendar-day-node': 'calendar',
|
||||
'cc-calendar-time-chunk-node': 'calendar',
|
||||
|
||||
// Academic nodes
|
||||
'cc-academic-year-node': 'academic',
|
||||
'cc-academic-term-node': 'academic',
|
||||
'cc-academic-week-node': 'academic',
|
||||
'cc-academic-day-node': 'academic',
|
||||
'cc-academic-period-node': 'academic',
|
||||
'cc-registration-period-node': 'academic',
|
||||
'cc-timetable-lesson-node': 'academic',
|
||||
'cc-planned-lesson-node': 'academic',
|
||||
'cc-school-timetable-node': 'academic',
|
||||
'cc-user-teacher-timetable-node': 'academic',
|
||||
'cc-user-timetable-lesson-node': 'academic',
|
||||
|
||||
// Curriculum nodes
|
||||
'cc-curriculum-structure-node': 'curriculum',
|
||||
'cc-key-stage-node': 'curriculum',
|
||||
'cc-key-stage-syllabus-node': 'curriculum',
|
||||
|
||||
'cc-year-group-syllabus-node': 'curriculum',
|
||||
'cc-subject-node': 'curriculum',
|
||||
'cc-topic-node': 'curriculum',
|
||||
'cc-topic-lesson-node': 'curriculum',
|
||||
'cc-learning-statement-node': 'curriculum',
|
||||
'cc-science-lab-node': 'curriculum',
|
||||
|
||||
// Pastoral nodes
|
||||
'cc-pastoral-structure-node': 'pastoral',
|
||||
'cc-year-group-node': 'pastoral',
|
||||
|
||||
// People nodes
|
||||
'cc-user-node': 'people',
|
||||
'cc-teacher-node': 'people',
|
||||
'cc-student-node': 'people',
|
||||
|
||||
// Resource nodes
|
||||
'cc-school-node': 'resource',
|
||||
'cc-department-node': 'resource',
|
||||
'cc-room-node': 'resource',
|
||||
'cc-subject-class-node': 'resource',
|
||||
} as const
|
||||
|
||||
// Helper function to get theme for a node type
|
||||
export const getNodeTheme = (nodeType: string) => {
|
||||
const themeKey = NODE_TYPE_THEMES[nodeType]
|
||||
return themeKey ? NODE_THEMES[themeKey] : NODE_THEMES.resource // Default to resource theme
|
||||
}
|
||||
|
||||
// Helper function to get styles for a specific node type
|
||||
export const getNodeStyles = (nodeType: string) => {
|
||||
const theme = getNodeTheme(nodeType)
|
||||
return {
|
||||
...SHARED_NODE_STYLES,
|
||||
container: {
|
||||
...SHARED_NODE_STYLES.container,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
},
|
||||
}
|
||||
}
|
||||
387
src/utils/tldraw/cc-base/cc-graph-types.ts
Normal file
387
src/utils/tldraw/cc-base/cc-graph-types.ts
Normal file
@ -0,0 +1,387 @@
|
||||
import { TLBinding, TLBaseShape, TLShapeId } from 'tldraw'
|
||||
import { CCBaseShape } from './cc-types'
|
||||
import { CCBaseProps } from './cc-props'
|
||||
import { ccGraphShapeProps } from './cc-graph-props'
|
||||
|
||||
// Export type for graph shape types
|
||||
export type GraphShapeType = keyof typeof ccGraphShapeProps
|
||||
|
||||
export interface ShapeState {
|
||||
parentId: TLShapeId | null
|
||||
isPageChild: boolean | null
|
||||
hasChildren: boolean | null
|
||||
bindings: TLBinding[] | null
|
||||
}
|
||||
|
||||
export type CCGraphShapeProps = CCBaseProps & {
|
||||
__primarylabel__: string
|
||||
unique_id: string
|
||||
path: string
|
||||
created: string
|
||||
merged: string
|
||||
state: ShapeState | null | undefined
|
||||
defaultComponent: boolean | null
|
||||
}
|
||||
|
||||
// Define the base shape type for graph shapes
|
||||
export type CCGraphShape = CCBaseShape & TLBaseShape<GraphShapeType, {
|
||||
__primarylabel__: CCGraphShapeProps['__primarylabel__']
|
||||
unique_id: CCGraphShapeProps['unique_id']
|
||||
path: CCGraphShapeProps['path']
|
||||
created: CCGraphShapeProps['created']
|
||||
merged: CCGraphShapeProps['merged']
|
||||
state: CCGraphShapeProps['state']
|
||||
defaultComponent: CCGraphShapeProps['defaultComponent']
|
||||
}>
|
||||
|
||||
export type CCUserNodeProps = CCGraphShapeProps & {
|
||||
user_name: string
|
||||
user_email: string
|
||||
user_type: string
|
||||
user_id: string
|
||||
worker_node_data: string
|
||||
}
|
||||
|
||||
export type CCTeacherNodeProps = CCGraphShapeProps & {
|
||||
teacher_code: string
|
||||
teacher_name_formal: string
|
||||
teacher_email: string
|
||||
user_db_name: string
|
||||
worker_db_name: string
|
||||
}
|
||||
|
||||
export type CCStudentNodeProps = CCGraphShapeProps & {
|
||||
student_name_formal: string
|
||||
student_code: string
|
||||
student_email: string
|
||||
}
|
||||
|
||||
export type CCCalendarNodeProps = CCGraphShapeProps & {
|
||||
title: string
|
||||
name: string
|
||||
calendar_type: string
|
||||
calendar_name: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
|
||||
export type CCCalendarYearNodeProps = CCGraphShapeProps & {
|
||||
year: string
|
||||
}
|
||||
|
||||
export type CCCalendarMonthNodeProps = CCGraphShapeProps & {
|
||||
year: string
|
||||
month: string
|
||||
month_name: string
|
||||
}
|
||||
|
||||
export type CCCalendarWeekNodeProps = CCGraphShapeProps & {
|
||||
start_date: string
|
||||
week_number: string
|
||||
iso_week: string
|
||||
}
|
||||
|
||||
export type CCCalendarDayNodeProps = CCGraphShapeProps & {
|
||||
date: string
|
||||
day_of_week: string
|
||||
iso_day: string
|
||||
}
|
||||
|
||||
export type CCCalendarTimeChunkNodeProps = CCGraphShapeProps & {
|
||||
start_time: string
|
||||
end_time: string
|
||||
}
|
||||
|
||||
export type CCSchoolNodeProps = CCGraphShapeProps & {
|
||||
school_uuid: string
|
||||
school_name: string
|
||||
school_website: string
|
||||
}
|
||||
|
||||
export type CCDepartmentNodeProps = CCGraphShapeProps & {
|
||||
department_name: string
|
||||
}
|
||||
|
||||
export type CCRoomNodeProps = CCGraphShapeProps & {
|
||||
room_code: string
|
||||
room_name: string
|
||||
}
|
||||
|
||||
export type CCSubjectClassNodeProps = CCGraphShapeProps & {
|
||||
subject_class_code: string
|
||||
year_group: string
|
||||
subject: string
|
||||
subject_code: string
|
||||
}
|
||||
|
||||
export type CCPastoralStructureNodeProps = CCGraphShapeProps & {
|
||||
pastoral_structure_type: string
|
||||
}
|
||||
|
||||
export type CCYearGroupNodeProps = CCGraphShapeProps & {
|
||||
year_group: string
|
||||
year_group_name: string
|
||||
}
|
||||
|
||||
export type CCCurriculumStructureNodeProps = CCGraphShapeProps & {
|
||||
curriculum_structure_type: string
|
||||
}
|
||||
|
||||
export type CCKeyStageNodeProps = CCGraphShapeProps & {
|
||||
key_stage_name: string
|
||||
key_stage: string
|
||||
}
|
||||
|
||||
export type CCKeyStageSyllabusNodeProps = CCGraphShapeProps & {
|
||||
ks_syllabus_id: string
|
||||
ks_syllabus_name: string
|
||||
ks_syllabus_key_stage: string
|
||||
ks_syllabus_subject: string
|
||||
ks_syllabus_subject_code: string
|
||||
}
|
||||
|
||||
export type CCYearGroupSyllabusNodeProps = CCGraphShapeProps & {
|
||||
yr_syllabus_id: string
|
||||
yr_syllabus_name: string
|
||||
yr_syllabus_year_group: string
|
||||
yr_syllabus_subject: string
|
||||
yr_syllabus_subject_code: string
|
||||
}
|
||||
|
||||
export type CCSubjectNodeProps = CCGraphShapeProps & {
|
||||
subject_code: string
|
||||
subject_name: string
|
||||
}
|
||||
|
||||
export type CCTopicNodeProps = CCGraphShapeProps & {
|
||||
topic_id: string
|
||||
topic_title: string
|
||||
total_number_of_lessons_for_topic: string
|
||||
topic_type: string
|
||||
topic_assessment_type: string
|
||||
}
|
||||
|
||||
export type CCTopicLessonNodeProps = CCGraphShapeProps & {
|
||||
topic_lesson_id: string
|
||||
topic_lesson_title: string
|
||||
topic_lesson_type: string
|
||||
topic_lesson_length: string
|
||||
topic_lesson_skills_learned: string
|
||||
topic_lesson_suggested_activities: string
|
||||
topic_lesson_weblinks: string
|
||||
}
|
||||
|
||||
export type CCLearningStatementNodeProps = CCGraphShapeProps & {
|
||||
lesson_learning_statement_id: string
|
||||
lesson_learning_statement: string
|
||||
lesson_learning_statement_type: string
|
||||
}
|
||||
|
||||
export type CCScienceLabNodeProps = CCGraphShapeProps & {
|
||||
science_lab_id: string
|
||||
science_lab_title: string
|
||||
science_lab_summary: string
|
||||
science_lab_requirements: string
|
||||
science_lab_procedure: string
|
||||
science_lab_safety: string
|
||||
science_lab_weblinks: string
|
||||
}
|
||||
|
||||
export type CCTeacherTimetableNodeProps = CCGraphShapeProps & {
|
||||
teacher_id: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
|
||||
export type CCTimetableLessonNodeProps = CCGraphShapeProps & {
|
||||
subject_class: string
|
||||
date: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
period_code: string
|
||||
}
|
||||
|
||||
export type CCPlannedLessonNodeProps = CCGraphShapeProps & {
|
||||
date: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
period_code: string
|
||||
subject_class: string
|
||||
year_group: string
|
||||
subject: string
|
||||
teacher_code: string
|
||||
planning_status: string
|
||||
topic_code: string
|
||||
topic_name: string
|
||||
lesson_code: string
|
||||
lesson_name: string
|
||||
learning_statement_codes: string
|
||||
learning_statements: string
|
||||
learning_resource_codes: string
|
||||
learning_resources: string
|
||||
}
|
||||
|
||||
export type CCSchoolTimetableNodeProps = CCGraphShapeProps & {
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
|
||||
export type CCAcademicYearNodeProps = CCGraphShapeProps & {
|
||||
year: string
|
||||
}
|
||||
|
||||
export type CCAcademicTermNodeProps = CCGraphShapeProps & {
|
||||
term_name: string
|
||||
term_number: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
|
||||
export type CCAcademicWeekNodeProps = CCGraphShapeProps & {
|
||||
academic_week_number: string
|
||||
start_date: string
|
||||
week_type: string
|
||||
}
|
||||
|
||||
export type CCAcademicDayNodeProps = CCGraphShapeProps & {
|
||||
academic_day: string
|
||||
date: string
|
||||
day_of_week: string
|
||||
day_type: string
|
||||
}
|
||||
|
||||
export type CCAcademicPeriodNodeProps = CCGraphShapeProps & {
|
||||
name: string
|
||||
date: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
period_code: string
|
||||
}
|
||||
|
||||
export type CCRegistrationPeriodNodeProps = CCGraphShapeProps & {
|
||||
name: string
|
||||
date: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
period_code: string
|
||||
}
|
||||
|
||||
export type CCDepartmentStructureNodeProps = CCGraphShapeProps & {
|
||||
department_structure_type: string
|
||||
}
|
||||
|
||||
export type CCUserTeacherTimetableNodeProps = CCGraphShapeProps & {
|
||||
school_db_name: string
|
||||
school_timetable_id: string
|
||||
}
|
||||
|
||||
export type CCUserTimetableLessonNodeProps = CCGraphShapeProps & {
|
||||
subject_class: string
|
||||
date: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
period_code: string
|
||||
school_db_name: string
|
||||
school_period_id: string
|
||||
}
|
||||
|
||||
// Define a type-safe mapping of node types to their configurations
|
||||
export type CCNodeTypes = {
|
||||
User: { props: CCUserNodeProps }
|
||||
Developer: { props: CCUserNodeProps }
|
||||
Teacher: { props: CCTeacherNodeProps }
|
||||
Student: { props: CCStudentNodeProps }
|
||||
Calendar: { props: CCCalendarNodeProps }
|
||||
TeacherTimetable: { props: CCTeacherTimetableNodeProps }
|
||||
TimetableLesson: { props: CCTimetableLessonNodeProps }
|
||||
PlannedLesson: { props: CCPlannedLessonNodeProps }
|
||||
School: { props: CCSchoolNodeProps }
|
||||
CalendarYear: { props: CCCalendarYearNodeProps }
|
||||
CalendarMonth: { props: CCCalendarMonthNodeProps }
|
||||
CalendarWeek: { props: CCCalendarWeekNodeProps }
|
||||
CalendarDay: { props: CCCalendarDayNodeProps }
|
||||
CalendarTimeChunk: { props: CCCalendarTimeChunkNodeProps }
|
||||
ScienceLab: { props: CCScienceLabNodeProps }
|
||||
KeyStageSyllabus: { props: CCKeyStageSyllabusNodeProps }
|
||||
YearGroupSyllabus: { props: CCYearGroupSyllabusNodeProps }
|
||||
CurriculumStructure: { props: CCCurriculumStructureNodeProps }
|
||||
Topic: { props: CCTopicNodeProps }
|
||||
TopicLesson: { props: CCTopicLessonNodeProps }
|
||||
LearningStatement: { props: CCLearningStatementNodeProps }
|
||||
SchoolTimetable: { props: CCSchoolTimetableNodeProps }
|
||||
AcademicYear: { props: CCAcademicYearNodeProps }
|
||||
AcademicTerm: { props: CCAcademicTermNodeProps }
|
||||
AcademicWeek: { props: CCAcademicWeekNodeProps }
|
||||
AcademicDay: { props: CCAcademicDayNodeProps }
|
||||
AcademicPeriod: { props: CCAcademicPeriodNodeProps }
|
||||
RegistrationPeriod: { props: CCRegistrationPeriodNodeProps }
|
||||
PastoralStructure: { props: CCPastoralStructureNodeProps }
|
||||
KeyStage: { props: CCKeyStageNodeProps }
|
||||
Department: { props: CCDepartmentNodeProps }
|
||||
Room: { props: CCRoomNodeProps }
|
||||
SubjectClass: { props: CCSubjectClassNodeProps }
|
||||
DepartmentStructure: { props: CCDepartmentStructureNodeProps }
|
||||
UserTeacherTimetable: { props: CCUserTeacherTimetableNodeProps }
|
||||
UserTimetableLesson: { props: CCUserTimetableLessonNodeProps }
|
||||
}
|
||||
|
||||
// Helper function to get shape type from node type
|
||||
export const getShapeType = (nodeType: keyof CCNodeTypes): string => {
|
||||
return `cc-${nodeType.replace(/([A-Z])/g, '-$1').toLowerCase().substring(1)}-node`;
|
||||
}
|
||||
|
||||
// Helper function to get allowed props from node type
|
||||
export const getAllowedProps = (): string[] => {
|
||||
return ['__primarylabel__', 'unique_id'];
|
||||
}
|
||||
|
||||
// Helper function to get node configuration
|
||||
export const getNodeConfig = <T extends keyof CCNodeTypes>(nodeType: T) => {
|
||||
const shapeType = getShapeType(nodeType);
|
||||
return {
|
||||
shapeType,
|
||||
allowedProps: getAllowedProps()
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to check if a string is a valid node type
|
||||
export const isValidNodeType = (type: string): type is keyof CCNodeTypes => {
|
||||
return type in {
|
||||
User: true,
|
||||
Developer: true,
|
||||
Teacher: true,
|
||||
Student: true,
|
||||
Calendar: true,
|
||||
TeacherTimetable: true,
|
||||
TimetableLesson: true,
|
||||
PlannedLesson: true,
|
||||
School: true,
|
||||
CalendarYear: true,
|
||||
CalendarMonth: true,
|
||||
CalendarWeek: true,
|
||||
CalendarDay: true,
|
||||
CalendarTimeChunk: true,
|
||||
ScienceLab: true,
|
||||
KeyStageSyllabus: true,
|
||||
YearGroupSyllabus: true,
|
||||
CurriculumStructure: true,
|
||||
Topic: true,
|
||||
TopicLesson: true,
|
||||
LearningStatement: true,
|
||||
SchoolTimetable: true,
|
||||
AcademicYear: true,
|
||||
AcademicTerm: true,
|
||||
AcademicWeek: true,
|
||||
AcademicDay: true,
|
||||
AcademicPeriod: true,
|
||||
RegistrationPeriod: true,
|
||||
PastoralStructure: true,
|
||||
KeyStage: true,
|
||||
Department: true,
|
||||
Room: true,
|
||||
SubjectClass: true,
|
||||
DepartmentStructure: true,
|
||||
UserTeacherTimetable: true,
|
||||
UserTimetableLesson: true,
|
||||
};
|
||||
}
|
||||
246
src/utils/tldraw/cc-base/cc-migrations.ts
Normal file
246
src/utils/tldraw/cc-base/cc-migrations.ts
Normal file
@ -0,0 +1,246 @@
|
||||
import { TLRecord, TLShape } from 'tldraw'
|
||||
import { getDefaultCCBaseProps, getDefaultCCCalendarProps, getDefaultCCLiveTranscriptionProps, getDefaultCCSettingsProps, getDefaultCCSlideProps, getDefaultCCSlideShowProps, getDefaultCCSlideLayoutBindingProps, getDefaultCCYoutubeEmbedProps, getDefaultCCSearchProps, getDefaultCCWebBrowserProps } from './cc-props'
|
||||
|
||||
// Export both shape and binding migrations
|
||||
export const ccBindingMigrations = {
|
||||
'cc-slide-layout': {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'binding') return record
|
||||
if (record.type !== 'cc-slide-layout') return record
|
||||
return {
|
||||
...record,
|
||||
props: {
|
||||
...getDefaultCCSlideLayoutBindingProps(),
|
||||
...record.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const ccShapeMigrations = {
|
||||
base: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-base') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCBaseProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
calendar: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-calendar') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCCalendarProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
liveTranscription: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-live-transcription') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCLiveTranscriptionProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-settings') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCSettingsProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
slideshow: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-slideshow') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCSlideShowProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
slide: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-slide') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCSlideProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'cc-youtube-embed': {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-youtube-embed') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCYoutubeEmbedProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
search: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-search') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCSearchProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
webBrowser: {
|
||||
firstVersion: 1,
|
||||
currentVersion: 1,
|
||||
migrators: {
|
||||
1: {
|
||||
up: (record: TLRecord) => {
|
||||
if (record.typeName !== 'shape') return record
|
||||
const shape = record as TLShape
|
||||
if (shape.type !== 'cc-web-browser') return record
|
||||
return {
|
||||
...shape,
|
||||
props: {
|
||||
...getDefaultCCWebBrowserProps(),
|
||||
...shape.props,
|
||||
},
|
||||
}
|
||||
},
|
||||
down: (record: TLRecord) => {
|
||||
return record
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
262
src/utils/tldraw/cc-base/cc-props.ts
Normal file
262
src/utils/tldraw/cc-base/cc-props.ts
Normal file
@ -0,0 +1,262 @@
|
||||
import { T } from 'tldraw'
|
||||
import { CC_BASE_STYLE_CONSTANTS, CC_SLIDESHOW_STYLE_CONSTANTS } from './cc-styles'
|
||||
|
||||
export interface CCBaseProps {
|
||||
title: string
|
||||
w: number
|
||||
h: number
|
||||
headerColor: string
|
||||
backgroundColor: string
|
||||
isLocked: boolean
|
||||
}
|
||||
|
||||
// Create a constant for the base props validation
|
||||
export const baseShapeProps = {
|
||||
title: T.string,
|
||||
w: T.number,
|
||||
h: T.number,
|
||||
headerColor: T.string,
|
||||
backgroundColor: T.string,
|
||||
isLocked: T.boolean,
|
||||
}
|
||||
|
||||
export const ccShapeProps = {
|
||||
base: baseShapeProps,
|
||||
|
||||
calendar: {
|
||||
...baseShapeProps,
|
||||
date: T.string,
|
||||
selectedDate: T.string,
|
||||
view: T.string,
|
||||
events: T.arrayOf(T.object({
|
||||
id: T.string,
|
||||
title: T.string,
|
||||
start: T.string,
|
||||
end: T.string,
|
||||
groupId: T.string.optional(),
|
||||
extendedProps: T.object({
|
||||
subjectClass: T.string,
|
||||
color: T.string,
|
||||
periodCode: T.string,
|
||||
path: T.string.optional()
|
||||
})
|
||||
})),
|
||||
},
|
||||
|
||||
liveTranscription: {
|
||||
...baseShapeProps,
|
||||
isRecording: T.boolean,
|
||||
segments: T.arrayOf(T.object({
|
||||
id: T.string,
|
||||
text: T.string,
|
||||
completed: T.boolean,
|
||||
start: T.string,
|
||||
end: T.string,
|
||||
})),
|
||||
currentSegment: T.object({
|
||||
id: T.string,
|
||||
text: T.string,
|
||||
completed: T.boolean,
|
||||
start: T.string,
|
||||
end: T.string,
|
||||
}).optional(),
|
||||
lastProcessedSegment: T.string.optional(),
|
||||
},
|
||||
|
||||
settings: {
|
||||
...baseShapeProps,
|
||||
userEmail: T.string,
|
||||
userRole: T.string,
|
||||
isTeacher: T.boolean,
|
||||
},
|
||||
|
||||
slideshow: {
|
||||
...baseShapeProps,
|
||||
currentSlideIndex: T.number,
|
||||
slidePattern: T.string,
|
||||
numSlides: T.number,
|
||||
slides: T.arrayOf(T.object({
|
||||
imageData: T.string,
|
||||
meta: T.object({
|
||||
text: T.string,
|
||||
format: T.string,
|
||||
}),
|
||||
})).optional(),
|
||||
},
|
||||
|
||||
slide: {
|
||||
...baseShapeProps,
|
||||
imageData: T.string,
|
||||
meta: T.object({
|
||||
text: T.string,
|
||||
format: T.string,
|
||||
}),
|
||||
},
|
||||
|
||||
'cc-youtube-embed': {
|
||||
...baseShapeProps,
|
||||
video_url: T.string,
|
||||
transcript: T.arrayOf(T.object({
|
||||
start: T.number,
|
||||
duration: T.number,
|
||||
text: T.string,
|
||||
})),
|
||||
transcriptVisible: T.boolean,
|
||||
},
|
||||
|
||||
search: {
|
||||
...baseShapeProps,
|
||||
query: T.string,
|
||||
results: T.arrayOf(T.object({
|
||||
title: T.string,
|
||||
url: T.string,
|
||||
content: T.string,
|
||||
})),
|
||||
isSearching: T.boolean,
|
||||
},
|
||||
|
||||
webBrowser: {
|
||||
...baseShapeProps,
|
||||
url: T.string,
|
||||
history: T.arrayOf(T.string),
|
||||
currentHistoryIndex: T.number,
|
||||
isLoading: T.boolean,
|
||||
},
|
||||
}
|
||||
|
||||
export const ccBindingProps = {
|
||||
'cc-slide-layout': {
|
||||
isMovingWithParent: T.boolean.optional(),
|
||||
placeholder: T.boolean.optional(),
|
||||
index: T.string
|
||||
},
|
||||
}
|
||||
|
||||
export const getDefaultCCBaseProps = () => ({
|
||||
title: 'Base Shape',
|
||||
w: 100,
|
||||
h: 100,
|
||||
headerColor: '#3e6589',
|
||||
backgroundColor: '#ffffff',
|
||||
isLocked: false,
|
||||
})
|
||||
|
||||
export const getDefaultCCCalendarProps = () => ({
|
||||
...getDefaultCCBaseProps(),
|
||||
date: new Date().toISOString(),
|
||||
selectedDate: new Date().toISOString(),
|
||||
view: 'timeGridWeek',
|
||||
events: [],
|
||||
})
|
||||
|
||||
export const getDefaultCCLiveTranscriptionProps = () => ({
|
||||
...getDefaultCCBaseProps(),
|
||||
isRecording: false,
|
||||
segments: [],
|
||||
currentSegment: undefined,
|
||||
lastProcessedSegment: undefined,
|
||||
})
|
||||
|
||||
export const getDefaultCCSettingsProps = () => ({
|
||||
...getDefaultCCBaseProps(),
|
||||
userEmail: '',
|
||||
userRole: '',
|
||||
isTeacher: false,
|
||||
})
|
||||
|
||||
export function getDefaultCCSlideShowProps() {
|
||||
// Base 16:9 ratio dimensions
|
||||
const baseWidth = 1280
|
||||
const baseHeight = 720
|
||||
// Add header height and spacing
|
||||
const totalHeight = baseHeight +
|
||||
CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_HEADER_HEIGHT + // Slideshow's own header
|
||||
CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_SPACING * 2 + // Top and bottom spacing
|
||||
CC_SLIDESHOW_STYLE_CONSTANTS.SLIDE_CONTENT_PADDING // Extra padding for content
|
||||
|
||||
return {
|
||||
title: 'Slideshow',
|
||||
w: baseWidth,
|
||||
h: totalHeight,
|
||||
headerColor: '#3e6589',
|
||||
backgroundColor: '#0f0f0f',
|
||||
isLocked: false,
|
||||
currentSlideIndex: 0,
|
||||
slidePattern: 'horizontal',
|
||||
numSlides: 3,
|
||||
slides: [],
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultCCSlideProps() {
|
||||
// Base 16:9 ratio dimensions
|
||||
const baseWidth = 1280
|
||||
const baseHeight = 720
|
||||
// Add header height
|
||||
const totalHeight = baseHeight + CC_BASE_STYLE_CONSTANTS.HEADER.height
|
||||
|
||||
return {
|
||||
title: 'Slide',
|
||||
w: baseWidth,
|
||||
h: totalHeight,
|
||||
headerColor: '#3e6589',
|
||||
backgroundColor: '#0f0f0f',
|
||||
isLocked: false,
|
||||
imageData: '',
|
||||
meta: {
|
||||
text: '',
|
||||
format: 'markdown'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultCCSlideLayoutBindingProps() {
|
||||
return {
|
||||
isMovingWithParent: false,
|
||||
placeholder: false,
|
||||
index: '0',
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultCCYoutubeEmbedProps() {
|
||||
const videoHeight = 450
|
||||
const totalHeight = videoHeight + CC_BASE_STYLE_CONSTANTS.HEADER.height + (CC_BASE_STYLE_CONSTANTS.CONTENT.padding * 2)
|
||||
|
||||
return {
|
||||
...getDefaultCCBaseProps(),
|
||||
title: 'YouTube Video',
|
||||
w: 800,
|
||||
h: totalHeight,
|
||||
headerColor: '#ff0000',
|
||||
backgroundColor: '#0f0f0f',
|
||||
isLocked: false,
|
||||
video_url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||
transcript: [],
|
||||
transcriptVisible: false,
|
||||
}
|
||||
}
|
||||
|
||||
export const getDefaultCCSearchProps = () => ({
|
||||
...getDefaultCCBaseProps(),
|
||||
w: 400,
|
||||
h: 500,
|
||||
title: 'Search',
|
||||
headerColor: '#1a73e8',
|
||||
backgroundColor: '#ffffff',
|
||||
query: '',
|
||||
results: [],
|
||||
isSearching: false,
|
||||
})
|
||||
|
||||
export const getDefaultCCWebBrowserProps = () => ({
|
||||
...getDefaultCCBaseProps(),
|
||||
title: 'Web Browser',
|
||||
w: 800,
|
||||
h: 600,
|
||||
headerColor: '#1a73e8',
|
||||
backgroundColor: '#ffffff',
|
||||
url: '',
|
||||
history: [],
|
||||
currentHistoryIndex: -1,
|
||||
isLoading: false,
|
||||
})
|
||||
118
src/utils/tldraw/cc-base/cc-styles.ts
Normal file
118
src/utils/tldraw/cc-base/cc-styles.ts
Normal file
@ -0,0 +1,118 @@
|
||||
// Style constants used by all CC shapes
|
||||
export const CC_BASE_STYLE_CONSTANTS = {
|
||||
FONT_FAMILY: 'Inter, sans-serif',
|
||||
FONT_SIZES: {
|
||||
small: 12,
|
||||
medium: 14,
|
||||
large: 16,
|
||||
},
|
||||
// Container styles
|
||||
CONTAINER: {
|
||||
borderRadius: '4px',
|
||||
borderWidth: '2px',
|
||||
borderColor: '#e2e8f0',
|
||||
boxShadow: '0 2px 4px var(--color-muted-1)',
|
||||
},
|
||||
HEADER: {
|
||||
height: 32,
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
CONTENT: {
|
||||
padding: 16,
|
||||
borderRadius: 8,
|
||||
borderWidth: 2,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
HANDLE: {
|
||||
width: 8,
|
||||
},
|
||||
COLORS: {
|
||||
primary: '#3e6589',
|
||||
primary_dark: '#2e4a69',
|
||||
secondary: '#718096',
|
||||
secondary_dark: '#5a687a',
|
||||
background: '#ffffff',
|
||||
border: '#e2e8f0',
|
||||
text: '#1a202c',
|
||||
textLight: '#718096',
|
||||
},
|
||||
// Minimum dimensions
|
||||
MIN_DIMENSIONS: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
} as const
|
||||
|
||||
// Calendar specific styles
|
||||
export const CC_CALENDAR_STYLE_CONSTANTS = {
|
||||
// Common button styles
|
||||
COMMON_BUTTON: {
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
padding: '0.4em 1em',
|
||||
fontSize: '0.95em',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.05em',
|
||||
cursor: 'pointer',
|
||||
transition: 'background-color 0.3s ease',
|
||||
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
|
||||
// Application button styles
|
||||
APPLICATION_BUTTON: {
|
||||
backgroundColor: '#4f80ff',
|
||||
color: '#fff',
|
||||
},
|
||||
|
||||
// Option button styles
|
||||
OPTION_BUTTON: {
|
||||
backgroundColor: '#f0f4f9',
|
||||
color: '#2c3e50',
|
||||
border: '1px solid #ddd',
|
||||
},
|
||||
|
||||
// Calendar event styles
|
||||
EVENT: {
|
||||
mainFrame: {
|
||||
backgroundColor: 'transparent',
|
||||
padding: '0px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: '100%',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
title: {
|
||||
fontSize: '1.1em',
|
||||
fontWeight: 'normal',
|
||||
textAlign: 'center',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
opacity: 1,
|
||||
padding: '0px 0px',
|
||||
width: '100%',
|
||||
letterSpacing: '0.02em',
|
||||
margin: '0px 0px',
|
||||
}
|
||||
}
|
||||
} as const
|
||||
|
||||
// Slideshow specific styles
|
||||
export const CC_SLIDESHOW_STYLE_CONSTANTS = {
|
||||
DEFAULT_SLIDE_WIDTH: 800,
|
||||
DEFAULT_SLIDE_HEIGHT: 600,
|
||||
SLIDE_HEADER_HEIGHT: 40,
|
||||
SLIDE_HEADER_PADDING: 8,
|
||||
SLIDE_CONTENT_PADDING: 16,
|
||||
SLIDE_BORDER_RADIUS: 4,
|
||||
SLIDE_BORDER_WIDTH: 1,
|
||||
SLIDE_SPACING: 16,
|
||||
SLIDE_COLORS: {
|
||||
background: '#ffffff',
|
||||
border: '#e2e8f0',
|
||||
text: '#ffffff',
|
||||
secondary: '#718096',
|
||||
},
|
||||
} as const
|
||||
4
src/utils/tldraw/cc-base/cc-types.ts
Normal file
4
src/utils/tldraw/cc-base/cc-types.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { TLBaseShape } from 'tldraw'
|
||||
import { CCBaseProps } from './cc-props'
|
||||
|
||||
export interface CCBaseShape extends TLBaseShape<string, CCBaseProps> {}
|
||||
411
src/utils/tldraw/graph/baseNodeShapeUtil.tsx
Normal file
411
src/utils/tldraw/graph/baseNodeShapeUtil.tsx
Normal file
@ -0,0 +1,411 @@
|
||||
import {
|
||||
Editor,
|
||||
HTMLContainer,
|
||||
Rectangle2d,
|
||||
ShapeUtil,
|
||||
TLDefaultColorTheme,
|
||||
getDefaultColorTheme,
|
||||
createShapeId
|
||||
} from 'tldraw'
|
||||
import {
|
||||
AllNodeShapes
|
||||
} from './graph-shape-types'
|
||||
import {
|
||||
AllRelationshipShapes
|
||||
} from './graph-relationship-types'
|
||||
import { getNodeComponent } from './nodeComponents';
|
||||
import axios from '../../../axiosConfig';
|
||||
import graphState from './graphStateUtil';
|
||||
|
||||
export const nodeTypeConfig = {
|
||||
Developer: { shapeType: 'developer_node', color: 'light-blue' },
|
||||
Teacher: { shapeType: 'teacher_node', color: 'light-green' },
|
||||
|
||||
User: { shapeType: 'user_node', color: 'light-green' },
|
||||
TeacherTimetable: { shapeType: 'teacher_timetable_node', color: 'blue' },
|
||||
TimetableLesson: { shapeType: 'timetable_lesson_node', color: 'light-blue' },
|
||||
PlannedLesson: { shapeType: 'planned_lesson_node', color: 'light-green' },
|
||||
School: { shapeType: 'school_node', color: 'grey' },
|
||||
Calendar: { shapeType: 'calendar_node', color: 'violet' },
|
||||
CalendarYear: { shapeType: 'calendar_year_node', color: 'red' },
|
||||
CalendarMonth: { shapeType: 'calendar_month_node', color: 'light-violet' },
|
||||
CalendarWeek: { shapeType: 'calendar_week_node', color: 'light-red' },
|
||||
CalendarDay: { shapeType: 'calendar_day_node', color: 'light-blue' },
|
||||
CalendarTimeChunk: { shapeType: 'calendar_time_chunk_node', color: 'blue' },
|
||||
ScienceLab: { shapeType: 'science_lab_node', color: 'yellow' },
|
||||
KeyStageSyllabus: { shapeType: 'key_stage_syllabus_node', color: 'grey' },
|
||||
YearGroupSyllabus: { shapeType: 'year_group_syllabus_node', color: 'light-blue' },
|
||||
CurriculumStructure: { shapeType: 'curriculum_structure_node', color: 'grey' },
|
||||
Topic: { shapeType: 'topic_node', color: 'green' },
|
||||
TopicLesson: { shapeType: 'topic_lesson_node', color: 'light-green' },
|
||||
LearningStatement: { shapeType: 'learning_statement_node', color: 'light-blue' },
|
||||
SchoolTimetable: { shapeType: 'school_timetable_node', color: 'grey' },
|
||||
AcademicYear: { shapeType: 'academic_year_node', color: 'light-violet' },
|
||||
AcademicTerm: { shapeType: 'academic_term_node', color: 'yellow' },
|
||||
AcademicWeek: { shapeType: 'academic_week_node', color: 'orange' },
|
||||
AcademicDay: { shapeType: 'academic_day_node', color: 'light-red' },
|
||||
AcademicPeriod: { shapeType: 'academic_period_node', color: 'light-green' },
|
||||
RegistrationPeriod: { shapeType: 'registration_period_node', color: 'light-green' },
|
||||
PastoralStructure: { shapeType: 'pastoral_structure_node', color: 'grey' },
|
||||
KeyStage: { shapeType: 'key_stage_node', color: 'blue' },
|
||||
Department: { shapeType: 'department_node', color: 'light-blue' },
|
||||
Room: { shapeType: 'room_node', color: 'violet' },
|
||||
SubjectClass: { shapeType: 'subject_class_node', color: 'light-blue' },
|
||||
};
|
||||
|
||||
const createNodeComponent = (shape: AllNodeShapes, theme: TLDefaultColorTheme, editor: Editor) => {
|
||||
let isDragging = false;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
|
||||
const borderColor = theme.id === 'dark' ? 'white' : 'black'
|
||||
|
||||
const handlePointerDown = (e: React.PointerEvent) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
const rect = e.currentTarget.getBoundingClientRect()
|
||||
const x = e.clientX - rect.left
|
||||
const y = e.clientY - rect.top
|
||||
|
||||
// Define button areas
|
||||
const openFileButtonArea = { x: 10, y: shape.props.h - 60, width: shape.props.w - 20, height: 25 }
|
||||
const getConnectedNodesButtonArea = { x: 10, y: shape.props.h - 30, width: shape.props.w - 20, height: 25 }
|
||||
|
||||
if (isPointInRect(x, y, openFileButtonArea)) {
|
||||
console.log('Clicked on Open File button')
|
||||
loadTldrawFile(shape.props.path, editor)
|
||||
} else if (isPointInRect(x, y, getConnectedNodesButtonArea)) {
|
||||
console.log('Clicked on Get Connected Nodes button')
|
||||
handleGetConnectedNodes()
|
||||
} else if (isPointInShape(x, y, shape) && !isPointInRect(x, y, openFileButtonArea) && !isPointInRect(x, y, getConnectedNodesButtonArea)) {
|
||||
console.log('Clicked on shape')
|
||||
isDragging = true;
|
||||
startX = e.clientX - shape.x;
|
||||
startY = e.clientY - shape.y;
|
||||
}
|
||||
}
|
||||
|
||||
const handlePointerMove = (e: React.PointerEvent) => {
|
||||
if (isDragging) {
|
||||
const newX = e.clientX - startX;
|
||||
const newY = e.clientY - startY;
|
||||
editor.updateShape({
|
||||
id: shape.id,
|
||||
type: shape.type,
|
||||
x: newX,
|
||||
y: newY,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const handlePointerUp = (e: React.PointerEvent) => {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
const isPointInShape = (x: number, y: number, shape: AllNodeShapes) => {
|
||||
const bounds = editor.getShapeGeometry(shape).bounds
|
||||
return x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height
|
||||
}
|
||||
|
||||
const isPointInRect = (x: number, y: number, rect: { x: number, y: number, width: number, height: number }) => {
|
||||
return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height
|
||||
}
|
||||
|
||||
let isFetchingConnectedNodes = false;
|
||||
|
||||
const handleGetConnectedNodes = async () => {
|
||||
if (isFetchingConnectedNodes) {
|
||||
console.log("WARNING! Already fetching connected nodes. Skipping...");
|
||||
return;
|
||||
}
|
||||
isFetchingConnectedNodes = true;
|
||||
|
||||
console.log("Getting connected nodes for:", shape.props.unique_id);
|
||||
try {
|
||||
const response = await axios.get(`/api/database/tools/get-connected-nodes-and-edges?unique_id=${shape.props.unique_id}`);
|
||||
console.log("Connected nodes response:", response.data);
|
||||
if (response.data.status === "success") {
|
||||
const mainNode = response.data.main_node;
|
||||
const connectedNodes = response.data.connected_nodes;
|
||||
const relationships = response.data.relationships;
|
||||
|
||||
// Add nodes to the graph
|
||||
[mainNode, ...connectedNodes].forEach((node: any) => {
|
||||
console.log("Node:", node);
|
||||
const newShapeId = createShapeId(node.node_data.unique_id);
|
||||
const doesShapeExist = editor.getShape(newShapeId);
|
||||
if (!doesShapeExist) {
|
||||
console.log("Creating new shape with ID:", newShapeId);
|
||||
const nodeConfig = nodeTypeConfig[node.node_type as keyof typeof nodeTypeConfig];
|
||||
if (nodeConfig) {
|
||||
const newShape = {
|
||||
id: newShapeId,
|
||||
type: nodeConfig.shapeType,
|
||||
x: 0,
|
||||
y: 0,
|
||||
props: {
|
||||
color: nodeConfig.color,
|
||||
...node.node_data
|
||||
}
|
||||
};
|
||||
console.log("New shape:", newShape);
|
||||
console.log("Creating shape:", newShape);
|
||||
editor.createShape(newShape);
|
||||
console.log("New shape created:", newShape);
|
||||
const bounds = editor.getShapeGeometry(newShapeId).bounds;
|
||||
console.log("Shape bounds:", bounds);
|
||||
console.log("Updating shape with width:", bounds.w, "and height:", bounds.h);
|
||||
newShape.props.w = bounds.w;
|
||||
newShape.props.h = bounds.h;
|
||||
console.log("Adding node to graphState:", newShape);
|
||||
const shapeWithWidthAndHeight = {
|
||||
...newShape,
|
||||
w: bounds.w,
|
||||
h: bounds.h
|
||||
}
|
||||
graphState.addNode(shapeWithWidthAndHeight);
|
||||
console.log("Node added to graphState:", newShape);
|
||||
} else {
|
||||
console.log("WARNING! Node type not found:", node.node_type);
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log("Updating shapes with dagre...");
|
||||
graphState.setEditor(editor);
|
||||
graphState.updateShapesWithDagre();
|
||||
|
||||
// Add edges to the graph
|
||||
relationships.forEach((relationship: any) => {
|
||||
graphState.addEdge(relationship.start_node.unique_id, relationship.end_node.unique_id);
|
||||
});
|
||||
|
||||
// Create edge shapes
|
||||
graphState.getEdges().forEach((edge: any) => {
|
||||
console.log("WARNING! Cancelling createEdgeComponent()...");
|
||||
// console.log("handleGetConnectedNodes(): Creating edge component for:", edge.v, edge.w);
|
||||
// createEdgeComponent(edge.v, edge.w, editor);
|
||||
});
|
||||
|
||||
console.log("Done!");
|
||||
} else {
|
||||
console.error('Error in response:', response.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching connected nodes:', error);
|
||||
} finally {
|
||||
isFetchingConnectedNodes = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadTldrawFile = async (path: string, editor: any) => {
|
||||
console.log("Loading tldraw_file...")
|
||||
try {
|
||||
const response = await axios.get(`/api/database/tldraw_fs/get_tldraw_user_file${path}/tldraw_file.json`);
|
||||
const fileContent = response.data;
|
||||
|
||||
console.log("File content:", fileContent);
|
||||
|
||||
if (fileContent && fileContent.document && fileContent.document.store) {
|
||||
|
||||
// Ensure the schema version is set
|
||||
if (!fileContent.document.schema) {
|
||||
console.log("!fileContent.document.schema")
|
||||
fileContent.document.schema = { schemaVersion: 1 };
|
||||
} else if (!fileContent.document.schema.schemaVersion) {
|
||||
console.log("!fileContent.document.schema.schemaVersion")
|
||||
fileContent.document.schema.schemaVersion = 1;
|
||||
}
|
||||
|
||||
// Load the new content
|
||||
console.log("Loading snapshot: ", fileContent)
|
||||
editor.loadSnapshot(fileContent);
|
||||
} else {
|
||||
console.error('Invalid file content structure:', fileContent);
|
||||
throw new Error('Invalid file content structure');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading tldraw file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<HTMLContainer
|
||||
id={shape.id}
|
||||
style={{
|
||||
border: `1px solid ${borderColor}`,
|
||||
borderRadius: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
pointerEvents: 'all',
|
||||
backgroundColor: theme[shape.props.color].semi,
|
||||
color: theme[shape.props.color].solid,
|
||||
boxShadow: '1px 1px 2px rgba(0, 0, 0, 0.1)',
|
||||
transition: 'all 0.3s ease',
|
||||
overflow: 'hidden',
|
||||
padding: '10px',
|
||||
height: shape.props.h,
|
||||
}}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
>
|
||||
{getNodeComponent(shape, theme)}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', marginTop: '10px' }}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: theme[shape.props.color].solid,
|
||||
color: theme[shape.props.color].semi,
|
||||
padding: '5px',
|
||||
borderRadius: '3px',
|
||||
cursor: 'pointer',
|
||||
marginBottom: '5px',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
onClick={() => loadTldrawFile(shape.props.path, editor)}
|
||||
>
|
||||
Open File
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: theme[shape.props.color].solid,
|
||||
color: theme[shape.props.color].semi,
|
||||
padding: '5px',
|
||||
borderRadius: '3px',
|
||||
cursor: 'pointer',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
onClick={handleGetConnectedNodes}
|
||||
>
|
||||
Get Connected Nodes
|
||||
</div>
|
||||
</div>
|
||||
</HTMLContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const createNodeIndicator = (shape: AllNodeShapes, editor: any) => {
|
||||
const bounds = editor.getShapeGeometry(shape).bounds
|
||||
const theme = getDefaultColorTheme({ isDarkMode: editor.user.getIsDarkMode() })
|
||||
return (
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={bounds.width}
|
||||
height={bounds.height}
|
||||
fill="none"
|
||||
stroke={theme[shape.props.color].solid}
|
||||
strokeWidth={2}
|
||||
rx={5}
|
||||
ry={5}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const createEdgeComponent = (sourceId: string, targetId: string, editor: any) => {
|
||||
console.log("Creating edge component for:", sourceId, targetId)
|
||||
const edge = {
|
||||
type: 'general_relationship',
|
||||
props: {
|
||||
w: 200,
|
||||
h: 300,
|
||||
color: 'black',
|
||||
__relationshiptype__: '',
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
}
|
||||
};
|
||||
editor.createShape(edge);
|
||||
graphState.addNode(edge);
|
||||
};
|
||||
|
||||
export abstract class BaseNodeShapeUtil<T extends AllNodeShapes> extends ShapeUtil<T> {
|
||||
static override type: string
|
||||
|
||||
static override props: any
|
||||
static override migrations: any
|
||||
|
||||
override isAspectRatioLocked = (_shape: T) => true
|
||||
override canResize = (_shape: T) => true
|
||||
|
||||
abstract override getDefaultProps(): T['props']
|
||||
|
||||
getGeometry(shape: T) {
|
||||
return new Rectangle2d({
|
||||
width: shape.props.w,
|
||||
height: shape.props.h,
|
||||
x: 0,
|
||||
y: 0,
|
||||
isFilled: true,
|
||||
})
|
||||
}
|
||||
|
||||
component(shape: T) {
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
return createNodeComponent(shape, theme, this.editor)
|
||||
}
|
||||
|
||||
indicator(shape: T) {
|
||||
return createNodeIndicator(shape, this.editor)
|
||||
}
|
||||
|
||||
onDrag = (shape: T, dx: number, dy: number) => {
|
||||
return {
|
||||
x: shape.x + dx,
|
||||
y: shape.y + dy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseRelationshipShapeUtil<T extends AllRelationshipShapes> extends ShapeUtil<T> {
|
||||
static override type: string
|
||||
|
||||
static override props: any
|
||||
static override migrations: any
|
||||
|
||||
override isAspectRatioLocked = (_shape: T) => true
|
||||
override canResize = (_shape: T) => true
|
||||
|
||||
abstract override getDefaultProps(): T['props']
|
||||
|
||||
getGeometry(shape: T) {
|
||||
return new Rectangle2d({
|
||||
width: shape.props.w,
|
||||
height: shape.props.h,
|
||||
x: 0,
|
||||
y: 0,
|
||||
isFilled: true,
|
||||
});
|
||||
}
|
||||
|
||||
component(shape: T) {
|
||||
// Define how the edge is rendered
|
||||
return (
|
||||
<line
|
||||
x1={shape.x}
|
||||
y1={shape.y}
|
||||
x2={shape.x}
|
||||
y2={shape.y}
|
||||
stroke={shape.props.color}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
indicator(shape: T) {
|
||||
// Define the indicator for the edge
|
||||
return (
|
||||
<line
|
||||
x1={shape.x}
|
||||
y1={shape.y}
|
||||
x2={shape.x}
|
||||
y2={shape.y}
|
||||
stroke={shape.props.color}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="4 2"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
23
src/utils/tldraw/graph/graph-relationship-migrations.ts
Normal file
23
src/utils/tldraw/graph/graph-relationship-migrations.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { createShapePropsMigrationIds, createShapePropsMigrationSequence } from 'tldraw'
|
||||
|
||||
// Ensure each node type and its migrations are added separately
|
||||
const generalRelationshipVersions = createShapePropsMigrationIds(
|
||||
'general_relationship',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
export const generalRelationshipShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: generalRelationshipVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
20
src/utils/tldraw/graph/graph-relationship-props.ts
Normal file
20
src/utils/tldraw/graph/graph-relationship-props.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { DefaultColorStyle, T, RecordProps } from 'tldraw'
|
||||
import {
|
||||
GeneralRelationshipShape
|
||||
} from './graph-relationship-types'
|
||||
|
||||
// Base node shape props
|
||||
export const baseRelationshipShapeProps = {
|
||||
w: T.number,
|
||||
h: T.number,
|
||||
color: DefaultColorStyle,
|
||||
__relationshiptype__: T.string,
|
||||
source: T.string,
|
||||
target: T.string,
|
||||
}
|
||||
|
||||
// General relationship shape props
|
||||
export const generalRelationshipShapeProps: RecordProps<GeneralRelationshipShape> = {
|
||||
...baseRelationshipShapeProps,
|
||||
}
|
||||
|
||||
14
src/utils/tldraw/graph/graph-relationship-types.ts
Normal file
14
src/utils/tldraw/graph/graph-relationship-types.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { TLBaseShape, TLDefaultColorStyle } from 'tldraw'
|
||||
import {
|
||||
GeneralRelationshipInterface
|
||||
} from '../../../types/graph_relationship_types'
|
||||
|
||||
export type BaseRelationshipShape<T extends string, U> = TLBaseShape<T, {
|
||||
w: number
|
||||
h: number
|
||||
color: TLDefaultColorStyle
|
||||
} & U>;
|
||||
|
||||
export type AllRelationshipShapes = GeneralRelationshipShape;
|
||||
|
||||
export type GeneralRelationshipShape = BaseRelationshipShape<"general_relationship", GeneralRelationshipInterface>;
|
||||
749
src/utils/tldraw/graph/graph-shape-migrations.ts
Normal file
749
src/utils/tldraw/graph/graph-shape-migrations.ts
Normal file
@ -0,0 +1,749 @@
|
||||
import { createShapePropsMigrationIds, createShapePropsMigrationSequence } from 'tldraw'
|
||||
|
||||
// Ensure each node type and its migrations are added separately
|
||||
const userNodeVersions = createShapePropsMigrationIds(
|
||||
'user_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
)
|
||||
|
||||
const developerNodeVersions = createShapePropsMigrationIds(
|
||||
'developer_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const teacherNodeVersions = createShapePropsMigrationIds(
|
||||
'teacher_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const studentNodeVersions = createShapePropsMigrationIds(
|
||||
'student_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
export const userNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: userNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const developerNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: developerNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const teacherNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: teacherNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const studentNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: studentNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// Calendar node shape migrations
|
||||
const calendarNodeVersions = createShapePropsMigrationIds(
|
||||
'calendar_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
)
|
||||
|
||||
const yearNodeVersions = createShapePropsMigrationIds(
|
||||
'calendar_year_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const monthNodeVersions = createShapePropsMigrationIds(
|
||||
'calendar_month_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const weekNodeVersions = createShapePropsMigrationIds(
|
||||
'calendar_week_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const dayNodeVersions = createShapePropsMigrationIds(
|
||||
'calendar_day_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const timeChunkNodeVersions = createShapePropsMigrationIds(
|
||||
'calendar_time_chunk_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
export const calendarNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: calendarNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const yearNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: yearNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const monthNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: monthNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const weekNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: weekNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const dayNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: dayNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const timeChunkNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: timeChunkNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
const schoolNodeVersions = createShapePropsMigrationIds(
|
||||
'school_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
)
|
||||
|
||||
const departmentNodeVersions = createShapePropsMigrationIds(
|
||||
'department_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const roomNodeVersions = createShapePropsMigrationIds(
|
||||
'room_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const subjectClassNodeVersions = createShapePropsMigrationIds(
|
||||
'subject_class_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
export const schoolNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: schoolNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const departmentNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: departmentNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const roomNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: roomNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const subjectClassNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: subjectClassNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
const pastoralStructureNodeVersions = createShapePropsMigrationIds(
|
||||
'pastoral_structure_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
)
|
||||
|
||||
const yearGroupNodeVersions = createShapePropsMigrationIds(
|
||||
'year_group_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const curriculumStructureNodeVersions = createShapePropsMigrationIds(
|
||||
'curriculum_structure_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const keyStageNodeVersions = createShapePropsMigrationIds(
|
||||
'key_stage_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const keyStageSyllabusNodeVersions = createShapePropsMigrationIds(
|
||||
'key_stage_syllabus_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const yearGroupSyllabusNodeVersions = createShapePropsMigrationIds(
|
||||
'year_group_syllabus_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const subjectNodeVersions = createShapePropsMigrationIds(
|
||||
'subject_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const topicNodeVersions = createShapePropsMigrationIds(
|
||||
'topic_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const topicLessonNodeVersions = createShapePropsMigrationIds(
|
||||
'topic_lesson_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const learningStatementNodeVersions = createShapePropsMigrationIds(
|
||||
'learning_statement_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const scienceLabNodeVersions = createShapePropsMigrationIds(
|
||||
'science_lab_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
export const pastoralStructureNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: pastoralStructureNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const yearGroupNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: yearGroupNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const curriculumStructureNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: curriculumStructureNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const keyStageNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: keyStageNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const keyStageSyllabusNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: keyStageSyllabusNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const yearGroupSyllabusNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: yearGroupSyllabusNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const subjectNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: subjectNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const topicNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: topicNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const topicLessonNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: topicLessonNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
export const learningStatementNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: learningStatementNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const scienceLabNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: scienceLabNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
const schoolTimetableNodeVersions = createShapePropsMigrationIds(
|
||||
'school_timetable_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
)
|
||||
|
||||
const academicYearNodeVersions = createShapePropsMigrationIds(
|
||||
'academic_year_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const academicTermNodeVersions = createShapePropsMigrationIds(
|
||||
'academic_term_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const academicWeekNodeVersions = createShapePropsMigrationIds(
|
||||
'academic_week_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const academicDayNodeVersions = createShapePropsMigrationIds(
|
||||
'academic_day_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const academicPeriodNodeVersions = createShapePropsMigrationIds(
|
||||
'academic_period_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const registrationPeriodNodeVersions = createShapePropsMigrationIds(
|
||||
'registration_period_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
export const schoolTimetableNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: schoolTimetableNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const academicYearNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: academicYearNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const academicTermNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: academicTermNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const academicWeekNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: academicWeekNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const academicDayNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: academicDayNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const academicPeriodNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: academicPeriodNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const registrationPeriodNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: registrationPeriodNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const teacherTimetableNodeVersions = createShapePropsMigrationIds(
|
||||
'teacher_timetable_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
)
|
||||
|
||||
const timetableLessonNodeVersions = createShapePropsMigrationIds(
|
||||
'timetable_lesson_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
const plannedLessonNodeVersions = createShapePropsMigrationIds(
|
||||
'planned_lesson_node',
|
||||
{
|
||||
AddSomeProperty: 1,
|
||||
}
|
||||
);
|
||||
|
||||
export const teacherTimetableNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: teacherTimetableNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const timetableLessonNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: timetableLessonNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export const plannedLessonNodeShapeMigrations = createShapePropsMigrationSequence({
|
||||
sequence: [
|
||||
{
|
||||
id: plannedLessonNodeVersions.AddSomeProperty,
|
||||
up(props) {
|
||||
props.someProperty = 'some value'
|
||||
},
|
||||
down(props) {
|
||||
delete props.someProperty
|
||||
},
|
||||
}
|
||||
],
|
||||
})
|
||||
332
src/utils/tldraw/graph/graph-shape-props.ts
Normal file
332
src/utils/tldraw/graph/graph-shape-props.ts
Normal file
@ -0,0 +1,332 @@
|
||||
import { DefaultColorStyle, RecordProps, T } from 'tldraw'
|
||||
import {
|
||||
UserNodeShape,
|
||||
DeveloperNodeShape,
|
||||
TeacherNodeShape,
|
||||
StudentNodeShape,
|
||||
CalendarNodeShape,
|
||||
CalendarYearNodeShape,
|
||||
CalendarMonthNodeShape,
|
||||
CalendarWeekNodeShape,
|
||||
CalendarDayNodeShape,
|
||||
CalendarTimeChunkNodeShape,
|
||||
SchoolNodeShape,
|
||||
DepartmentNodeShape,
|
||||
RoomNodeShape,
|
||||
SubjectClassNodeShape,
|
||||
PastoralStructureNodeShape,
|
||||
YearGroupNodeShape,
|
||||
CurriculumStructureNodeShape,
|
||||
KeyStageNodeShape,
|
||||
KeyStageSyllabusNodeShape,
|
||||
YearGroupSyllabusNodeShape,
|
||||
SubjectNodeShape,
|
||||
TopicNodeShape,
|
||||
TopicLessonNodeShape,
|
||||
LearningStatementNodeShape,
|
||||
ScienceLabNodeShape,
|
||||
SchoolTimetableNodeShape,
|
||||
AcademicYearNodeShape,
|
||||
AcademicTermNodeShape,
|
||||
AcademicWeekNodeShape,
|
||||
AcademicDayNodeShape,
|
||||
AcademicPeriodNodeShape,
|
||||
RegistrationPeriodNodeShape,
|
||||
TeacherTimetableNodeShape,
|
||||
TimetableLessonNodeShape,
|
||||
PlannedLessonNodeShape,
|
||||
} from './graph-shape-types'
|
||||
|
||||
// Base node shape props
|
||||
const baseNodeShapeProps = {
|
||||
w: T.number,
|
||||
h: T.number,
|
||||
color: DefaultColorStyle,
|
||||
__primarylabel__: T.string,
|
||||
unique_id: T.string,
|
||||
path: T.string,
|
||||
created: T.string,
|
||||
merged: T.string,
|
||||
}
|
||||
|
||||
export const userNodeShapeProps: RecordProps<UserNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
user_id: T.string,
|
||||
user_name: T.string,
|
||||
user_email: T.string,
|
||||
worker_node_data: T.string,
|
||||
user_type: T.string,
|
||||
}
|
||||
|
||||
export const developerNodeShapeProps: RecordProps<DeveloperNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
user_id: T.string,
|
||||
user_name: T.string,
|
||||
user_email: T.string,
|
||||
user_type: T.string,
|
||||
}
|
||||
|
||||
export const teacherNodeShapeProps: RecordProps<TeacherNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
teacher_code: T.string,
|
||||
teacher_name_formal: T.string,
|
||||
teacher_email: T.string,
|
||||
worker_db_name: T.string,
|
||||
}
|
||||
|
||||
export const studentNodeShapeProps: RecordProps<StudentNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
student_code: T.string,
|
||||
student_name_formal: T.string,
|
||||
student_email: T.string,
|
||||
worker_db_name: T.string,
|
||||
}
|
||||
|
||||
// Calendar node shape props
|
||||
export const calendarNodeShapeProps: RecordProps<CalendarNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
name: T.string,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
}
|
||||
|
||||
export const calendarYearNodeShapeProps: RecordProps<CalendarYearNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
year: T.string,
|
||||
}
|
||||
|
||||
export const calendarMonthNodeShapeProps: RecordProps<CalendarMonthNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
year: T.string,
|
||||
month: T.string,
|
||||
month_name: T.string,
|
||||
}
|
||||
|
||||
export const calendarWeekNodeShapeProps: RecordProps<CalendarWeekNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
start_date: T.string,
|
||||
week_number: T.string,
|
||||
iso_week: T.string,
|
||||
}
|
||||
|
||||
export const calendarDayNodeShapeProps: RecordProps<CalendarDayNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
date: T.string,
|
||||
day_of_week: T.string,
|
||||
iso_day: T.string,
|
||||
}
|
||||
|
||||
export const calendarTimeChunkNodeShapeProps: RecordProps<CalendarTimeChunkNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
}
|
||||
|
||||
|
||||
// School
|
||||
export const schoolNodeShapeProps: RecordProps<SchoolNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
school_name: T.string,
|
||||
school_website: T.string,
|
||||
school_uuid: T.string,
|
||||
}
|
||||
|
||||
export const departmentNodeShapeProps: RecordProps<DepartmentNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
department_name: T.string,
|
||||
}
|
||||
|
||||
export const roomNodeShapeProps: RecordProps<RoomNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
room_code: T.string,
|
||||
room_name: T.string,
|
||||
}
|
||||
|
||||
export const subjectClassNodeShapeProps: RecordProps<SubjectClassNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
subject_class_code: T.string,
|
||||
year_group: T.string,
|
||||
subject: T.string,
|
||||
subject_code: T.string,
|
||||
}
|
||||
|
||||
// Curriculum
|
||||
export const pastoralStructureNodeShapeProps: RecordProps<PastoralStructureNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
}
|
||||
|
||||
export const yearGroupNodeShapeProps: RecordProps<YearGroupNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
year_group: T.string,
|
||||
year_group_name: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const curriculumStructureNodeShapeProps: RecordProps<CurriculumStructureNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
}
|
||||
|
||||
export const keyStageNodeShapeProps: RecordProps<KeyStageNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
key_stage_name: T.string,
|
||||
key_stage: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const keyStageSyllabusNodeShapeProps: RecordProps<KeyStageSyllabusNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
ks_syllabus_id: T.string,
|
||||
ks_syllabus_name: T.string,
|
||||
ks_syllabus_key_stage: T.string,
|
||||
ks_syllabus_subject: T.string,
|
||||
ks_syllabus_subject_code: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const yearGroupSyllabusNodeShapeProps: RecordProps<YearGroupSyllabusNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
yr_syllabus_id: T.string,
|
||||
yr_syllabus_name: T.string,
|
||||
yr_syllabus_year_group: T.string,
|
||||
yr_syllabus_subject: T.string,
|
||||
yr_syllabus_subject_code: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const subjectNodeShapeProps: RecordProps<SubjectNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
subject_code: T.string,
|
||||
subject_name: T.string,
|
||||
}
|
||||
|
||||
export const topicNodeShapeProps: RecordProps<TopicNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
topic_id: T.string,
|
||||
topic_title: T.string,
|
||||
total_number_of_lessons_for_topic: T.string,
|
||||
topic_type: T.string,
|
||||
topic_assessment_type: T.string,
|
||||
}
|
||||
|
||||
export const topicLessonNodeShapeProps: RecordProps<TopicLessonNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
topic_lesson_id: T.string,
|
||||
topic_lesson_title: T.string,
|
||||
topic_lesson_type: T.string,
|
||||
topic_lesson_length: T.string,
|
||||
topic_lesson_suggested_activities: T.string,
|
||||
topic_lesson_skills_learned: T.string,
|
||||
topic_lesson_weblinks: T.string,
|
||||
|
||||
}
|
||||
|
||||
export const learningStatementNodeShapeProps: RecordProps<LearningStatementNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
lesson_learning_statement_id: T.string,
|
||||
lesson_learning_statement: T.string,
|
||||
lesson_learning_statement_type: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const scienceLabNodeShapeProps: RecordProps<ScienceLabNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
science_lab_id: T.string,
|
||||
science_lab_title: T.string,
|
||||
science_lab_summary: T.string,
|
||||
science_lab_requirements: T.string,
|
||||
science_lab_procedure: T.string,
|
||||
science_lab_safety: T.string,
|
||||
science_lab_weblinks: T.string,
|
||||
}
|
||||
|
||||
// School Timetable
|
||||
export const schoolTimetableNodeShapeProps: RecordProps<SchoolTimetableNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const academicYearNodeShapeProps: RecordProps<AcademicYearNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
year: T.string,
|
||||
}
|
||||
|
||||
|
||||
export const academicTermNodeShapeProps: RecordProps<AcademicTermNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
term_name: T.string,
|
||||
term_number: T.string,
|
||||
start_date: T.string,
|
||||
end_date: T.string,
|
||||
}
|
||||
|
||||
export const academicWeekNodeShapeProps: RecordProps<AcademicWeekNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
academic_week_number: T.string,
|
||||
start_date: T.string,
|
||||
week_type: T.string,
|
||||
}
|
||||
|
||||
export const academicDayNodeShapeProps: RecordProps<AcademicDayNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
academic_day: T.string,
|
||||
date: T.string,
|
||||
day_of_week: T.string,
|
||||
day_type: T.string,
|
||||
}
|
||||
|
||||
export const academicPeriodNodeShapeProps: RecordProps<AcademicPeriodNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
name: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
}
|
||||
|
||||
export const registrationPeriodNodeShapeProps: RecordProps<RegistrationPeriodNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
name: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Teacher Timetable
|
||||
export const teacherTimetableNodeShapeProps: RecordProps<TeacherTimetableNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
}
|
||||
|
||||
export const timetableLessonNodeShapeProps: RecordProps<TimetableLessonNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
subject_class: T.string,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
}
|
||||
|
||||
export const plannedLessonNodeShapeProps: RecordProps<PlannedLessonNodeShape> = {
|
||||
...baseNodeShapeProps,
|
||||
date: T.string,
|
||||
start_time: T.string,
|
||||
end_time: T.string,
|
||||
period_code: T.string,
|
||||
subject_class: T.string,
|
||||
year_group: T.string,
|
||||
subject: T.string,
|
||||
teacher_code: T.string,
|
||||
planning_status: T.string,
|
||||
topic_code: T.string.optional().nullable(),
|
||||
topic_name: T.string.optional().nullable(),
|
||||
lesson_code: T.string.optional().nullable(),
|
||||
lesson_name: T.string.optional().nullable(),
|
||||
learning_statement_codes: T.string.optional().nullable(),
|
||||
learning_statements: T.string.optional().nullable(),
|
||||
learning_resource_codes: T.string.optional().nullable(),
|
||||
learning_resources: T.string.optional().nullable(),
|
||||
}
|
||||
94
src/utils/tldraw/graph/graph-shape-types.ts
Normal file
94
src/utils/tldraw/graph/graph-shape-types.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { TLBaseShape, TLDefaultColorStyle } from 'tldraw'
|
||||
import {
|
||||
UserNodeInterface,
|
||||
DeveloperNodeInterface,
|
||||
TeacherNodeInterface,
|
||||
StudentNodeInterface,
|
||||
CalendarNodeInterface,
|
||||
CalendarYearNodeInterface,
|
||||
CalendarMonthNodeInterface,
|
||||
CalendarWeekNodeInterface,
|
||||
CalendarDayNodeInterface,
|
||||
CalendarTimeChunkNodeInterface,
|
||||
SchoolNodeInterface,
|
||||
DepartmentNodeInterface,
|
||||
RoomNodeInterface,
|
||||
SubjectClassNodeInterface,
|
||||
PastoralStructureNodeInterface,
|
||||
YearGroupNodeInterface,
|
||||
CurriculumStructureNodeInterface,
|
||||
KeyStageNodeInterface,
|
||||
KeyStageSyllabusNodeInterface,
|
||||
YearGroupSyllabusNodeInterface,
|
||||
SubjectNodeInterface,
|
||||
TopicNodeInterface,
|
||||
TopicLessonNodeInterface,
|
||||
LearningStatementNodeInterface,
|
||||
ScienceLabNodeInterface,
|
||||
SchoolTimetableNodeInterface,
|
||||
AcademicYearNodeInterface,
|
||||
AcademicTermNodeInterface,
|
||||
AcademicWeekNodeInterface,
|
||||
AcademicDayNodeInterface,
|
||||
AcademicPeriodNodeInterface,
|
||||
RegistrationPeriodNodeInterface,
|
||||
TeacherTimetableNodeInterface,
|
||||
TimetableLessonNodeInterface,
|
||||
PlannedLessonNodeInterface
|
||||
} from '../../../types/graph_node_types';
|
||||
|
||||
export type BaseNodeShape<T extends string, U> = TLBaseShape<T, {
|
||||
w: number
|
||||
h: number
|
||||
color: TLDefaultColorStyle
|
||||
} & U>;
|
||||
|
||||
export type AllNodeShapes = UserNodeShape | DeveloperNodeShape | TeacherNodeShape | StudentNodeShape | CalendarNodeShape | CalendarYearNodeShape | CalendarMonthNodeShape | CalendarWeekNodeShape | CalendarDayNodeShape | CalendarTimeChunkNodeShape | ScienceLabNodeShape | KeyStageSyllabusNodeShape | YearGroupNodeShape | YearGroupSyllabusNodeShape | CurriculumStructureNodeShape | TopicNodeShape | TopicLessonNodeShape | LearningStatementNodeShape | SchoolNodeShape | TeacherTimetableNodeShape | TimetableLessonNodeShape | PlannedLessonNodeShape | SchoolTimetableNodeShape | SubjectClassNodeShape | SubjectNodeShape | AcademicDayNodeShape | AcademicWeekNodeShape | AcademicYearNodeShape | AcademicTermNodeShape | AcademicPeriodNodeShape | RegistrationPeriodNodeShape | PastoralStructureNodeShape | KeyStageNodeShape | RoomNodeShape | DepartmentNodeShape;
|
||||
|
||||
// User entity node shapes
|
||||
export type UserNodeShape = BaseNodeShape<"user_node", UserNodeInterface>;
|
||||
export type DeveloperNodeShape = BaseNodeShape<"developer_node", DeveloperNodeInterface>;
|
||||
export type TeacherNodeShape = BaseNodeShape<"teacher_node", TeacherNodeInterface>;
|
||||
export type StudentNodeShape = BaseNodeShape<"student_node", StudentNodeInterface>;
|
||||
|
||||
// Calendar node shapes
|
||||
export type CalendarNodeShape = BaseNodeShape<"calendar_node", CalendarNodeInterface>;
|
||||
export type CalendarYearNodeShape = BaseNodeShape<"calendar_year_node", CalendarYearNodeInterface>;
|
||||
export type CalendarMonthNodeShape = BaseNodeShape<"calendar_month_node", CalendarMonthNodeInterface>;
|
||||
export type CalendarWeekNodeShape = BaseNodeShape<"calendar_week_node", CalendarWeekNodeInterface>;
|
||||
export type CalendarDayNodeShape = BaseNodeShape<"calendar_day_node", CalendarDayNodeInterface>;
|
||||
export type CalendarTimeChunkNodeShape = BaseNodeShape<"calendar_time_chunk_node", CalendarTimeChunkNodeInterface>;
|
||||
|
||||
// School entity node shapes
|
||||
export type SchoolNodeShape = BaseNodeShape<"school_node", SchoolNodeInterface>;
|
||||
export type DepartmentNodeShape = BaseNodeShape<"department_node", DepartmentNodeInterface>;
|
||||
export type RoomNodeShape = BaseNodeShape<"room_node", RoomNodeInterface>;
|
||||
export type SubjectClassNodeShape = BaseNodeShape<"subject_class_node", SubjectClassNodeInterface>;
|
||||
|
||||
// Curriculum entity node shapes
|
||||
export type PastoralStructureNodeShape = BaseNodeShape<"pastoral_structure_node", PastoralStructureNodeInterface>;
|
||||
export type YearGroupNodeShape = BaseNodeShape<"year_group_node", YearGroupNodeInterface>;
|
||||
export type CurriculumStructureNodeShape = BaseNodeShape<"curriculum_structure_node", CurriculumStructureNodeInterface>;
|
||||
export type KeyStageNodeShape = BaseNodeShape<"key_stage_node", KeyStageNodeInterface>;
|
||||
export type KeyStageSyllabusNodeShape = BaseNodeShape<"key_stage_syllabus_node", KeyStageSyllabusNodeInterface>;
|
||||
export type YearGroupSyllabusNodeShape = BaseNodeShape<"year_group_syllabus_node", YearGroupSyllabusNodeInterface>;
|
||||
export type SubjectNodeShape = BaseNodeShape<"subject_node", SubjectNodeInterface>;
|
||||
export type TopicNodeShape = BaseNodeShape<"topic_node", TopicNodeInterface>;
|
||||
export type TopicLessonNodeShape = BaseNodeShape<"topic_lesson_node", TopicLessonNodeInterface>;
|
||||
export type LearningStatementNodeShape = BaseNodeShape<"learning_statement_node", LearningStatementNodeInterface>;
|
||||
export type ScienceLabNodeShape = BaseNodeShape<"science_lab_node", ScienceLabNodeInterface>;
|
||||
|
||||
// School timetable entity node shapes
|
||||
export type SchoolTimetableNodeShape = BaseNodeShape<"school_timetable_node", SchoolTimetableNodeInterface>;
|
||||
export type AcademicYearNodeShape = BaseNodeShape<"academic_year_node", AcademicYearNodeInterface>;
|
||||
export type AcademicTermNodeShape = BaseNodeShape<"academic_term_node", AcademicTermNodeInterface>;
|
||||
export type AcademicWeekNodeShape = BaseNodeShape<"academic_week_node", AcademicWeekNodeInterface>;
|
||||
export type AcademicDayNodeShape = BaseNodeShape<"academic_day_node", AcademicDayNodeInterface>;
|
||||
export type AcademicPeriodNodeShape = BaseNodeShape<"academic_period_node", AcademicPeriodNodeInterface>;
|
||||
export type RegistrationPeriodNodeShape = BaseNodeShape<"registration_period_node", RegistrationPeriodNodeInterface>;
|
||||
|
||||
// Teacher timetable entity node shapes
|
||||
export type TeacherTimetableNodeShape = BaseNodeShape<"teacher_timetable_node", TeacherTimetableNodeInterface>;
|
||||
export type TimetableLessonNodeShape = BaseNodeShape<"timetable_lesson_node", TimetableLessonNodeInterface>;
|
||||
export type PlannedLessonNodeShape = BaseNodeShape<"planned_lesson_node", PlannedLessonNodeInterface>;
|
||||
|
||||
971
src/utils/tldraw/graph/graphShapeUtil.tsx
Normal file
971
src/utils/tldraw/graph/graphShapeUtil.tsx
Normal file
@ -0,0 +1,971 @@
|
||||
import { BaseNodeShapeUtil, BaseRelationshipShapeUtil } from './baseNodeShapeUtil'
|
||||
import {
|
||||
UserNodeShape,
|
||||
DeveloperNodeShape,
|
||||
TeacherNodeShape,
|
||||
StudentNodeShape,
|
||||
CalendarNodeShape,
|
||||
CalendarYearNodeShape,
|
||||
CalendarMonthNodeShape,
|
||||
CalendarWeekNodeShape,
|
||||
CalendarDayNodeShape,
|
||||
CalendarTimeChunkNodeShape,
|
||||
ScienceLabNodeShape,
|
||||
KeyStageSyllabusNodeShape,
|
||||
YearGroupNodeShape,
|
||||
YearGroupSyllabusNodeShape,
|
||||
CurriculumStructureNodeShape,
|
||||
TopicNodeShape,
|
||||
TopicLessonNodeShape,
|
||||
LearningStatementNodeShape,
|
||||
SchoolNodeShape,
|
||||
TeacherTimetableNodeShape,
|
||||
TimetableLessonNodeShape,
|
||||
PlannedLessonNodeShape,
|
||||
SchoolTimetableNodeShape,
|
||||
SubjectClassNodeShape,
|
||||
SubjectNodeShape,
|
||||
AcademicDayNodeShape,
|
||||
AcademicWeekNodeShape,
|
||||
AcademicYearNodeShape,
|
||||
AcademicTermNodeShape,
|
||||
AcademicPeriodNodeShape,
|
||||
RegistrationPeriodNodeShape,
|
||||
PastoralStructureNodeShape,
|
||||
KeyStageNodeShape,
|
||||
RoomNodeShape,
|
||||
DepartmentNodeShape,
|
||||
} from './graph-shape-types'
|
||||
import {
|
||||
userNodeShapeProps,
|
||||
developerNodeShapeProps,
|
||||
teacherNodeShapeProps,
|
||||
studentNodeShapeProps,
|
||||
calendarNodeShapeProps,
|
||||
calendarYearNodeShapeProps,
|
||||
calendarMonthNodeShapeProps,
|
||||
calendarWeekNodeShapeProps,
|
||||
calendarDayNodeShapeProps,
|
||||
calendarTimeChunkNodeShapeProps,
|
||||
scienceLabNodeShapeProps,
|
||||
keyStageSyllabusNodeShapeProps,
|
||||
yearGroupNodeShapeProps,
|
||||
yearGroupSyllabusNodeShapeProps,
|
||||
curriculumStructureNodeShapeProps,
|
||||
topicNodeShapeProps,
|
||||
topicLessonNodeShapeProps,
|
||||
learningStatementNodeShapeProps,
|
||||
schoolNodeShapeProps,
|
||||
teacherTimetableNodeShapeProps,
|
||||
timetableLessonNodeShapeProps,
|
||||
plannedLessonNodeShapeProps,
|
||||
schoolTimetableNodeShapeProps,
|
||||
subjectClassNodeShapeProps,
|
||||
subjectNodeShapeProps,
|
||||
academicDayNodeShapeProps,
|
||||
academicWeekNodeShapeProps,
|
||||
academicYearNodeShapeProps,
|
||||
academicTermNodeShapeProps,
|
||||
academicPeriodNodeShapeProps,
|
||||
registrationPeriodNodeShapeProps,
|
||||
pastoralStructureNodeShapeProps,
|
||||
keyStageNodeShapeProps,
|
||||
departmentNodeShapeProps,
|
||||
roomNodeShapeProps
|
||||
} from './graph-shape-props'
|
||||
import {
|
||||
userNodeShapeMigrations,
|
||||
developerNodeShapeMigrations,
|
||||
teacherNodeShapeMigrations,
|
||||
studentNodeShapeMigrations,
|
||||
calendarNodeShapeMigrations,
|
||||
yearNodeShapeMigrations,
|
||||
monthNodeShapeMigrations,
|
||||
weekNodeShapeMigrations,
|
||||
dayNodeShapeMigrations,
|
||||
timeChunkNodeShapeMigrations,
|
||||
keyStageSyllabusNodeShapeMigrations,
|
||||
yearGroupNodeShapeMigrations,
|
||||
yearGroupSyllabusNodeShapeMigrations,
|
||||
curriculumStructureNodeShapeMigrations,
|
||||
topicNodeShapeMigrations,
|
||||
topicLessonNodeShapeMigrations,
|
||||
learningStatementNodeShapeMigrations,
|
||||
scienceLabNodeShapeMigrations,
|
||||
schoolNodeShapeMigrations,
|
||||
teacherTimetableNodeShapeMigrations,
|
||||
timetableLessonNodeShapeMigrations,
|
||||
plannedLessonNodeShapeMigrations,
|
||||
schoolTimetableNodeShapeMigrations,
|
||||
subjectClassNodeShapeMigrations,
|
||||
subjectNodeShapeMigrations,
|
||||
academicDayNodeShapeMigrations,
|
||||
academicWeekNodeShapeMigrations,
|
||||
academicYearNodeShapeMigrations,
|
||||
academicTermNodeShapeMigrations,
|
||||
academicPeriodNodeShapeMigrations,
|
||||
registrationPeriodNodeShapeMigrations,
|
||||
pastoralStructureNodeShapeMigrations,
|
||||
roomNodeShapeMigrations,
|
||||
departmentNodeShapeMigrations,
|
||||
keyStageNodeShapeMigrations,
|
||||
} from './graph-shape-migrations'
|
||||
import { GeneralRelationshipShape } from './graph-relationship-types'
|
||||
import { generalRelationshipShapeProps } from './graph-relationship-props'
|
||||
import { generalRelationshipShapeMigrations } from './graph-relationship-migrations'
|
||||
|
||||
// User Nodes
|
||||
export class UserNodeShapeUtil extends BaseNodeShapeUtil<UserNodeShape> {
|
||||
static override type = 'user_node' as const
|
||||
static override props = userNodeShapeProps
|
||||
static override migrations = userNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): UserNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'blue',
|
||||
__primarylabel__: 'User',
|
||||
unique_id: '',
|
||||
user_name: '',
|
||||
user_email: '',
|
||||
user_type: '',
|
||||
user_id: '',
|
||||
worker_node_data: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DeveloperNodeShapeUtil extends BaseNodeShapeUtil<DeveloperNodeShape> {
|
||||
static override type = 'developer_node' as const
|
||||
static override props = developerNodeShapeProps
|
||||
static override migrations = developerNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): DeveloperNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Developer',
|
||||
unique_id: '',
|
||||
user_name: '',
|
||||
user_email: '',
|
||||
user_type: '',
|
||||
user_id: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TeacherNodeShapeUtil extends BaseNodeShapeUtil<TeacherNodeShape> {
|
||||
static override type = 'teacher_node' as const
|
||||
static override props = teacherNodeShapeProps
|
||||
static override migrations = teacherNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): TeacherNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Teacher',
|
||||
unique_id: '',
|
||||
teacher_code: '',
|
||||
teacher_name_formal: '',
|
||||
teacher_email: '',
|
||||
worker_db_name: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class StudentNodeShapeUtil extends BaseNodeShapeUtil<StudentNodeShape> {
|
||||
static override type = 'student_node' as const
|
||||
static override props = studentNodeShapeProps
|
||||
static override migrations = studentNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): StudentNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Student',
|
||||
unique_id: '',
|
||||
student_code: '',
|
||||
student_name_formal: '',
|
||||
student_email: '',
|
||||
worker_db_name: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calendar Nodes
|
||||
export class CalendarNodeShapeUtil extends BaseNodeShapeUtil<CalendarNodeShape> {
|
||||
static override type = 'calendar_node' as const
|
||||
static override props = calendarNodeShapeProps
|
||||
static override migrations = calendarNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CalendarNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Calendar',
|
||||
unique_id: '',
|
||||
name: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CalendarYearNodeShapeUtil extends BaseNodeShapeUtil<CalendarYearNodeShape> {
|
||||
static override type = 'calendar_year_node' as const
|
||||
static override props = calendarYearNodeShapeProps
|
||||
static override migrations = yearNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CalendarYearNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Calendar Year',
|
||||
unique_id: '',
|
||||
year: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CalendarMonthNodeShapeUtil extends BaseNodeShapeUtil<CalendarMonthNodeShape> {
|
||||
static override type = 'calendar_month_node' as const
|
||||
static override props = calendarMonthNodeShapeProps
|
||||
static override migrations = monthNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CalendarMonthNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Calendar Month',
|
||||
unique_id: '',
|
||||
year: '',
|
||||
month: '',
|
||||
month_name: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CalendarWeekNodeShapeUtil extends BaseNodeShapeUtil<CalendarWeekNodeShape> {
|
||||
static override type = 'calendar_week_node' as const
|
||||
static override props = calendarWeekNodeShapeProps
|
||||
static override migrations = weekNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CalendarWeekNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Calendar Week',
|
||||
unique_id: '',
|
||||
start_date: '',
|
||||
week_number: '',
|
||||
iso_week: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CalendarDayNodeShapeUtil extends BaseNodeShapeUtil<CalendarDayNodeShape> {
|
||||
static override type = 'calendar_day_node' as const
|
||||
static override props = calendarDayNodeShapeProps
|
||||
static override migrations = dayNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CalendarDayNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Calendar Day',
|
||||
unique_id: '',
|
||||
date: '',
|
||||
day_of_week: '',
|
||||
iso_day: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CalendarTimeChunkNodeShapeUtil extends BaseNodeShapeUtil<CalendarTimeChunkNodeShape> {
|
||||
static override type = 'calendar_time_chunk_node' as const
|
||||
static override props = calendarTimeChunkNodeShapeProps
|
||||
static override migrations = timeChunkNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CalendarTimeChunkNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Calendar Time Chunk',
|
||||
unique_id: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// School Nodes
|
||||
export class SubjectClassNodeShapeUtil extends BaseNodeShapeUtil<SubjectClassNodeShape> {
|
||||
static override type = 'subject_class_node' as const
|
||||
static override props = subjectClassNodeShapeProps
|
||||
static override migrations = subjectClassNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): SubjectClassNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Subject Class',
|
||||
unique_id: '',
|
||||
subject_class_code: '',
|
||||
year_group: '',
|
||||
subject: '',
|
||||
subject_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SchoolNodeShapeUtil extends BaseNodeShapeUtil<SchoolNodeShape> {
|
||||
static override type = 'school_node' as const
|
||||
static override props = schoolNodeShapeProps
|
||||
static override migrations = schoolNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): SchoolNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'School',
|
||||
unique_id: '',
|
||||
school_uuid: '',
|
||||
school_name: '',
|
||||
school_website: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DepartmentNodeShapeUtil extends BaseNodeShapeUtil<DepartmentNodeShape> {
|
||||
static override type = 'department_node' as const
|
||||
static override props = departmentNodeShapeProps
|
||||
static override migrations = departmentNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): DepartmentNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Department',
|
||||
unique_id: '',
|
||||
department_name: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RoomNodeShapeUtil extends BaseNodeShapeUtil<RoomNodeShape> {
|
||||
static override type = 'room_node' as const
|
||||
static override props = roomNodeShapeProps
|
||||
static override migrations = roomNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): RoomNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Room',
|
||||
unique_id: '',
|
||||
room_name: '',
|
||||
room_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Curriculum Nodes
|
||||
export class PastoralStructureNodeShapeUtil extends BaseNodeShapeUtil<PastoralStructureNodeShape> {
|
||||
static override type = 'pastoral_structure_node' as const
|
||||
static override props = pastoralStructureNodeShapeProps
|
||||
static override migrations = pastoralStructureNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): PastoralStructureNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 130,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Pastoral Structure',
|
||||
unique_id: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class YearGroupNodeShapeUtil extends BaseNodeShapeUtil<YearGroupNodeShape> {
|
||||
static override type = 'year_group_node' as const
|
||||
static override props = yearGroupNodeShapeProps
|
||||
static override migrations = yearGroupNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): YearGroupNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 150,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Year Group',
|
||||
unique_id: '',
|
||||
year_group: '',
|
||||
year_group_name: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CurriculumStructureNodeShapeUtil extends BaseNodeShapeUtil<CurriculumStructureNodeShape> {
|
||||
static override type = 'curriculum_structure_node' as const
|
||||
static override props = curriculumStructureNodeShapeProps
|
||||
static override migrations = curriculumStructureNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): CurriculumStructureNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 130,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Curriculum Structure',
|
||||
unique_id: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class KeyStageNodeShapeUtil extends BaseNodeShapeUtil<KeyStageNodeShape> {
|
||||
static override type = 'key_stage_node' as const
|
||||
static override props = keyStageNodeShapeProps
|
||||
static override migrations = keyStageNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): KeyStageNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 150,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Key Stage',
|
||||
unique_id: '',
|
||||
key_stage_name: '',
|
||||
key_stage: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class KeyStageSyllabusNodeShapeUtil extends BaseNodeShapeUtil<KeyStageSyllabusNodeShape> {
|
||||
static override type = 'key_stage_syllabus_node' as const
|
||||
static override props = keyStageSyllabusNodeShapeProps
|
||||
static override migrations = keyStageSyllabusNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): KeyStageSyllabusNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Key Stage Syllabus',
|
||||
unique_id: '',
|
||||
ks_syllabus_id: '',
|
||||
ks_syllabus_name: '',
|
||||
ks_syllabus_key_stage: '',
|
||||
ks_syllabus_subject: '',
|
||||
ks_syllabus_subject_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class YearGroupSyllabusNodeShapeUtil extends BaseNodeShapeUtil<YearGroupSyllabusNodeShape> {
|
||||
static override type = 'year_group_syllabus_node' as const
|
||||
static override props = yearGroupSyllabusNodeShapeProps
|
||||
static override migrations = yearGroupSyllabusNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): YearGroupSyllabusNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Year Group Syllabus',
|
||||
unique_id: '',
|
||||
yr_syllabus_id: '',
|
||||
yr_syllabus_name: '',
|
||||
yr_syllabus_year_group: '',
|
||||
yr_syllabus_subject: '',
|
||||
yr_syllabus_subject_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SubjectNodeShapeUtil extends BaseNodeShapeUtil<SubjectNodeShape> {
|
||||
static override type = 'subject_node' as const
|
||||
static override props = subjectNodeShapeProps
|
||||
static override migrations = subjectNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): SubjectNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Subject',
|
||||
unique_id: '',
|
||||
subject_code: '',
|
||||
subject_name: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TopicNodeShapeUtil extends BaseNodeShapeUtil<TopicNodeShape> {
|
||||
static override type = 'topic_node' as const
|
||||
static override props = topicNodeShapeProps
|
||||
static override migrations = topicNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): TopicNodeShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 400,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Topic',
|
||||
unique_id: '',
|
||||
topic_id: '',
|
||||
topic_title: '',
|
||||
total_number_of_lessons_for_topic: '',
|
||||
topic_type: '',
|
||||
topic_assessment_type: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TopicLessonNodeShapeUtil extends BaseNodeShapeUtil<TopicLessonNodeShape> {
|
||||
static override type = 'topic_lesson_node' as const
|
||||
static override props = topicLessonNodeShapeProps
|
||||
static override migrations = topicLessonNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): TopicLessonNodeShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 500,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Topic Lesson',
|
||||
unique_id: '',
|
||||
topic_lesson_id: '',
|
||||
topic_lesson_title: '',
|
||||
topic_lesson_type: '',
|
||||
topic_lesson_length: '',
|
||||
topic_lesson_skills_learned: '',
|
||||
topic_lesson_suggested_activities: '',
|
||||
topic_lesson_weblinks: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class LearningStatementNodeShapeUtil extends BaseNodeShapeUtil<LearningStatementNodeShape> {
|
||||
static override type = 'learning_statement_node' as const
|
||||
static override props = learningStatementNodeShapeProps
|
||||
static override migrations = learningStatementNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): LearningStatementNodeShape['props'] {
|
||||
return {
|
||||
w: 180,
|
||||
h: 300,
|
||||
color: 'light-blue',
|
||||
__primarylabel__: 'Learning Statement',
|
||||
unique_id: '',
|
||||
lesson_learning_statement_id: '',
|
||||
lesson_learning_statement: '',
|
||||
lesson_learning_statement_type: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ScienceLabNodeShapeUtil extends BaseNodeShapeUtil<ScienceLabNodeShape> {
|
||||
static override type = 'science_lab_node' as const
|
||||
static override props = scienceLabNodeShapeProps
|
||||
static override migrations = scienceLabNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): ScienceLabNodeShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 400,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Science Lab',
|
||||
unique_id: '',
|
||||
science_lab_id: '',
|
||||
science_lab_title: '',
|
||||
science_lab_summary: '',
|
||||
science_lab_requirements: '',
|
||||
science_lab_procedure: '',
|
||||
science_lab_safety: '',
|
||||
science_lab_weblinks: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// School Timetable Nodes
|
||||
export class SchoolTimetableNodeShapeUtil extends BaseNodeShapeUtil<SchoolTimetableNodeShape> {
|
||||
static override type = 'school_timetable_node' as const
|
||||
|
||||
static override props = schoolTimetableNodeShapeProps
|
||||
static override migrations = schoolTimetableNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): SchoolTimetableNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'School Timetable',
|
||||
unique_id: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AcademicYearNodeShapeUtil extends BaseNodeShapeUtil<AcademicYearNodeShape> {
|
||||
static override type = 'academic_year_node' as const
|
||||
static override props = academicYearNodeShapeProps
|
||||
static override migrations = academicYearNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): AcademicYearNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Academic Year',
|
||||
unique_id: '',
|
||||
year: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AcademicTermNodeShapeUtil extends BaseNodeShapeUtil<AcademicTermNodeShape> {
|
||||
static override type = 'academic_term_node' as const
|
||||
static override props = academicTermNodeShapeProps
|
||||
static override migrations = academicTermNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): AcademicTermNodeShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Academic Term',
|
||||
unique_id: '',
|
||||
term_name: '',
|
||||
term_number: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AcademicWeekNodeShapeUtil extends BaseNodeShapeUtil<AcademicWeekNodeShape> {
|
||||
static override type = 'academic_week_node' as const
|
||||
static override props = academicWeekNodeShapeProps
|
||||
static override migrations = academicWeekNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): AcademicWeekNodeShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Academic Week',
|
||||
unique_id: '',
|
||||
start_date: '',
|
||||
week_type: '',
|
||||
academic_week_number: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AcademicDayNodeShapeUtil extends BaseNodeShapeUtil<AcademicDayNodeShape> {
|
||||
static override type = 'academic_day_node' as const
|
||||
static override props = academicDayNodeShapeProps
|
||||
static override migrations = academicDayNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): AcademicDayNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Academic Day',
|
||||
unique_id: '',
|
||||
academic_day: '',
|
||||
date: '',
|
||||
day_of_week: '',
|
||||
day_type: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AcademicPeriodNodeShapeUtil extends BaseNodeShapeUtil<AcademicPeriodNodeShape> {
|
||||
static override type = 'academic_period_node' as const
|
||||
static override props = academicPeriodNodeShapeProps
|
||||
static override migrations = academicPeriodNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): AcademicPeriodNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 300,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Academic Period',
|
||||
unique_id: '',
|
||||
name: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RegistrationPeriodNodeShapeUtil extends BaseNodeShapeUtil<RegistrationPeriodNodeShape> {
|
||||
static override type = 'registration_period_node' as const
|
||||
static override props = registrationPeriodNodeShapeProps
|
||||
static override migrations = registrationPeriodNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): RegistrationPeriodNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 200,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Registration Period',
|
||||
unique_id: '',
|
||||
name: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Teacher Timetable Nodes
|
||||
export class TeacherTimetableNodeShapeUtil extends BaseNodeShapeUtil<TeacherTimetableNodeShape> {
|
||||
static override type = 'teacher_timetable_node' as const
|
||||
static override props = teacherTimetableNodeShapeProps
|
||||
static override migrations = teacherTimetableNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): TeacherTimetableNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 130,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Teacher Timetable',
|
||||
unique_id: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TimetableLessonNodeShapeUtil extends BaseNodeShapeUtil<TimetableLessonNodeShape> {
|
||||
static override type = 'timetable_lesson_node' as const
|
||||
static override props = timetableLessonNodeShapeProps
|
||||
static override migrations = timetableLessonNodeShapeMigrations
|
||||
|
||||
override isAspectRatioLocked = (_shape: TimetableLessonNodeShape) => true
|
||||
override canResize = (_shape: TimetableLessonNodeShape) => true
|
||||
|
||||
getDefaultProps(): TimetableLessonNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 250,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Timetable Lesson',
|
||||
unique_id: '',
|
||||
subject_class: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PlannedLessonNodeShapeUtil extends BaseNodeShapeUtil<PlannedLessonNodeShape> {
|
||||
static override type = 'planned_lesson_node' as const
|
||||
static override props = plannedLessonNodeShapeProps
|
||||
static override migrations = plannedLessonNodeShapeMigrations
|
||||
|
||||
getDefaultProps(): PlannedLessonNodeShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 250,
|
||||
color: 'white',
|
||||
__primarylabel__: 'Planned Lesson',
|
||||
unique_id: '',
|
||||
date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
period_code: '',
|
||||
subject_class: '',
|
||||
year_group: '',
|
||||
subject: '',
|
||||
teacher_code: '',
|
||||
planning_status: '',
|
||||
topic_code: '',
|
||||
topic_name: '',
|
||||
lesson_code: '',
|
||||
lesson_name: '',
|
||||
learning_statement_codes: '',
|
||||
learning_statements: '',
|
||||
learning_resource_codes: '',
|
||||
learning_resources: '',
|
||||
path: '',
|
||||
created: '',
|
||||
merged: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Relationships
|
||||
export class GeneralRelationshipShapeUtil extends BaseRelationshipShapeUtil<GeneralRelationshipShape> {
|
||||
static override type = 'general_relationship' as const
|
||||
static override props = generalRelationshipShapeProps
|
||||
static override migrations = generalRelationshipShapeMigrations
|
||||
|
||||
getDefaultProps(): GeneralRelationshipShape['props'] {
|
||||
return {
|
||||
w: 200,
|
||||
h: 250,
|
||||
color: 'black',
|
||||
__relationshiptype__: '',
|
||||
source: '',
|
||||
target: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const allShapeUtils = [
|
||||
DeveloperNodeShapeUtil,
|
||||
TeacherNodeShapeUtil,
|
||||
StudentNodeShapeUtil,
|
||||
UserNodeShapeUtil,
|
||||
TeacherTimetableNodeShapeUtil,
|
||||
TimetableLessonNodeShapeUtil,
|
||||
PlannedLessonNodeShapeUtil,
|
||||
SchoolNodeShapeUtil,
|
||||
CalendarNodeShapeUtil,
|
||||
CalendarYearNodeShapeUtil,
|
||||
CalendarMonthNodeShapeUtil,
|
||||
CalendarWeekNodeShapeUtil,
|
||||
CalendarDayNodeShapeUtil,
|
||||
CalendarTimeChunkNodeShapeUtil,
|
||||
ScienceLabNodeShapeUtil,
|
||||
KeyStageSyllabusNodeShapeUtil,
|
||||
YearGroupSyllabusNodeShapeUtil,
|
||||
CurriculumStructureNodeShapeUtil,
|
||||
TopicNodeShapeUtil,
|
||||
TopicLessonNodeShapeUtil,
|
||||
LearningStatementNodeShapeUtil,
|
||||
SchoolTimetableNodeShapeUtil,
|
||||
AcademicYearNodeShapeUtil,
|
||||
AcademicTermNodeShapeUtil,
|
||||
AcademicWeekNodeShapeUtil,
|
||||
AcademicDayNodeShapeUtil,
|
||||
AcademicPeriodNodeShapeUtil,
|
||||
RegistrationPeriodNodeShapeUtil,
|
||||
DepartmentNodeShapeUtil,
|
||||
RoomNodeShapeUtil,
|
||||
PastoralStructureNodeShapeUtil,
|
||||
YearGroupNodeShapeUtil,
|
||||
KeyStageNodeShapeUtil
|
||||
];
|
||||
102
src/utils/tldraw/graph/graphStateUtil.tsx
Normal file
102
src/utils/tldraw/graph/graphStateUtil.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import dagre from '@dagrejs/dagre';
|
||||
import { createShapeId, Editor } from 'tldraw';
|
||||
|
||||
const graphState = {
|
||||
g: new dagre.graphlib.Graph(),
|
||||
nodeData: new Map<string, any>(),
|
||||
editor: null as Editor | null,
|
||||
|
||||
initGraph: () => {
|
||||
graphState.g.setGraph({});
|
||||
graphState.g.setDefaultEdgeLabel(() => ({}));
|
||||
},
|
||||
|
||||
updateNodesWithDagre: () => {
|
||||
dagre.layout(graphState.g);
|
||||
// Update positions in nodeData after layout
|
||||
graphState.g.nodes().forEach((id) => {
|
||||
const node = graphState.g.node(id);
|
||||
if (graphState.nodeData.has(id)) {
|
||||
const fullNode = graphState.nodeData.get(id);
|
||||
fullNode.x = node.x;
|
||||
fullNode.y = node.y;
|
||||
graphState.nodeData.set(id, fullNode);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateShapesWithDagre: () => {
|
||||
console.log("Updating shapes with dagre...");
|
||||
if (!graphState.editor) {
|
||||
console.error("Editor is not set. Call setEditor before updating shapes.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Updating nodes with dagre...");
|
||||
graphState.updateNodesWithDagre();
|
||||
console.log("Nodes updated with dagre...");
|
||||
|
||||
console.log("Updating set of shapes with dagre...");
|
||||
graphState.nodeData.forEach((shape, id) => {
|
||||
console.log("Updating shape with dagre:", shape, id);
|
||||
const node = graphState.g.node(id);
|
||||
console.log("Node without w and h:", node);
|
||||
const nodeWithWidthAndHeight = {
|
||||
...node,
|
||||
width: shape.w,
|
||||
height: shape.h
|
||||
}
|
||||
console.log("Node with w and h:", nodeWithWidthAndHeight);
|
||||
|
||||
if (nodeWithWidthAndHeight) {
|
||||
console.log("Updating shape:", shape);
|
||||
|
||||
graphState.editor!.updateShape({
|
||||
id: createShapeId(node.label),
|
||||
type: shape.type,
|
||||
x: nodeWithWidthAndHeight.x - nodeWithWidthAndHeight.width / 2,
|
||||
y: nodeWithWidthAndHeight.y - nodeWithWidthAndHeight.height / 2,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addNode: (shape: any) => {
|
||||
console.log("Adding shape to graphState:", shape);
|
||||
const id = shape.props.unique_id;
|
||||
console.log("Adding node to graphState:", id);
|
||||
graphState.g.setNode(id, {
|
||||
label: id,
|
||||
width: shape.props.w,
|
||||
height: shape.props.h
|
||||
});
|
||||
graphState.nodeData.set(id, shape);
|
||||
},
|
||||
|
||||
addEdge: (source: string, target: string) => {
|
||||
graphState.g.setEdge(source, target);
|
||||
},
|
||||
|
||||
getNode: (id: string) => {
|
||||
return graphState.nodeData.get(id);
|
||||
},
|
||||
|
||||
getAllNodes: () => {
|
||||
return Array.from(graphState.nodeData.values()).filter(item => {
|
||||
// Check if the item has a type property and it's not an edge type
|
||||
return item.type && !item.type.includes('relationship');
|
||||
});
|
||||
},
|
||||
|
||||
getEdges: () => {
|
||||
return graphState.g.edges();
|
||||
},
|
||||
|
||||
setEditor: (editor: Editor) => {
|
||||
graphState.editor = editor;
|
||||
}
|
||||
};
|
||||
|
||||
graphState.initGraph();
|
||||
|
||||
export default graphState;
|
||||
689
src/utils/tldraw/graph/nodeComponents.tsx
Normal file
689
src/utils/tldraw/graph/nodeComponents.tsx
Normal file
@ -0,0 +1,689 @@
|
||||
import React from 'react';
|
||||
import { AllNodeShapes } from './graph-shape-types';
|
||||
|
||||
interface NodeComponentProps<T extends AllNodeShapes = AllNodeShapes> {
|
||||
shape: T;
|
||||
theme: any;
|
||||
}
|
||||
|
||||
interface BaseNodeProps {
|
||||
__primarylabel__: string;
|
||||
unique_id: string;
|
||||
}
|
||||
|
||||
interface TeacherNodeProps extends BaseNodeProps {
|
||||
teacher_name_formal: string;
|
||||
teacher_code: string;
|
||||
teacher_email: string;
|
||||
worker_db_name: string;
|
||||
}
|
||||
|
||||
interface StudentNodeProps extends BaseNodeProps {
|
||||
student_name_formal: string;
|
||||
student_code: string;
|
||||
student_email: string;
|
||||
worker_db_name: string;
|
||||
}
|
||||
|
||||
interface UserNodeProps extends BaseNodeProps {
|
||||
user_name: string;
|
||||
user_email: string;
|
||||
user_type: string;
|
||||
}
|
||||
|
||||
interface CalendarNodeProps extends BaseNodeProps {
|
||||
name: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
interface CalendarNodeProps extends BaseNodeProps {
|
||||
name: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
interface CalendarYearNodeProps extends BaseNodeProps {
|
||||
year: string;
|
||||
}
|
||||
|
||||
interface CalendarMonthNodeProps extends BaseNodeProps {
|
||||
month_name: string;
|
||||
year: string;
|
||||
}
|
||||
|
||||
interface CalendarWeekNodeProps extends BaseNodeProps {
|
||||
start_date: string;
|
||||
iso_week: string;
|
||||
}
|
||||
|
||||
interface CalendarDayNodeProps extends BaseNodeProps {
|
||||
day_of_week: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
interface CalendarTimeChunkNodeProps extends BaseNodeProps {
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
interface CalendarTimeChunkNodeProps extends BaseNodeProps {
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
interface SchoolNodeProps extends BaseNodeProps {
|
||||
school_name: string;
|
||||
school_website: string;
|
||||
}
|
||||
|
||||
interface DepartmentNodeProps extends BaseNodeProps {
|
||||
department_name: string;
|
||||
}
|
||||
|
||||
interface RoomNodeProps extends BaseNodeProps {
|
||||
room_name: string;
|
||||
room_code: string;
|
||||
}
|
||||
|
||||
interface SubjectClassNodeProps extends BaseNodeProps {
|
||||
subject_class_code: string;
|
||||
year_group: string;
|
||||
subject: string;
|
||||
}
|
||||
|
||||
interface PastoralStructureNodeProps extends BaseNodeProps {
|
||||
}
|
||||
|
||||
interface YearGroupNodeProps extends BaseNodeProps {
|
||||
year_group: string;
|
||||
}
|
||||
|
||||
interface CurriculumStructureNodeProps extends BaseNodeProps {
|
||||
}
|
||||
|
||||
interface KeyStageNodeProps extends BaseNodeProps {
|
||||
key_stage: string;
|
||||
}
|
||||
|
||||
interface KeyStageSyllabusNodeProps extends BaseNodeProps {
|
||||
ks_syllabus_id: string;
|
||||
ks_syllabus_subject: string;
|
||||
}
|
||||
|
||||
interface YearGroupSyllabusNodeProps extends BaseNodeProps {
|
||||
yr_syllabus_id: string;
|
||||
yr_syllabus_subject: string;
|
||||
}
|
||||
|
||||
interface SubjectNodeProps extends BaseNodeProps {
|
||||
subject_name: string;
|
||||
subject_code: string;
|
||||
}
|
||||
|
||||
interface TopicNodeProps extends BaseNodeProps {
|
||||
topic_title: string;
|
||||
topic_id: string;
|
||||
total_number_of_lessons_for_topic: string;
|
||||
topic_type: string;
|
||||
topic_assessment_type: string;
|
||||
}
|
||||
|
||||
interface TopicLessonNodeProps extends BaseNodeProps {
|
||||
topic_lesson_title: string;
|
||||
topic_lesson_id: string;
|
||||
topic_lesson_type: string;
|
||||
topic_lesson_length: string;
|
||||
topic_lesson_suggested_activities: string;
|
||||
topic_lesson_skills_learned: string;
|
||||
topic_lesson_weblinks: string;
|
||||
}
|
||||
|
||||
interface LearningStatementNodeProps extends BaseNodeProps {
|
||||
lesson_learning_statement: string;
|
||||
lesson_learning_statement_id: string;
|
||||
lesson_learning_statement_type: string;
|
||||
}
|
||||
|
||||
interface ScienceLabNodeProps extends BaseNodeProps {
|
||||
science_lab_title: string;
|
||||
science_lab_id: string;
|
||||
science_lab_summary: string;
|
||||
science_lab_requirements: string;
|
||||
science_lab_procedure: string;
|
||||
science_lab_safety: string;
|
||||
science_lab_weblinks: string;
|
||||
}
|
||||
|
||||
interface SchoolTimetableNodeProps extends BaseNodeProps {
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
interface AcademicYearNodeProps extends BaseNodeProps {
|
||||
year: string;
|
||||
}
|
||||
|
||||
interface AcademicTermNodeProps extends BaseNodeProps {
|
||||
term_name: string;
|
||||
term_number: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
}
|
||||
|
||||
interface AcademicWeekNodeProps extends BaseNodeProps {
|
||||
start_date: string;
|
||||
week_type: string;
|
||||
academic_week_number: string;
|
||||
}
|
||||
|
||||
interface AcademicDayNodeProps extends BaseNodeProps {
|
||||
day_of_week: string;
|
||||
day_type: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
interface AcademicPeriodNodeProps extends BaseNodeProps {
|
||||
name: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
period_code: string;
|
||||
}
|
||||
|
||||
interface RegistrationPeriodNodeProps extends BaseNodeProps {
|
||||
name: string;
|
||||
date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
period_code: string;
|
||||
}
|
||||
|
||||
interface TeacherTimetableNodeProps extends BaseNodeProps {
|
||||
}
|
||||
|
||||
interface TimetableLessonNodeProps extends BaseNodeProps {
|
||||
subject_class: string;
|
||||
date: string;
|
||||
period_code: string;
|
||||
}
|
||||
|
||||
interface PlannedLessonNodeProps extends BaseNodeProps {
|
||||
subject_class: string;
|
||||
date: string;
|
||||
period_code: string;
|
||||
planning_status: string;
|
||||
teacher_code: string;
|
||||
year_group: string;
|
||||
subject: string;
|
||||
}
|
||||
|
||||
const DefaultNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => (
|
||||
<>
|
||||
<div className="w-full flex justify-center" style={{ marginBottom: '5px' }}>
|
||||
<div
|
||||
className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center"
|
||||
style={{ color: 'white', fontWeight: 'bold' }}
|
||||
>
|
||||
{(shape.props.__primarylabel__).toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
// Users
|
||||
const UserNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as UserNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>User Name: {props.user_name}</div>
|
||||
<div>User Email: {props.user_email}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const DeveloperNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as UserNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>User Name: {props.user_name}</div>
|
||||
<div>Use Email: {props.user_email}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TeacherNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as TeacherNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Teacher Name: {props.teacher_name_formal}</div>
|
||||
<div>Teacher Code: {props.teacher_code}</div>
|
||||
<div>Email: {props.teacher_email}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const StudentNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as StudentNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Student Name: {props.student_name_formal}</div>
|
||||
<div>Student Code: {props.student_code}</div>
|
||||
<div>Email: {props.student_email}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Calendar
|
||||
const CalendarNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CalendarNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Calendar Name: {props.name}</div>
|
||||
<div>Start Date: {props.start_date}</div>
|
||||
<div>End Date: {props.end_date}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CalendarYearNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CalendarYearNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Year: {props.year}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CalendarMonthNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CalendarMonthNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Month: {props.month_name}</div>
|
||||
<div>Year: {props.year}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CalendarWeekNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CalendarWeekNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Week Start Date: {props.start_date}</div>
|
||||
<div>ISO Week: {props.iso_week}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CalendarDayNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CalendarDayNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Day of Week: {props.day_of_week}</div>
|
||||
<div>Date: {props.date}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CalendarTimeChunkNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CalendarTimeChunkNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Start Time: {props.start_time}</div>
|
||||
<div>End Time: {props.end_time}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Schools
|
||||
const SchoolNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as SchoolNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>School Name: {props.school_name}</div>
|
||||
<div>School Website: {props.school_website}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const DepartmentNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as DepartmentNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Department Name: {props.department_name}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RoomNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as RoomNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Room Name: {props.room_name}</div>
|
||||
<div>Room Code: {props.room_code}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SubjectClassNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as SubjectClassNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Subject Class: {props.subject_class_code}</div>
|
||||
<div>Year Group: {props.year_group}</div>
|
||||
<div>Subject: {props.subject}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Curriculum
|
||||
const PastoralStructureNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as PastoralStructureNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const YearGroupNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as YearGroupNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Year Group: {props.year_group}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CurriculumStructureNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as CurriculumStructureNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const KeyStageNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as KeyStageNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Key Stage: {props.key_stage}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const KeyStageSyllabusNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as KeyStageSyllabusNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Syllabus ID: {props.ks_syllabus_id}</div>
|
||||
<div>Subject: {props.ks_syllabus_subject}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const YearGroupSyllabusNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as YearGroupSyllabusNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Syllabus ID: {props.yr_syllabus_id}</div>
|
||||
<div>Subject: {props.yr_syllabus_subject}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SubjectNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as SubjectNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Subject: {props.subject_name}</div>
|
||||
<div>Code: {props.subject_code}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TopicNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as TopicNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Title: {props.topic_title}</div>
|
||||
<div>ID: {props.topic_id}</div>
|
||||
<div>Lessons: {props.total_number_of_lessons_for_topic}</div>
|
||||
<div>Type: {props.topic_type}</div>
|
||||
<div>Assessment Type: {props.topic_assessment_type}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TopicLessonNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as TopicLessonNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Title: {props.topic_lesson_title}</div>
|
||||
<div>ID: {props.topic_lesson_id}</div>
|
||||
<div>Type: {props.topic_lesson_type}</div>
|
||||
<div>Length: {props.topic_lesson_length}</div>
|
||||
<div>Suggested Activities: {props.topic_lesson_suggested_activities}</div>
|
||||
<div>Skills Learned: {props.topic_lesson_skills_learned}</div>
|
||||
<div>Web Links: {props.topic_lesson_weblinks}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const LearningStatementNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as LearningStatementNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Learning Statement: {props.lesson_learning_statement}</div>
|
||||
<div>ID: {props.lesson_learning_statement_id}</div>
|
||||
<div>Type: {props.lesson_learning_statement_type}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const ScienceLabNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as ScienceLabNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Title: {props.science_lab_title}</div>
|
||||
<div>ID: {props.science_lab_id}</div>
|
||||
<div>Summary: {props.science_lab_summary}</div>
|
||||
<div>Requirements: {props.science_lab_requirements}</div>
|
||||
<div>Procedure: {props.science_lab_procedure}</div>
|
||||
<div>Safety: {props.science_lab_safety}</div>
|
||||
<div>Web Links: {props.science_lab_weblinks}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// School Timetable
|
||||
const SchoolTimetableNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as SchoolTimetableNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Start Date: {props.start_date}</div>
|
||||
<div>End Date: {props.end_date}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AcademicYearNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as AcademicYearNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Year: {props.year}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AcademicTermNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as AcademicTermNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Term Name: {props.term_name}</div>
|
||||
<div>Term Number: {props.term_number}</div>
|
||||
<div>Start Date: {props.start_date}</div>
|
||||
<div>End Date: {props.end_date}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AcademicWeekNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as AcademicWeekNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Start Date: {props.start_date}</div>
|
||||
<div>Week Type: {props.week_type}</div>
|
||||
<div>Academic Week Number: {props.academic_week_number}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AcademicDayNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as AcademicDayNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Day of Week: {props.day_of_week}</div>
|
||||
<div>Day Type: {props.day_type}</div>
|
||||
<div>Date: {props.date}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AcademicPeriodNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as AcademicPeriodNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Name: {props.name}</div>
|
||||
<div>Date: {props.date}</div>
|
||||
<div>Start Time: {props.start_time}</div>
|
||||
<div>End Time: {props.end_time}</div>
|
||||
<div>Period Code: {props.period_code}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const RegistrationPeriodNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as RegistrationPeriodNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Name: {props.name}</div>
|
||||
<div>Date: {props.date}</div>
|
||||
<div>Start Time: {props.start_time}</div>
|
||||
<div>End Time: {props.end_time}</div>
|
||||
<div>Period Code: {props.period_code}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Teacher Timetable
|
||||
const TeacherTimetableNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as TeacherTimetableNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TimetableLessonNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as TimetableLessonNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Subject Class: {props.subject_class}</div>
|
||||
<div>Date: {props.date}</div>
|
||||
<div>Period Code: {props.period_code}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const PlannedLessonNodeComponent: React.FC<NodeComponentProps> = ({ shape, theme }) => {
|
||||
const props = shape.props as PlannedLessonNodeProps;
|
||||
return (
|
||||
<>
|
||||
<DefaultNodeComponent shape={shape} theme={theme} />
|
||||
<div>Subject Class: {props.subject_class}</div>
|
||||
<div>Year Group: {props.year_group}</div>
|
||||
<div>Subject: {props.subject}</div>
|
||||
<div>Teacher Code: {props.teacher_code}</div>
|
||||
<div>Planning Status: {props.planning_status}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const nodeComponents: { [key: string]: React.FC<NodeComponentProps> } = {
|
||||
user_node: UserNodeComponent,
|
||||
teacher_node: TeacherNodeComponent,
|
||||
student_node: StudentNodeComponent,
|
||||
timetable_lesson_node: TimetableLessonNodeComponent,
|
||||
developer_node: DeveloperNodeComponent,
|
||||
school_node: SchoolNodeComponent,
|
||||
department_node: DepartmentNodeComponent,
|
||||
planned_lesson_node: PlannedLessonNodeComponent,
|
||||
registration_period_node: RegistrationPeriodNodeComponent,
|
||||
teacher_timetable_node: TeacherTimetableNodeComponent,
|
||||
academic_year_node: AcademicYearNodeComponent,
|
||||
academic_term_node: AcademicTermNodeComponent,
|
||||
academic_week_node: AcademicWeekNodeComponent,
|
||||
academic_day_node: AcademicDayNodeComponent,
|
||||
academic_period_node: AcademicPeriodNodeComponent,
|
||||
key_stage_node: KeyStageNodeComponent,
|
||||
key_stage_syllabus_node: KeyStageSyllabusNodeComponent,
|
||||
year_group_syllabus_node: YearGroupSyllabusNodeComponent,
|
||||
subject_node: SubjectNodeComponent,
|
||||
topic_node: TopicNodeComponent,
|
||||
topic_lesson_node: TopicLessonNodeComponent,
|
||||
learning_statement_node: LearningStatementNodeComponent,
|
||||
science_lab_node: ScienceLabNodeComponent,
|
||||
school_timetable_node: SchoolTimetableNodeComponent,
|
||||
calendar_node: CalendarNodeComponent,
|
||||
calendar_year_node: CalendarYearNodeComponent,
|
||||
calendar_month_node: CalendarMonthNodeComponent,
|
||||
calendar_week_node: CalendarWeekNodeComponent,
|
||||
calendar_day_node: CalendarDayNodeComponent,
|
||||
calendar_time_chunk_node: CalendarTimeChunkNodeComponent,
|
||||
year_group_node: YearGroupNodeComponent,
|
||||
pastoral_structure_node: PastoralStructureNodeComponent,
|
||||
curriculum_structure_node: CurriculumStructureNodeComponent,
|
||||
room_node: RoomNodeComponent,
|
||||
subject_class_node: SubjectClassNodeComponent,
|
||||
};
|
||||
|
||||
export const getNodeComponent = (shape: AllNodeShapes, theme: any) => {
|
||||
const Component = nodeComponents[shape.type] || DefaultNodeComponent;
|
||||
return <Component shape={shape} theme={theme} />;
|
||||
};
|
||||
225
src/utils/userContext.tsx
Normal file
225
src/utils/userContext.tsx
Normal file
@ -0,0 +1,225 @@
|
||||
import { ReactNode, createContext, useContext, useState, useEffect } from 'react';
|
||||
import { UserNodeInterface } from '../types/graph_node_types';
|
||||
import { TLUser } from 'tldraw';
|
||||
|
||||
export const getFromLocalStorage = (key: string) => {
|
||||
const item = localStorage.getItem(key);
|
||||
if (item === null || item === 'undefined') return null;
|
||||
try {
|
||||
return JSON.parse(item);
|
||||
} catch (error) {
|
||||
console.error(`Error parsing JSON for key ${key}:`, error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const setToLocalStorage = (key: string, value: any) => {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
export const removeFromLocalStorage = (key: string) => {
|
||||
localStorage.removeItem(key);
|
||||
};
|
||||
|
||||
export const setStateAndStorage = (setter: React.Dispatch<React.SetStateAction<any>>, key: string, value: any) => {
|
||||
setter(value);
|
||||
setToLocalStorage(key, value);
|
||||
};
|
||||
|
||||
const AuthContext = createContext({
|
||||
userRole: null as string | null,
|
||||
firebaseIdToken: null as string | null,
|
||||
msAccessToken: null as string | null,
|
||||
neo4jDbName: null as string | null,
|
||||
userNode: null as UserNodeInterface | null,
|
||||
tldrawUserFilePath: null as string | null,
|
||||
oneNoteNotebook: null as any,
|
||||
isLoading: false,
|
||||
error: null as string | null,
|
||||
logout: async () => {},
|
||||
getIdToken: async () => Promise.resolve('') as Promise<string>,
|
||||
});
|
||||
|
||||
export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [user, setUser] = useState<TLUser | null>(getFromLocalStorage('user'));
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [userRole, setUserRole] = useState<string | null>(getFromLocalStorage('userRole'));
|
||||
const [firebaseIdToken, setToken] = useState<string | null>(getFromLocalStorage('firebaseIdToken'));
|
||||
const [msAccessToken, setMsToken] = useState<string | null>(getFromLocalStorage('msAccessToken'));
|
||||
const [neo4jDbName, setNeo4jDbName] = useState<string | null>(getFromLocalStorage('neo4jDbName'));
|
||||
const [userNode, setUserNode] = useState<UserNodeInterface | null>(getFromLocalStorage('userNode'));
|
||||
const [tldrawUserFilePath, setTldrawUserFilePath] = useState<string | null>(getFromLocalStorage('tldrawUserFilePath'));
|
||||
const [oneNoteNotebook, setOneNoteNotebook] = useState<any>(getFromLocalStorage('oneNoteNotebook'));
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = auth.onAuthStateChanged(async (user) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (user) {
|
||||
console.log("AuthProvider - useEffect - User state changed:", user);
|
||||
const storedUserRole = getFromLocalStorage('userRole');
|
||||
const storedFirebaseIdToken = await user.getIdToken();
|
||||
const storedMsAccessToken = getFromLocalStorage('msAccessToken');
|
||||
const storedNeo4jDbName = getFromLocalStorage('neo4jDbName');
|
||||
const storedUserNode = getFromLocalStorage('userNode');
|
||||
const storedOneNoteNotebook = getFromLocalStorage('oneNoteNotebook');
|
||||
|
||||
setStateAndStorage(setUser, 'user', user);
|
||||
setStateAndStorage(setUserRole, 'userRole', storedUserRole);
|
||||
setStateAndStorage(setToken, 'firebaseIdToken', storedFirebaseIdToken);
|
||||
setStateAndStorage(setMsToken, 'msAccessToken', storedMsAccessToken);
|
||||
setStateAndStorage(setNeo4jDbName, 'neo4jDbName', storedNeo4jDbName);
|
||||
setStateAndStorage(setUserNode, 'userNode', storedUserNode);
|
||||
setStateAndStorage(setTldrawUserFilePath, 'tldrawUserFilePath', storedUserNode?.path);
|
||||
setStateAndStorage(setOneNoteNotebook, 'oneNoteNotebook', storedOneNoteNotebook);
|
||||
|
||||
console.log("AuthProvider - useEffect - Updated state:", { user, userRole: storedUserRole });
|
||||
} else {
|
||||
setStateAndStorage(setUser, 'user', null);
|
||||
setStateAndStorage(setUserRole, 'userRole', null);
|
||||
setStateAndStorage(setToken, 'firebaseIdToken', null);
|
||||
setStateAndStorage(setMsToken, 'msAccessToken', null);
|
||||
setStateAndStorage(setNeo4jDbName, 'neo4jDbName', null);
|
||||
setStateAndStorage(setUserNode, 'userNode', null);
|
||||
setStateAndStorage(setTldrawUserFilePath, 'tldrawUserFilePath', null);
|
||||
setStateAndStorage(setOneNoteNotebook, 'oneNoteNotebook', null);
|
||||
|
||||
removeFromLocalStorage('userRole');
|
||||
removeFromLocalStorage('msAccessToken');
|
||||
removeFromLocalStorage('neo4jDbName');
|
||||
removeFromLocalStorage('userNode');
|
||||
removeFromLocalStorage('oneNoteNotebook');
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error instanceof Error ? error.message : 'An unknown error occurred');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => unsubscribe();
|
||||
}, []);
|
||||
|
||||
const login = async (email: string, password: string, role: string) => {
|
||||
try {
|
||||
const { user, firebaseIdToken, firestoreUserDoc } = await emailLogin(email, password);
|
||||
const userRole = role || firestoreUserDoc?.userRole || null;
|
||||
const userNode = await fetchUserNode(user.uid);
|
||||
|
||||
setStateAndStorage(setUser, 'user', user);
|
||||
setStateAndStorage(setUserRole, 'userRole', userRole);
|
||||
setStateAndStorage(setToken, 'firebaseIdToken', firebaseIdToken);
|
||||
setStateAndStorage(setUserNode, 'userNode', userNode);
|
||||
setStateAndStorage(setTldrawUserFilePath, 'tldrawUserFilePath', userNode.path);
|
||||
return { user, firebaseIdToken, userNode, userRole, message: 'Login successful' };
|
||||
} catch (error) {
|
||||
setError(error instanceof Error ? error.message : 'An unknown error occurred');
|
||||
return { user: null, firebaseIdToken: null, userNode: null, userRole: null, message: 'Login failed' };
|
||||
}
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
console.log("Logging out...");
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await logoutUser();
|
||||
setStateAndStorage(setUser, 'user', null);
|
||||
setStateAndStorage(setUserRole, 'userRole', null);
|
||||
setStateAndStorage(setToken, 'firebaseIdToken', null);
|
||||
setStateAndStorage(setMsToken, 'msAccessToken', null);
|
||||
setStateAndStorage(setNeo4jDbName, 'neo4jDbName', null);
|
||||
setStateAndStorage(setUserNode, 'userNode', null);
|
||||
setStateAndStorage(setTldrawUserFilePath, 'tldrawUserFilePath', null);
|
||||
setStateAndStorage(setOneNoteNotebook, 'oneNoteNotebook', null);
|
||||
removeFromLocalStorage('user');
|
||||
removeFromLocalStorage('firebaseIdToken');
|
||||
removeFromLocalStorage('msAccessToken');
|
||||
removeFromLocalStorage('neo4jDbName');
|
||||
removeFromLocalStorage('userNode');
|
||||
removeFromLocalStorage('tldrawUserFilePath');
|
||||
removeFromLocalStorage('oneNoteNotebook');
|
||||
} catch (error) {
|
||||
setError(error instanceof Error ? error.message : 'An unknown error occurred');
|
||||
} finally {
|
||||
console.log("Finished logging out");
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const registerUser = async (email: string, password: string, username: string, userRole: string): Promise<{ user: User | null; firebaseIdToken: string | null; userNode: UserNodeInterface | null; userRole: string | null; message: string | null }> => {
|
||||
try {
|
||||
const { user, firebaseIdToken, neo4jDbName, userNode, tldrawUserFilePath } = await registerUserWithEmailAndPassword(email, password, username, userRole);
|
||||
setStateAndStorage(setUser, 'user', user);
|
||||
setStateAndStorage(setUserRole, 'userRole', userRole);
|
||||
setStateAndStorage(setToken, 'firebaseIdToken', firebaseIdToken);
|
||||
setStateAndStorage(setNeo4jDbName, 'neo4jDbName', neo4jDbName);
|
||||
setStateAndStorage(setUserNode, 'userNode', userNode);
|
||||
setStateAndStorage(setTldrawUserFilePath, 'tldrawUserFilePath', userNode.path);
|
||||
return { user, firebaseIdToken, userNode, userRole, message: 'Email user registered successfully' };
|
||||
} catch (error) {
|
||||
return { user: null, firebaseIdToken: null, userNode: null, userRole: null, message: error instanceof Error ? error.message : 'An unknown error occurred' };
|
||||
}
|
||||
};
|
||||
|
||||
const registerOrLoginWithMicrosoft = async (role: string): Promise<{ user: User | null; firebaseIdToken: string | null; msAccessToken: string | null; userNode: UserNodeInterface | null; userRole: string | null; message: string | null; oneNoteNotebook: any }> => {
|
||||
try {
|
||||
console.log("registerOrLoginWithMicrosoft - Starting");
|
||||
console.log(`Ready to sign in with Microsoft as ${role}`);
|
||||
const { user, firebaseIdToken, msAccessToken, neo4jDbName, userNode, oneNoteNotebook, isNewUser } = await signInWithMicrosoft(role);
|
||||
setStateAndStorage(setUser, 'user', user || null);
|
||||
setStateAndStorage(setUserRole, 'userRole', role);
|
||||
setStateAndStorage(setToken, 'firebaseIdToken', firebaseIdToken || null);
|
||||
setStateAndStorage(setMsToken, 'msAccessToken', msAccessToken || null);
|
||||
setStateAndStorage(setNeo4jDbName, 'neo4jDbName', neo4jDbName || null);
|
||||
setStateAndStorage(setUserNode, 'userNode', userNode || null);
|
||||
setStateAndStorage(setTldrawUserFilePath, 'tldrawUserFilePath', userNode?.path || null);
|
||||
setStateAndStorage(setOneNoteNotebook, 'oneNoteNotebook', oneNoteNotebook || null);
|
||||
|
||||
console.log(`Signed in with Microsoft as ${role}`);
|
||||
console.log("OneNote Notebook:", oneNoteNotebook);
|
||||
setIsLoading(false);
|
||||
return { user, userRole: role, firebaseIdToken, msAccessToken, userNode, oneNoteNotebook, message: isNewUser ? 'User registered successfully with Microsoft' : 'User logged in successfully with Microsoft' };
|
||||
} catch (error) {
|
||||
setError(error instanceof Error ? error.message : 'An unknown error occurred');
|
||||
setIsLoading(false);
|
||||
return { user: null, userRole: null, firebaseIdToken: null, msAccessToken: null, userNode: null, oneNoteNotebook: null, message: 'Failed to sign in with Microsoft' };
|
||||
}
|
||||
};
|
||||
|
||||
const getIdToken = async () => {
|
||||
if (user) {
|
||||
return await user.getIdToken(true);
|
||||
}
|
||||
throw new Error('User not authenticated');
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
user,
|
||||
userRole,
|
||||
firebaseIdToken,
|
||||
msAccessToken,
|
||||
userNode,
|
||||
tldrawUserFilePath,
|
||||
oneNoteNotebook,
|
||||
login,
|
||||
logout,
|
||||
registerUser,
|
||||
registerOrLoginWithMicrosoft,
|
||||
error,
|
||||
isLoading,
|
||||
neo4jDbName,
|
||||
clearError: () => setError(null),
|
||||
getIdToken,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAuth = () => useContext(AuthContext);
|
||||
35
tsconfig.base.json
Normal file
35
tsconfig.base.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Default",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["dom", "DOM.Iterable", "esnext"],
|
||||
"experimentalDecorators": true,
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true,
|
||||
"useDefineForClassFields": true,
|
||||
"noImplicitOverride": true,
|
||||
"types": ["node", "@types/jest"]
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"include": ["src", "scripts", "vite.config.mts"],
|
||||
"exclude": ["node_modules", "dist", ".tsbuild*"],
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"types": ["bun-types", "node"],
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
16
vite.config.mts
Normal file
16
vite.config.mts
Normal file
@ -0,0 +1,16 @@
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import path from 'path'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [react({ tsDecorators: true })],
|
||||
root: path.join(__dirname, 'src/client'),
|
||||
publicDir: path.join(__dirname, 'public'),
|
||||
server: {
|
||||
port: 5000,
|
||||
host: '0.0.0.0', // Expose the server to the network
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['assets'],
|
||||
},
|
||||
}))
|
||||
Loading…
x
Reference in New Issue
Block a user