No description
  • TypeScript 99.5%
  • CSS 0.3%
  • Dockerfile 0.1%
Find a file
Wesley Channon 722a88110d
All checks were successful
deploy / build (push) Successful in 20s
deploy / deploy (push) Successful in 2s
deploy / detect (push) Successful in 4s
deploy / verify (push) Successful in 19s
feat: timetables + timetable-detail apps — weekly grid editor
timetables (singleton): per-school list with status filter (draft /
active / archived), search by name + description.
timetable-detail (hidden keyed): header with activate/archive/delete
lifecycle actions, editable header form on drafts, Mon-Fri × periods
grid with click-cell to add/edit/delete entries (class, teacher, room,
notes). Reads periods from attendance + classes from directory for
the pickers.

Spec sync: brings in TimetablesTimetableList / EntryList /
*Schedule envelope shapes from backend a38a8da.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 09:58:18 +02:00
.forgejo/workflows deploy: frontend on school.synthrik.com via CI/CD 2026-05-11 18:45:11 +00:00
deploy/k8s deploy: services own /api/* natively; drop api-stripprefix middleware 2026-05-11 20:00:21 +00:00
openapi feat: timetables + timetable-detail apps — weekly grid editor 2026-05-14 09:58:18 +02:00
src feat: timetables + timetable-detail apps — weekly grid editor 2026-05-14 09:58:18 +02:00
.gitignore init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
.prettierignore init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
.prettierrc init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
Dockerfile deploy: frontend on school.synthrik.com via CI/CD 2026-05-11 18:45:11 +00:00
eslint.config.js init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
index.html init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
nginx.conf deploy: frontend on school.synthrik.com via CI/CD 2026-05-11 18:45:11 +00:00
package.json feat: wire login to identity service via OpenAPI codegen 2026-05-11 18:32:52 +00:00
pnpm-lock.yaml feat: wire login to identity service via OpenAPI codegen 2026-05-11 18:32:52 +00:00
pnpm-workspace.yaml init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
README.md init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
tsconfig.app.json init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
tsconfig.json init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
tsconfig.node.json init: windowed-desktop framework scaffold 2026-05-11 17:07:32 +00:00
vite.config.ts deploy: drop Vite dev proxy, swagger at school.synthrik.com/api 2026-05-11 19:14:53 +00:00

synthrik / frontend

The Synthrik schools-platform frontend. One shell, role-gated app launcher, windowed-desktop UI.

Stack

  • Vite + React 19 + TypeScript (strict)
  • Tailwind v4 + a small set of in-repo primitives (src/ui/)
  • Zustand for window-manager state
  • TanStack Query for server state (wired later)
  • TanStack Router for routing (wired later)
  • Custom pointer-events window manager — no react-rnd, no react-draggable

Layout

src/
├── main.tsx, app.tsx          # bootstrap + providers
├── ui/                        # primitives (Button, Icon, …)
├── shell/                     # the desktop chrome
│   ├── canvas.tsx, topbar.tsx, appbar.tsx, window.tsx, grid.tsx
│   ├── registry.tsx           # registered apps
│   └── wm/                    # window manager (zustand store + drag/resize/snap)
├── apps/                      # each bolt-on is a self-contained tree
│   └── hello/                 # the smoke-test app
├── lib/                       # env, utils, api clients (later)
└── styles/globals.css

Each "app" registers itself with appRegistry from src/shell/registry.tsx. The app bar's launcher shows whatever has been registered; clicking spawns a window with the app's component as the body.

Run

pnpm install
pnpm dev          # http://localhost:5173
pnpm typecheck    # tsc -b --noEmit
pnpm lint         # eslint .
pnpm build        # tsc -b && vite build

Window manager — quick reference

  • Drag: pointerdown on the window header. Drop near a screen edge to snap (left/right half, top/bottom half, or a corner quadrant).
  • Resize: pointerdown on a window edge or corner. Min size enforced.
  • Header icons (right to left): close · maximise/restore · grid-snap-cycle (walks through halves + quadrants) · minimise.
  • Snap grid: toggle with the "grid" button in the top bar — when on, drag and resize round to gridSize (16 px default).
  • Double-click header: maximise / restore.
  • App bar:
    • Left section: installed-app launchers (click to spawn).
    • Right section: per-window pills (click = focus, click focused = minimise, click minimised = restore + focus).

Adding a new app

// src/apps/attendance/index.tsx
import { Icon } from "@/ui/icon";

function AttendanceApp({ windowId }: { windowId: string }) {
  return <div>...</div>;
}

export const attendanceApp = {
  id: "attendance",
  name: "Attendance",
  icon: Icon.Spawn, // pick or add one
  defaultSize: { w: 720, h: 480 },
  component: AttendanceApp,
};

Then register it in src/apps/index.ts:

import { attendanceApp } from "./attendance";
appRegistry.register(attendanceApp);