feat(frontend): route migration - add /admin/login, /admin/students, /admin/live/[slotId], rooms/[roomId]
This commit is contained in:
22
frontend/.svelte-kit/ambient.d.ts
vendored
22
frontend/.svelte-kit/ambient.d.ts
vendored
@@ -40,6 +40,7 @@
|
||||
declare module '$env/static/private' {
|
||||
export const SHELL: string;
|
||||
export const npm_command: string;
|
||||
export const COREPACK_ENABLE_AUTO_PIN: string;
|
||||
export const UV_CACHE_DIR: string;
|
||||
export const npm_config_userconfig: string;
|
||||
export const COLORTERM: string;
|
||||
@@ -81,6 +82,8 @@ declare module '$env/static/private' {
|
||||
export const SYSTEMD_EXEC_PID: string;
|
||||
export const _: string;
|
||||
export const KITTY_PUBLIC_KEY: string;
|
||||
export const NoDefaultCurrentDirectoryInExePath: string;
|
||||
export const CLAUDECODE: string;
|
||||
export const MOTD_SHOWN: string;
|
||||
export const HOME: string;
|
||||
export const LC_PAPER: string;
|
||||
@@ -111,7 +114,6 @@ declare module '$env/static/private' {
|
||||
export const npm_config_prefix: string;
|
||||
export const ZDOTDIR: string;
|
||||
export const USER: string;
|
||||
export const GIT_PAGER: string;
|
||||
export const SDL_VIDEODRIVER: string;
|
||||
export const HYPRLAND_INSTANCE_SIGNATURE: string;
|
||||
export const MANPAGER: string;
|
||||
@@ -122,6 +124,7 @@ declare module '$env/static/private' {
|
||||
export const LESS_TERMCAP_ue: string;
|
||||
export const MOZ_ENABLE_WAYLAND: string;
|
||||
export const LESS_TERMCAP_us: string;
|
||||
export const GIT_EDITOR: string;
|
||||
export const PAGER: string;
|
||||
export const LC_TELEPHONE: string;
|
||||
export const ANDROID_SDK_ROOT: string;
|
||||
@@ -132,24 +135,26 @@ declare module '$env/static/private' {
|
||||
export const MANAGERPIDFDID: string;
|
||||
export const npm_config_user_agent: string;
|
||||
export const ROCM_PATH: string;
|
||||
export const OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: string;
|
||||
export const XDG_STATE_HOME: string;
|
||||
export const npm_execpath: string;
|
||||
export const LD_LIBRARY_PATH: string;
|
||||
export const DISABLE_AUTOUPDATER: string;
|
||||
export const LC_CTYPE: string;
|
||||
export const XDG_RUNTIME_DIR: string;
|
||||
export const KITTY_LISTEN_ON: string;
|
||||
export const GRIMBLAST_EDITOR: string;
|
||||
export const GEMINI_CLI: string;
|
||||
export const CLAUDE_CODE_ENTRYPOINT: string;
|
||||
export const DEBUGINFOD_URLS: string;
|
||||
export const npm_package_json: string;
|
||||
export const LC_TIME: string;
|
||||
export const HYPRCURSOR_THEME: string;
|
||||
export const GEMINI_CLI_NO_RELAUNCH: string;
|
||||
export const JOURNAL_STREAM: string;
|
||||
export const LC_COLLATE: string;
|
||||
export const XCURSOR_THEME: string;
|
||||
export const XDG_DATA_DIRS: string;
|
||||
export const GDK_BACKEND: string;
|
||||
export const CLAUDE_CODE_EXECPATH: string;
|
||||
export const npm_config_noproxy: string;
|
||||
export const PATH: string;
|
||||
export const npm_config_node_gyp: string;
|
||||
@@ -254,6 +259,7 @@ declare module '$env/dynamic/private' {
|
||||
export const env: {
|
||||
SHELL: string;
|
||||
npm_command: string;
|
||||
COREPACK_ENABLE_AUTO_PIN: string;
|
||||
UV_CACHE_DIR: string;
|
||||
npm_config_userconfig: string;
|
||||
COLORTERM: string;
|
||||
@@ -295,6 +301,8 @@ declare module '$env/dynamic/private' {
|
||||
SYSTEMD_EXEC_PID: string;
|
||||
_: string;
|
||||
KITTY_PUBLIC_KEY: string;
|
||||
NoDefaultCurrentDirectoryInExePath: string;
|
||||
CLAUDECODE: string;
|
||||
MOTD_SHOWN: string;
|
||||
HOME: string;
|
||||
LC_PAPER: string;
|
||||
@@ -325,7 +333,6 @@ declare module '$env/dynamic/private' {
|
||||
npm_config_prefix: string;
|
||||
ZDOTDIR: string;
|
||||
USER: string;
|
||||
GIT_PAGER: string;
|
||||
SDL_VIDEODRIVER: string;
|
||||
HYPRLAND_INSTANCE_SIGNATURE: string;
|
||||
MANPAGER: string;
|
||||
@@ -336,6 +343,7 @@ declare module '$env/dynamic/private' {
|
||||
LESS_TERMCAP_ue: string;
|
||||
MOZ_ENABLE_WAYLAND: string;
|
||||
LESS_TERMCAP_us: string;
|
||||
GIT_EDITOR: string;
|
||||
PAGER: string;
|
||||
LC_TELEPHONE: string;
|
||||
ANDROID_SDK_ROOT: string;
|
||||
@@ -346,24 +354,26 @@ declare module '$env/dynamic/private' {
|
||||
MANAGERPIDFDID: string;
|
||||
npm_config_user_agent: string;
|
||||
ROCM_PATH: string;
|
||||
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: string;
|
||||
XDG_STATE_HOME: string;
|
||||
npm_execpath: string;
|
||||
LD_LIBRARY_PATH: string;
|
||||
DISABLE_AUTOUPDATER: string;
|
||||
LC_CTYPE: string;
|
||||
XDG_RUNTIME_DIR: string;
|
||||
KITTY_LISTEN_ON: string;
|
||||
GRIMBLAST_EDITOR: string;
|
||||
GEMINI_CLI: string;
|
||||
CLAUDE_CODE_ENTRYPOINT: string;
|
||||
DEBUGINFOD_URLS: string;
|
||||
npm_package_json: string;
|
||||
LC_TIME: string;
|
||||
HYPRCURSOR_THEME: string;
|
||||
GEMINI_CLI_NO_RELAUNCH: string;
|
||||
JOURNAL_STREAM: string;
|
||||
LC_COLLATE: string;
|
||||
XCURSOR_THEME: string;
|
||||
XDG_DATA_DIRS: string;
|
||||
GDK_BACKEND: string;
|
||||
CLAUDE_CODE_EXECPATH: string;
|
||||
npm_config_noproxy: string;
|
||||
PATH: string;
|
||||
npm_config_node_gyp: string;
|
||||
|
||||
@@ -13,7 +13,11 @@ export const nodes = [
|
||||
() => import('./nodes/9'),
|
||||
() => import('./nodes/10'),
|
||||
() => import('./nodes/11'),
|
||||
() => import('./nodes/12')
|
||||
() => import('./nodes/12'),
|
||||
() => import('./nodes/13'),
|
||||
() => import('./nodes/14'),
|
||||
() => import('./nodes/15'),
|
||||
() => import('./nodes/16')
|
||||
];
|
||||
|
||||
export const server_loads = [];
|
||||
@@ -24,11 +28,15 @@ export const dictionary = {
|
||||
"/admin/attendance": [5,[2]],
|
||||
"/admin/courses": [6,[2]],
|
||||
"/admin/export": [7,[2]],
|
||||
"/admin/notes": [8,[2]],
|
||||
"/admin/rooms": [9,[2]],
|
||||
"/admin/sessions": [10,[2]],
|
||||
"/login": [11],
|
||||
"/s/[code]": [12]
|
||||
"/admin/live/[slotId]": [8,[2]],
|
||||
"/admin/login": [9,[2]],
|
||||
"/admin/notes": [10,[2]],
|
||||
"/admin/rooms": [11,[2]],
|
||||
"/admin/rooms/[roomId]": [12,[2]],
|
||||
"/admin/sessions": [13,[2]],
|
||||
"/admin/students": [14,[2]],
|
||||
"/login": [15],
|
||||
"/s/[code]": [16]
|
||||
};
|
||||
|
||||
export const hooks = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default as component } from "../../../../src/routes/admin/sessions/+page.svelte";
|
||||
export { default as component } from "../../../../src/routes/admin/notes/+page.svelte";
|
||||
@@ -1 +1 @@
|
||||
export { default as component } from "../../../../src/routes/login/+page.svelte";
|
||||
export { default as component } from "../../../../src/routes/admin/rooms/+page.svelte";
|
||||
@@ -1 +1 @@
|
||||
export { default as component } from "../../../../src/routes/s/[code]/+page.svelte";
|
||||
export { default as component } from "../../../../src/routes/admin/rooms/[roomId]/+page.svelte";
|
||||
1
frontend/.svelte-kit/generated/client/nodes/13.js
Normal file
1
frontend/.svelte-kit/generated/client/nodes/13.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as component } from "../../../../src/routes/admin/sessions/+page.svelte";
|
||||
1
frontend/.svelte-kit/generated/client/nodes/14.js
Normal file
1
frontend/.svelte-kit/generated/client/nodes/14.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as component } from "../../../../src/routes/admin/students/+page.svelte";
|
||||
1
frontend/.svelte-kit/generated/client/nodes/15.js
Normal file
1
frontend/.svelte-kit/generated/client/nodes/15.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as component } from "../../../../src/routes/login/+page.svelte";
|
||||
1
frontend/.svelte-kit/generated/client/nodes/16.js
Normal file
1
frontend/.svelte-kit/generated/client/nodes/16.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as component } from "../../../../src/routes/s/[code]/+page.svelte";
|
||||
@@ -1 +1 @@
|
||||
export { default as component } from "../../../../src/routes/admin/notes/+page.svelte";
|
||||
export { default as component } from "../../../../src/routes/admin/live/[slotId]/+page.svelte";
|
||||
@@ -1 +1 @@
|
||||
export { default as component } from "../../../../src/routes/admin/rooms/+page.svelte";
|
||||
export { default as component } from "../../../../src/routes/admin/login/+page.svelte";
|
||||
@@ -22,10 +22,10 @@ export const options = {
|
||||
service_worker_options: undefined,
|
||||
server_error_boundaries: false,
|
||||
templates: {
|
||||
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
||||
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n\t\t<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n\t\t<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,500;0,8..60,600;0,8..60,700;1,8..60,400&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600&family=Caveat:wght@400;500;600&display=swap\">\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
||||
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
|
||||
},
|
||||
version_hash: "eq7x7d"
|
||||
version_hash: "2cu8qm"
|
||||
};
|
||||
|
||||
export async function get_hooks() {
|
||||
|
||||
17
frontend/.svelte-kit/non-ambient.d.ts
vendored
17
frontend/.svelte-kit/non-ambient.d.ts
vendored
@@ -29,24 +29,31 @@ declare module "$app/types" {
|
||||
type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;
|
||||
|
||||
export interface AppTypes {
|
||||
RouteId(): "/" | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/notes" | "/admin/rooms" | "/admin/sessions" | "/login" | "/s" | "/s/[code]";
|
||||
RouteId(): "/" | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/live" | "/admin/live/[slotId]" | "/admin/login" | "/admin/notes" | "/admin/rooms" | "/admin/rooms/[roomId]" | "/admin/sessions" | "/admin/students" | "/login" | "/s" | "/s/[code]";
|
||||
RouteParams(): {
|
||||
"/admin/live/[slotId]": { slotId: string };
|
||||
"/admin/rooms/[roomId]": { roomId: string };
|
||||
"/s/[code]": { code: string }
|
||||
};
|
||||
LayoutParams(): {
|
||||
"/": { code?: string };
|
||||
"/admin": Record<string, never>;
|
||||
"/": { slotId?: string; roomId?: string; code?: string };
|
||||
"/admin": { slotId?: string; roomId?: string };
|
||||
"/admin/attendance": Record<string, never>;
|
||||
"/admin/courses": Record<string, never>;
|
||||
"/admin/export": Record<string, never>;
|
||||
"/admin/live": { slotId?: string };
|
||||
"/admin/live/[slotId]": { slotId: string };
|
||||
"/admin/login": Record<string, never>;
|
||||
"/admin/notes": Record<string, never>;
|
||||
"/admin/rooms": Record<string, never>;
|
||||
"/admin/rooms": { roomId?: string };
|
||||
"/admin/rooms/[roomId]": { roomId: string };
|
||||
"/admin/sessions": Record<string, never>;
|
||||
"/admin/students": Record<string, never>;
|
||||
"/login": Record<string, never>;
|
||||
"/s": { code?: string };
|
||||
"/s/[code]": { code: string }
|
||||
};
|
||||
Pathname(): "/" | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/notes" | "/admin/rooms" | "/admin/sessions" | "/login" | `/s/${string}` & {};
|
||||
Pathname(): "/" | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | `/admin/live/${string}` & {} | "/admin/login" | "/admin/notes" | "/admin/rooms" | `/admin/rooms/${string}` & {} | "/admin/sessions" | "/admin/students" | "/login" | `/s/${string}` & {};
|
||||
ResolvedPathname(): `${"" | `/${string}`}${ReturnType<AppTypes['Pathname']>}`;
|
||||
Asset(): string & {};
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
"/admin/attendance": [],
|
||||
"/admin/courses": [],
|
||||
"/admin/export": [],
|
||||
"/admin/live/[slotId]": [],
|
||||
"/admin/login": [],
|
||||
"/admin/notes": [],
|
||||
"/admin/rooms": [],
|
||||
"/admin/rooms/[roomId]": [],
|
||||
"/admin/sessions": [],
|
||||
"/admin/students": [],
|
||||
"/login": [],
|
||||
"/s/[code]": []
|
||||
}
|
||||
@@ -11,8 +11,8 @@ type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = EnsureDefined<LayoutData>;
|
||||
type LayoutRouteId = RouteId | "/" | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/notes" | "/admin/rooms" | "/admin/sessions" | "/login" | "/s/[code]" | null
|
||||
type LayoutParams = RouteParams & { code?: string }
|
||||
type LayoutRouteId = RouteId | "/" | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/live/[slotId]" | "/admin/login" | "/admin/notes" | "/admin/rooms" | "/admin/rooms/[roomId]" | "/admin/sessions" | "/admin/students" | "/login" | "/s/[code]" | null
|
||||
type LayoutParams = RouteParams & { slotId?: string; roomId?: string; code?: string }
|
||||
type LayoutParentData = EnsureDefined<{}>;
|
||||
|
||||
export type PageServerData = null;
|
||||
|
||||
@@ -11,8 +11,8 @@ type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = Omit<EnsureDefined<import('../$types.js').LayoutData>, keyof LayoutData> & EnsureDefined<LayoutData>;
|
||||
type LayoutRouteId = RouteId | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/notes" | "/admin/rooms" | "/admin/sessions"
|
||||
type LayoutParams = RouteParams & { }
|
||||
type LayoutRouteId = RouteId | "/admin" | "/admin/attendance" | "/admin/courses" | "/admin/export" | "/admin/live/[slotId]" | "/admin/login" | "/admin/notes" | "/admin/rooms" | "/admin/rooms/[roomId]" | "/admin/sessions" | "/admin/students"
|
||||
type LayoutParams = RouteParams & { slotId?: string; roomId?: string }
|
||||
type LayoutParentData = EnsureDefined<import('../$types.js').LayoutData>;
|
||||
|
||||
export type PageServerData = null;
|
||||
|
||||
18
frontend/.svelte-kit/types/src/routes/admin/live/[slotId]/$types.d.ts
vendored
Normal file
18
frontend/.svelte-kit/types/src/routes/admin/live/[slotId]/$types.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;
|
||||
type RouteParams = { slotId: string };
|
||||
type RouteId = '/admin/live/[slotId]';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = Omit<EnsureDefined<import('../../../$types.js').LayoutData>, keyof import('../../$types.js').LayoutData> & EnsureDefined<import('../../$types.js').LayoutData>;
|
||||
|
||||
export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;
|
||||
export type PageServerData = null;
|
||||
export type PageData = Expand<PageParentData>;
|
||||
export type PageProps = { params: RouteParams; data: PageData }
|
||||
17
frontend/.svelte-kit/types/src/routes/admin/login/$types.d.ts
vendored
Normal file
17
frontend/.svelte-kit/types/src/routes/admin/login/$types.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;
|
||||
type RouteParams = { };
|
||||
type RouteId = '/admin/login';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = Omit<EnsureDefined<import('../../$types.js').LayoutData>, keyof import('../$types.js').LayoutData> & EnsureDefined<import('../$types.js').LayoutData>;
|
||||
|
||||
export type PageServerData = null;
|
||||
export type PageData = Expand<PageParentData>;
|
||||
export type PageProps = { params: RouteParams; data: PageData }
|
||||
18
frontend/.svelte-kit/types/src/routes/admin/rooms/[roomId]/$types.d.ts
vendored
Normal file
18
frontend/.svelte-kit/types/src/routes/admin/rooms/[roomId]/$types.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;
|
||||
type RouteParams = { roomId: string };
|
||||
type RouteId = '/admin/rooms/[roomId]';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = Omit<EnsureDefined<import('../../../$types.js').LayoutData>, keyof import('../../$types.js').LayoutData> & EnsureDefined<import('../../$types.js').LayoutData>;
|
||||
|
||||
export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;
|
||||
export type PageServerData = null;
|
||||
export type PageData = Expand<PageParentData>;
|
||||
export type PageProps = { params: RouteParams; data: PageData }
|
||||
17
frontend/.svelte-kit/types/src/routes/admin/students/$types.d.ts
vendored
Normal file
17
frontend/.svelte-kit/types/src/routes/admin/students/$types.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
type MatcherParam<M> = M extends (param : string) => param is (infer U extends string) ? U : string;
|
||||
type RouteParams = { };
|
||||
type RouteId = '/admin/students';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = Omit<EnsureDefined<import('../../$types.js').LayoutData>, keyof import('../$types.js').LayoutData> & EnsureDefined<import('../$types.js').LayoutData>;
|
||||
|
||||
export type PageServerData = null;
|
||||
export type PageData = Expand<PageParentData>;
|
||||
export type PageProps = { params: RouteParams; data: PageData }
|
||||
@@ -1,47 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { token } from '$lib/auth';
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { token } from '$lib/auth';
|
||||
import { goto } from '$app/navigation';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
if ($token) {
|
||||
goto('/admin');
|
||||
}
|
||||
});
|
||||
onMount(() => {
|
||||
goto($token ? '/admin' : '/admin/login');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="welcome">
|
||||
<h1>FPTutor Attendance</h1>
|
||||
<p>Efficiently tracking attendance and student observations.</p>
|
||||
|
||||
<div class="actions">
|
||||
<a href="/login" class="btn">Tutor Login</a>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>Students: Please use the link provided by your tutor during the session.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.welcome {
|
||||
max-width: 600px;
|
||||
margin: 100px auto;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 12px;
|
||||
}
|
||||
h1 { font-size: 2.5em; color: #333; margin-bottom: 10px; }
|
||||
p { color: #666; font-size: 1.2em; }
|
||||
.actions { margin-top: 40px; }
|
||||
.btn {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
padding: 12px 30px;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer { margin-top: 60px; font-size: 0.9em; color: #888; }
|
||||
</style>
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
onMount(() => {
|
||||
if (!$token) {
|
||||
goto('/login');
|
||||
goto('/admin/login');
|
||||
}
|
||||
});
|
||||
|
||||
function handleLogout() {
|
||||
logout();
|
||||
goto('/login');
|
||||
goto('/admin/login');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
10
frontend/src/routes/admin/live/[slotId]/+page.svelte
Normal file
10
frontend/src/routes/admin/live/[slotId]/+page.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
// Stub — full implementation in Phase 6
|
||||
const slotId = $derived(parseInt(($page.params as Record<string, string>).slotId));
|
||||
</script>
|
||||
|
||||
<div style="padding:28px 36px">
|
||||
<span class="eyebrow">Tutor:innen-Ansicht · Live</span>
|
||||
<h1 class="h1" style="font-family:var(--serif)">Slot {slotId}</h1>
|
||||
</div>
|
||||
82
frontend/src/routes/admin/login/+page.svelte
Normal file
82
frontend/src/routes/admin/login/+page.svelte
Normal file
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { api } from '$lib/api';
|
||||
import { token } from '$lib/auth';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
let error = '';
|
||||
let loading = false;
|
||||
|
||||
async function login() {
|
||||
loading = true;
|
||||
error = '';
|
||||
try {
|
||||
const res = await api.auth.login(email, password);
|
||||
token.set(res.token);
|
||||
goto('/admin');
|
||||
} catch (e: any) {
|
||||
error = e.message || 'Invalid credentials';
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="login-container">
|
||||
<h1>Tutor Login</h1>
|
||||
<form on:submit|preventDefault={login}>
|
||||
<div class="field">
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email" bind:value={email} required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="password">Password</label>
|
||||
<input id="password" type="password" bind:value={password} required />
|
||||
</div>
|
||||
{#if error}
|
||||
<p class="error">{error}</p>
|
||||
{/if}
|
||||
<button type="submit" disabled={loading}>
|
||||
{loading ? 'Logging in...' : 'Login'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
margin: 100px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:disabled {
|
||||
background: #ccc;
|
||||
}
|
||||
</style>
|
||||
@@ -1,205 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '$lib/api';
|
||||
import RoomCanvas from '$lib/RoomCanvas.svelte';
|
||||
import type { Room, LayoutElement } from '$lib/types';
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '$lib/api';
|
||||
import type { Room } from '$lib/types';
|
||||
|
||||
let rooms = $state<Room[]>([]);
|
||||
let selectedRoomId = $state<number | null>(null);
|
||||
let selectedRoom = $state<Room | null>(null);
|
||||
let newRoomName = $state('');
|
||||
let rooms = $state<Room[]>([]);
|
||||
let newRoomName = $state('');
|
||||
|
||||
onMount(async () => {
|
||||
await loadRooms();
|
||||
});
|
||||
onMount(async () => {
|
||||
rooms = await api.admin.rooms.list();
|
||||
});
|
||||
|
||||
async function loadRooms() {
|
||||
rooms = await api.admin.rooms.list();
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (selectedRoomId) {
|
||||
api.admin.rooms.get(selectedRoomId).then(r => selectedRoom = r);
|
||||
}
|
||||
});
|
||||
|
||||
async function createRoom() {
|
||||
const defaultLayout: LayoutElement[] = [
|
||||
{ id: 's1', label: '1', x: 2, y: 2, width: 1, height: 1, type: 'seat' }
|
||||
];
|
||||
try {
|
||||
const room = await api.admin.rooms.create(newRoomName, defaultLayout);
|
||||
newRoomName = '';
|
||||
await loadRooms();
|
||||
selectedRoomId = room.id;
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveLayout() {
|
||||
if (!selectedRoom) return;
|
||||
try {
|
||||
await api.admin.rooms.updateLayout(selectedRoom.id, selectedRoom.layout);
|
||||
alert('Layout saved');
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
}
|
||||
|
||||
function addElement(type: LayoutElement['type']) {
|
||||
if (!selectedRoom) return;
|
||||
const id = Math.random().toString(36).substr(2, 9);
|
||||
const newEl: LayoutElement = {
|
||||
id,
|
||||
label: type === 'seat' ? (selectedRoom.layout.filter(e => e.type === 'seat').length + 1).toString() : '',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: type === 'table' ? 2 : 1,
|
||||
height: 1,
|
||||
type
|
||||
};
|
||||
selectedRoom.layout = [...selectedRoom.layout, newEl];
|
||||
}
|
||||
|
||||
let selectedElementId = $state<string | null>(null);
|
||||
let selectedElement = $derived(selectedRoom?.layout.find(e => e.id === selectedElementId));
|
||||
|
||||
function deleteElement() {
|
||||
if (!selectedRoom || !selectedElementId) return;
|
||||
selectedRoom.layout = selectedRoom.layout.filter(e => e.id !== selectedElementId);
|
||||
selectedElementId = null;
|
||||
}
|
||||
async function createRoom() {
|
||||
if (!newRoomName.trim()) return;
|
||||
try {
|
||||
await api.admin.rooms.create(newRoomName, []);
|
||||
newRoomName = '';
|
||||
rooms = await api.admin.rooms.list();
|
||||
} catch (_) {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>Room Layouts</h1>
|
||||
<div style="padding:28px 36px;display:flex;flex-direction:column;gap:22px">
|
||||
<div>
|
||||
<span class="eyebrow">Räume</span>
|
||||
<h1 class="h1" style="font-family:var(--serif)">Raumlayout-Editor</h1>
|
||||
</div>
|
||||
|
||||
<div class="management-grid">
|
||||
<div class="rooms-panel">
|
||||
<h2>Rooms</h2>
|
||||
<form onsubmit={createRoom}>
|
||||
<input bind:value={newRoomName} placeholder="Room Name" required />
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
|
||||
<div class="room-list">
|
||||
{#each rooms as room}
|
||||
<div
|
||||
class="room-item"
|
||||
class:selected={selectedRoomId === room.id}
|
||||
onclick={() => selectedRoomId = room.id}
|
||||
>
|
||||
{room.name}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="card" style="overflow:hidden">
|
||||
<div style="padding:14px 18px;border-bottom:1px solid var(--rule);display:flex;align-items:center;justify-content:space-between">
|
||||
<div class="serif" style="font-size:18px;font-weight:500">Räume</div>
|
||||
<form onsubmit={(e) => { e.preventDefault(); createRoom(); }} style="display:flex;gap:8px">
|
||||
<input class="input" bind:value={newRoomName} placeholder="Raumname" style="width:200px" />
|
||||
<button class="btn" type="submit">+ Neuer Raum</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="editor-panel">
|
||||
{#if selectedRoom}
|
||||
<div class="editor-header">
|
||||
<h2>Editing: {selectedRoom.name}</h2>
|
||||
<div class="toolbar">
|
||||
<button onclick={() => addElement('seat')}>Add Seat</button>
|
||||
<button onclick={() => addElement('table')}>Add Table</button>
|
||||
<button onclick={() => addElement('door')}>Add Door</button>
|
||||
<button class="save-btn" onclick={saveLayout}>Save Layout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="canvas-container">
|
||||
<RoomCanvas
|
||||
bind:elements={selectedRoom.layout}
|
||||
editable={true}
|
||||
selectedId={selectedElementId}
|
||||
onElementClick={(el) => selectedElementId = el.id}
|
||||
/>
|
||||
|
||||
<div class="properties-panel">
|
||||
<h3>Properties</h3>
|
||||
{#if selectedElement}
|
||||
<div class="field">
|
||||
<label>Label</label>
|
||||
<input bind:value={selectedElement.label} />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Width</label>
|
||||
<input type="number" step="0.5" bind:value={selectedElement.width} />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Height</label>
|
||||
<input type="number" step="0.5" bind:value={selectedElement.height} />
|
||||
</div>
|
||||
<button class="delete-btn" onclick={deleteElement}>Delete Element</button>
|
||||
{:else}
|
||||
<p>Select an element to edit properties.</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p>Select a room to edit its layout.</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if rooms.length === 0}
|
||||
<div style="padding:32px;text-align:center">
|
||||
<span class="small" style="color:var(--ink-4)">Noch keine Räume angelegt.</span>
|
||||
</div>
|
||||
{:else}
|
||||
<table style="width:100%;border-collapse:collapse;font-size:13px">
|
||||
<thead>
|
||||
<tr style="background:rgba(0,0,0,0.02);color:var(--ink-3);text-align:left">
|
||||
<th style="padding:10px 14px;font-family:var(--mono);font-size:10.5px;letter-spacing:0.1em;text-transform:uppercase">Name</th>
|
||||
<th style="padding:10px 14px;font-family:var(--mono);font-size:10.5px;letter-spacing:0.1em;text-transform:uppercase;text-align:right">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each rooms as room, i}
|
||||
<tr class="row-hover" style="border-top:{i === 0 ? 'none' : '1px solid var(--rule)'}">
|
||||
<td style="padding:12px 14px">{room.name}</td>
|
||||
<td style="padding:12px 14px;text-align:right">
|
||||
<a href="/admin/rooms/{room.id}" class="btn ghost sm">Bearbeiten</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.management-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 200px 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
.room-item {
|
||||
padding: 10px;
|
||||
border: 1px solid #eee;
|
||||
margin-bottom: 5px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.room-item.selected {
|
||||
background: #e7f1ff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
.editor-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.toolbar button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.save-btn {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.canvas-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.properties-panel {
|
||||
width: 200px;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.field label {
|
||||
display: block;
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
}
|
||||
.field input {
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
}
|
||||
.delete-btn {
|
||||
margin-top: 10px;
|
||||
color: red;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
111
frontend/src/routes/admin/rooms/[roomId]/+page.svelte
Normal file
111
frontend/src/routes/admin/rooms/[roomId]/+page.svelte
Normal file
@@ -0,0 +1,111 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '$lib/api';
|
||||
import RoomCanvas from '$lib/RoomCanvas.svelte';
|
||||
import type { Room, LayoutElement } from '$lib/types';
|
||||
|
||||
const roomId = $derived(parseInt(($page.params as Record<string, string>).roomId));
|
||||
|
||||
let room = $state<Room | null>(null);
|
||||
|
||||
onMount(async () => {
|
||||
room = await api.admin.rooms.get(roomId);
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (roomId) {
|
||||
api.admin.rooms.get(roomId).then((r: Room) => { room = r; });
|
||||
}
|
||||
});
|
||||
|
||||
async function saveLayout() {
|
||||
if (!room) return;
|
||||
try {
|
||||
await api.admin.rooms.updateLayout(room.id, room.layout);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function addElement(type: LayoutElement['type']) {
|
||||
if (!room) return;
|
||||
const id = Math.random().toString(36).substr(2, 9);
|
||||
const newEl: LayoutElement = {
|
||||
id,
|
||||
label: type === 'seat' ? (room.layout.filter((e: LayoutElement) => e.type === 'seat').length + 1).toString() : '',
|
||||
x: 0, y: 0,
|
||||
width: type === 'table' ? 2 : 1,
|
||||
height: 1,
|
||||
type,
|
||||
};
|
||||
room.layout = [...room.layout, newEl];
|
||||
}
|
||||
|
||||
let selectedElementId = $state<string | null>(null);
|
||||
const selectedElement = $derived(room?.layout.find((e: LayoutElement) => e.id === selectedElementId));
|
||||
|
||||
function deleteElement() {
|
||||
if (!room || !selectedElementId) return;
|
||||
room.layout = room.layout.filter((e: LayoutElement) => e.id !== selectedElementId);
|
||||
selectedElementId = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if room}
|
||||
<div style="padding:28px 36px;display:flex;flex-direction:column;gap:16px">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between">
|
||||
<div>
|
||||
<span class="eyebrow">Räume</span>
|
||||
<h2 class="h2" style="font-family:var(--serif)">{room.name}</h2>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px">
|
||||
<button class="btn ghost" onclick={() => addElement('seat')}>+ Sitz</button>
|
||||
<button class="btn ghost" onclick={() => addElement('table')}>+ Tisch</button>
|
||||
<button class="btn ghost" onclick={() => addElement('door')}>+ Tür</button>
|
||||
<button class="btn" onclick={saveLayout}>Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;gap:20px">
|
||||
<div style="flex:1">
|
||||
<RoomCanvas
|
||||
bind:elements={room.layout}
|
||||
editable={true}
|
||||
selectedId={selectedElementId}
|
||||
onElementClick={(el) => { selectedElementId = el.id; }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card" style="width:240px;padding:16px;display:flex;flex-direction:column;gap:12px">
|
||||
<div class="eyebrow">Auswahl</div>
|
||||
{#if selectedElement}
|
||||
<div style="display:flex;flex-direction:column;gap:8px">
|
||||
<div>
|
||||
<div class="tiny" style="color:var(--ink-3)">Bezeichnung</div>
|
||||
<input class="input" bind:value={selectedElement.label} />
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
||||
<div>
|
||||
<div class="tiny" style="color:var(--ink-3)">Breite</div>
|
||||
<input class="input" type="number" step="0.5" bind:value={selectedElement.width} />
|
||||
</div>
|
||||
<div>
|
||||
<div class="tiny" style="color:var(--ink-3)">Höhe</div>
|
||||
<input class="input" type="number" step="0.5" bind:value={selectedElement.height} />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
style="color:var(--accent);background:none;border:none;cursor:pointer;text-align:left;font-family:var(--sans);font-size:13px;padding:4px 0"
|
||||
onclick={deleteElement}
|
||||
>Löschen ⌫</button>
|
||||
</div>
|
||||
{:else}
|
||||
<span class="small" style="color:var(--ink-4)">Element auswählen</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div style="padding:28px 36px">
|
||||
<span class="small" style="color:var(--ink-4)">Raum wird geladen…</span>
|
||||
</div>
|
||||
{/if}
|
||||
8
frontend/src/routes/admin/students/+page.svelte
Normal file
8
frontend/src/routes/admin/students/+page.svelte
Normal file
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
// Stub — full implementation in Phase 6
|
||||
</script>
|
||||
|
||||
<div style="padding:28px 36px">
|
||||
<span class="eyebrow">Studierende</span>
|
||||
<h1 class="h1" style="font-family:var(--serif)">Studierende</h1>
|
||||
</div>
|
||||
Reference in New Issue
Block a user