Skip to content

rewrite everything - #401

Open
nemvince wants to merge 26 commits into
mainfrom
feat/orpc-rewrite-modules
Open

nemvince wants to merge 26 commits into
mainfrom
feat/orpc-rewrite-modules

Conversation

@nemvince

@nemvince nemvince commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Summary

Replaces the Hono + hono-openapi backend and the hand-rolled client plumbing with a
typed oRPC contract shared by every app, restructures Chronos into self-contained
modules, and rebuilds Iris' settings and timetable controls.

Closes #282
Closes #272
Closes #390

Changes

Stack: Hono → oRPC

  • One contract in @filcdev/api (a procedure per endpoint) replaces Hono routes
    with per-request zod validation.
  • Chronos binds handlers to it via implement(appContract) — a missing, extra or
    mistyped procedure now fails tsc instead of 404ing at runtime.
  • iris and kiosk call it through createApiClient; OpenAPI is generated from the
    contract (bun run openapi:generate), so mergen's Kotlin client still works.
  • The { success, data } envelope is gone: payloads are the value, errors carry a
    machine-readable code from one shared error map.
  • better-auth, the door-lock WebSocket and the unsubscribe HTML pages stay outside
    the contract, dispatched from src/index.ts.

Chronos: modules

  • src/routes/<feature>/ + src/utils/<feature>/ → one src/modules/<feature>/
    folder per feature (handlers, _router.ts, schema.ts, private utils).
  • A module declares its own cron jobs and notifications (_module.ts), so adding a
    feature no longer means editing the cron/notification bootstrap by hand.
  • New @filcdev/auth package: the better-auth user fields, session types and the
    browser client, shared by Chronos and the apps.

Iris: SSR + client

  • Moved to TanStack Start SSR with a per-request router, query client and oRPC
    client; the SSR origin resolves through the proxy-aware accessor.
  • Production is served by srvx over the build output — the custom server.ts is
    gone.
  • Fixed a run of SSR bugs: absolute RPC url in the browser, restored query
    hydration, global stylesheet imported from the root route, i18next instance
    cached per language.

Features

  • PetrikWiFi — the whole WiFi feature on the oRPC stack: self-service and admin
    surfaces, RADIUS authorization, UniFi integration, FreeRADIUS deployment
    (apps/radius/), and a one-shot PetrikWiFi import script.
  • API keys — better-auth's plugin replaces the hand-rolled implementation;
    new user and admin screens plus the admin procedures over every user's keys.
  • Excel export — substitutions and moved lessons, with a date range picker.
  • Timetable — empty state when no timetable is available.

Iris: settings + toolbar

  • The settings dialog and /settings page were the same controls twice → one
    dialog with sections (General / Appearance / Groups / Notifications / API Keys),
    mounted once at the app root.
  • New Appearance section: theme plus the grid/card timetable view. The view is a
    real preference now (instant, no reload; remembered across devices) instead of a
    ?view= param.
  • Public timetable toolbar rebuilt as one bar on a new ToggleGroup primitive —
    one control language, two rows on mobile, no sideways scrolling.
  • API-key list is a list rather than a 7-column table that never fit.
  • Navbar language picker removed (it lives in General).

Chores

  • Toolchain bumped (bun 1.4.2, turbo 2.11.7, biome 2.5.15, react 19.3, vite 8.3.3,
    tailwind 4.3.3, better-auth 1.7.7) and shared versions folded into the root
    catalog.
  • Telemetry pins its data-collection categories instead of sendDefaultPii.
  • Dropped the better-auth account issuer column (migration 0022).
  • Kiosk moved onto the shared client.

Verification

  • bun run lint
  • bun run typecheck (8/8)
  • bun run build (3/3) — routers, server entrypoints and bundler config all changed
  • Translations added to both en and hu
  • Migrations generated and committed (0022, 0024)

Chronos now serves a single oRPC contract owned by @filcdev/api instead
of Hono routes with per-request zod validation:

- packages/api holds the contract (one procedure per endpoint, with the
  filcRoute metadata mergen's generated Kotlin client reads), the wire
  schemas, the shared error map and the typed client factory.
- Chronos binds handlers to that contract with implement(appContract), so
  a missing, extra or mistyped procedure fails tsc instead of 404ing.
- iris and kiosk consume the contract through createApiClient; iris also
  moves onto TanStack Start SSR with a per-request client and router.
- The response envelope is gone: payloads are the value, and errors carry
  the machine-readable code from the shared error map.
- better-auth, the aegis door-lock WebSocket and the unsubscribe HTML pages
  stay outside the contract and are dispatched from src/index.ts.

Gates: bun lint, bun typecheck (8/8), bun run build (3/3).
apps/chronos/src/routes/<feature>/ and the feature-private helpers under
src/utils/<feature>/ become one folder per module:

  src/modules/<feature>/
    <handlers>.ts   # from src/routes/<feature>/
    _router.ts      # unchanged: the plain literal src/router.ts assembles
    utils/          # from src/utils/<feature>/, when the feature had any
    schema.ts       # from src/database/schema/<feature>.ts

src/database/schema keeps authentication, authorization and api-keys: they
are the identity and RBAC tables every other schema has a foreign key to,
so they stay shared rather than belonging to one module. The notification
engine stays in src/utils/notifications because four features dispatch
through it.

Verified: no route imported another feature's routes, and every feature's
utils were touched only by that feature (plus cron.ts, which the module
manifests will replace).

Gates: bun lint, bun typecheck.
`kioskHeartbeatResponseSchema` was a discriminatedUnion on `registered`, but
two of its three branches are `registered: z.literal(true)` and differ only in
`enabled`. Zod rejects that at parse time, so *every* heartbeat — registered
or not — threw "Duplicate discriminator value" and the endpoint answered 500.

A plain union validates the same three shapes, discriminating by declared
order. Verified against a live server: an unknown box gets `{"registered":
false}`, an enabled box its config, a disabled box its name and kind without
one. The generated OpenAPI document is byte-identical, so mergen's client is
unaffected.
The feature folders existed, but everything a feature owns beyond its routes
was registered by hand in four shared files: `database/index.ts` spread every
schema, `utils/cron.ts` listed every job, and the notification engine kept a
second copy of every handler's audience resolver and preference key in two
records keyed by the same string union. Adding a notification meant editing
five places that had nothing to do with each other.

Now a module's `_module.ts` (`satisfies Module`) declares its `jobs` and its
`notifications`, `modules/index.ts` lists them, and both `utils/cron.ts` and
the notification bootstrap read that list. A handler carries its own
`getAudience` and `preferenceKey`, so the engine's `audienceResolvers` and
`preferenceKeys` records are gone — what a notification says and who receives
it now live with the feature that raises it.

Tables deliberately stay out of the manifest: `src/database/index.ts`
constructs `db` at module load, so it must not import a module's handlers and
through them the engine that queries `db`. They get their own leaf list in
`modules/schemas.ts`, which drizzle.config.ts globs alongside the shared
identity/RBAC schema.

The routers are unchanged on purpose — `src/router.ts` still spells every
feature out so `base.router({...})` keeps failing to compile when a procedure
is missing or mistyped.

Verified on a live server against Postgres: 3 cron jobs and 8 notification
handlers registered, exactly as before, with the same preference gates
(cohort_reselection_required correctly ungated); all three heartbeat response
shapes, the 104-path OpenAPI document and /api/health unchanged.
Both server-side call sites built the API origin by parsing the request
object: `new URL(getRequest().url).origin`. Two problems with that.

`getRequest()` returns the h3 request, whose url carries the origin the
*server* saw. Behind the platform proxy that is the internal origin, so a
server render sent its API calls back to itself instead of out to Chronos —
and iris's own server.ts answers /api/* with a 404, which the loaders'
`prefetch` swallows. The result is a shell with no data and a browser that
has to redo the work: content that only appears after a refresh.

The accessor for this is `getRequestUrl()`, which returns a URL already and
resolves the origin from `x-forwarded-host` / `x-forwarded-proto`. Note that
`xForwardedHost` defaults to *false*, so the host — the half that matters
behind the proxy — needs asking for explicitly; verified with a probe that a
plain request resolves to `http://localhost:3000` and one carrying
`X-Forwarded-Host: filc.petrik.hu` to `https://filc.petrik.hu`.

Second, `fetchSession` now returns null instead of throwing when the lookup
fails. It runs in the root route's `beforeLoad`, so a throw there fails the
whole render rather than one query. Verified against the production build:
a request whose resolved origin is unreachable used to answer 500 with
"Something went wrong!", and now answers 200 with the page rendered.

Both `new URL()` calls are gone from the SSR path. `requestOrigin` is
wrapped in the file's existing `createIsomorphicFn` idiom, because
`@tanstack/react-start/server` is denied in the client bundle and the
build's import-protection plugin rejects the import outside a `.server()`
branch.
Two bugs stacked, both invisible from the page and only visible in the data.

The previous commit built the server-side API url as
`${requestOrigin() ?? apiBaseUrl}/rpc`, dropping the `/api` segment whenever
an origin was available — so every server-render request went to `/rpc/...`
instead of `/api/rpc/...` and 404'd. `prefetch` swallows a failed
`ensureQueryData`, so the failure was silent: the query cache stayed empty,
nothing was hydrated, and the browser started cold.

With the path fixed, the payload still carried no query state. The cause is
the SSR query integration: `@tanstack/react-router-ssr-query` (newest release
1.167.3, and no longer published in step with the router) implements the
pre-1.169 dehydrate contract, pushing queries into a ReadableStream the router
used to drain. The installed router awaits `router.options.dehydrate()` and
serialises its return value as `dehydratedData` — that package's hook returns
undefined, so the payload shipped nothing.

Replaced with a local `setupSsrQuery` that dehydrates to a returned value and
hydrates on the client through `router.options.hydrate`, which the router
still calls. Only queries settled by the end of the render are dehydrated;
one arriving later is simply fetched by the browser. The package's redirect
handling is not carried over because nothing in the app throws redirects.

Verified in dev against a seeded timetable: the SSR payload now carries
`queryKey`/`dataUpdatedAt` for the four prefetched queries, the page renders
the timetable server-side, and the browser issues zero API calls on load.
Gates: bun lint, bun typecheck (8/8), bun run build (3/3).
Bumps the toolchain (bun 1.4.2, turbo 2.11.7, biome 2.5.15), the
frontend runtime (react 19.3, vite 8.3.3, sentry 11, tailwind 4.3.3) and
better-auth 1.7.7, and moves every version that more than one workspace
pinned on its own into the root `catalog`: @tsconfig/strictest (8
workspaces), @types/react, react/react-dom, tailwindcss, lucide-react,
dayjs, recharts, sonner, next-themes, @vitejs/plugin-react, vite and the
@TanStack pair that had drifted apart.

One known gap, kept as-is: bun does not resolve `catalog:` in
`peerDependencies` — it writes the literal string into the lockfile
rather than the range — so `@filcdev/{auth,navigator-3d,ui}` now declare
`react: "catalog:"` as a peer. The two `react` peer entries are left
untouched pending a decision; every `dependencies` entry resolves
correctly.

Also picks up the turborepo agent-guidance block it re-adds to AGENTS.md
before repository-scoped commands.
…roles

`account.issuer` and its composite unique index came from a better-auth
version whose account model carried an issuer; 1.7.7 does not define the
field outside the JWT plugin, so the column was dead weight that no
insert filled and no query read. `accountId` alone stays unique per
provider through the remaining index.

`user.roles` gains `default(['user'])` so a row inserted without roles —
better-auth's own sign-up path — is a normal user instead of violating
the not-null constraint.

Migration 0022 is generated, not hand-written: `drizzle-kit generate`
reports "No schema changes, nothing to migrate" against it, so the
snapshot and the schema agree.
…aultPii

Sentry v11 replaces `sendDefaultPii` with per-category
`dataCollection` defaults, and those default to collecting: cookies,
request bodies, user info and genAI/GraphQL payloads all now flow unless
each is disabled. Simply dropping the old option, which the v11 upgrade
did, silently turned collection back on for both apps.

Every category is therefore pinned explicitly to the v10 behaviour the
option used to give, in chronos and iris alike; anything unlisted keeps
its permissive default, which is noted at the call site. Iris's replay
comment loses its `sendDefaultPii` reference so it points at the
baseline that now enforces the same rule.
`createI18n` built a fresh instance on every server render — a new
ResourceStore over both locale bundles, translator, and lazily built
`Intl` caches, all discarded after one request.

Instances are now keyed by language. A single shared one was never an
option, because `changeLanguage` mutates in place and would leak the
first request's language into every later render; keyed per language the
language is fixed at creation and server renders only read from it. The
cookie's value is normalized before it becomes a key, so a caller cannot
grow the map with arbitrary values, and a hit additionally requires
`cached.language === lng` — in the browser a switch moves the instance
off the language it was created under.

`supportedLngs` is spread into the options rather than passed as the
`as const` tuple, which i18next would otherwise hold and could mutate.
The global stylesheet was imported by `src/client.tsx`, the browser
entry. TanStack Start discovers a route's dev CSS by crawling the module
graph of the matched routes' files, and the entry is not a route, so
`GET /@tanstack-start/styles.css?routes=…` answered 200 with an empty
body: the `<link>` HeadContent rendered was blank and the page stayed
unstyled until the client bundle ran and Vite injected the CSS — a
multi-second flash of unstyled content on every dev load. Production was
unaffected, because manifest CSS discovery reads the build graph.

Importing from the root route is the documented placement for app-wide
CSS and puts the stylesheet in the graph the dev crawl walks. Verified
with JS disabled on a cold dev server: the endpoint returns the full
bundle and first paint carries the tokens, with the same CSS asset hash
in the production build as before.
The browser branch built the client with the relative `url: '/api/rpc'`,
and oRPC's link codec constructs a `URL` from that value: the browser
constructor rejects a relative input outright, so every browser-side call
threw `TypeError: Failed to construct 'URL': Invalid URL` before a
request was ever made. The server render was unaffected — it already
resolved an absolute url from `requestOrigin()` — which meant the page
shipped server-rendered data and then failed on the first client-side
query, including the timetable-scoped ones.

The url is now resolved per call from `window.location.origin`, which is
correct in both deployments: the dev Vite proxy and the platform proxy
put iris and Chronos behind one origin.

Verified in the browser against the running dev stack — the client's own
timetable, cohort and period calls return data where they previously
threw, the timetable renders without its error state, and client-side
navigation loads.
…te range

Closes #282. The export menu offered CSV and PDF only, sent no date
range, and the exports mixed untranslated headers with a mangled date
boundary. Four defects, fixed together because they share the same two
call sites:

- Excel joins CSV and PDF, via a lazy `write-excel-file/browser` import
  so the ~30 kB parser stays out of the initial bundle.
- The range picker's `to` was built with `toISOString()` on a local
  midnight, which is the previous day in CEST: the last day of every
  range was silently dropped. `use-export-range.ts` formats both bounds
  as `YYYY-MM-DD` and supplies the localized label, so the two export
  pages share one definition instead of each deriving its own.
- The CSV reader split on commas and corrupted the quoted multi-value
  fields the backend emits ("matek, fizika" columns and embedded
  newlines); it now parses quoted fields, and the flags are split out of
  the component to keep cognitive complexity in bounds.
- Column headers and the range label were hardcoded English; they are
  keys in both locale trees now, along with the new menu entries.

The PDF export additionally embeds the Outfit latin and latin-ext
subsets: the standard-14 fonts are WinAnsi-only, so "Teljes időszak"
exported as "Teljes idQszak" and "Őzike" as "Pzike". Both subsets are
required — latin-ext alone has no ASCII — and the `.woff` form, because
`fontkit` cannot subset the variable `.woff2`.
`apps/iris/server.ts` reimplemented what the framework-adjacent runner
already does: hand-rolled static serving of `dist/client`, a `/api`
guard, and a port read. `srvx serve --prod -s ../client
dist/server/server.js` is the shape the TanStack Start docs give for a
Vite build on Node/Bun, and it serves the same two things (the client
directory and the built server entry) with ETag and Brotli, which the
custom file did not do.

Deleted `server.ts` and its `tsconfig` exclusion; `start` is now the
srvx command. The `/api` guard it carried is not needed: Traefik routes
`/api` to Chronos ahead of iris, so a server render's self-fetch never
lands back on this process.

The Docker release stage needed fixing to match, and it was already
broken before this change: it copied only `dist` and `server.ts`, but
the built server bundle keeps `react`/`react-dom`/`@tanstack/*`
external, so both the old and the new entrypoint failed with
`Cannot find module 'react-dom/server'`. The stage now copies both
`node_modules` levels at their original relative paths — entries under
`apps/iris/node_modules` are symlinks into the root `node_modules/.bun`
store, so flattening them breaks resolution — and runs `dist` from
`apps/iris/` so the bundle's upward module walk finds them. The
entrypoint invokes srvx by path rather than `bun x`, which would fetch
it from the network at container start.

Verified with a real `docker build` and the built image behind a
Traefik-shaped proxy: SSR 200 rendering the timetable, `/healthz` 200,
hashed assets 200 with ETag, and a browser load with the theme tokens
applied and no error state. `bun lint`, `bun typecheck` (8/8) and
`bun run build` (3/3) pass.

Note: the release image now carries the installed dependency tree, so
it is larger than before; slimming it needs a production-only install.
…s plugin

The `api_key` table, its scrypt hashing and the `/users/me/api-keys` oRPC
procedures were a reimplementation of what `@better-auth/api-key` already
does, and they carried three defects the plugin does not have: a
synchronous `scryptSync` (32.7 ms per validation, blocking the event loop
on a single-process runtime), a `prefix` column that was literally the
first 8 characters of the secret while the schema called it non-secret,
and a `key_hash` index that could never be used because validation
recomputed the hash in memory.

The plugin owns the `apikey` table now. It hashes with SHA-256, enforces
expiry and an enable flag, rate-limits per key, and — registered with
`enableSessionForAPIKeys` — turns a valid key into a session, so
`resolveCaller` needs no API-key branch at all and the guards below it
keep working unchanged.

Two things the plugin does not do, added here:

- It reads only `x-api-key`. The aegis door-lock firmware sends
  `Authorization: Bearer`, so `customAPIKeyGetter` accepts both.
- It *throws* on a key that is present but unusable (`Invalid API key.`,
  `API Key is disabled`) instead of answering null. Uncaught, that turned
  every request carrying a stale key into a 500; both call sites now treat
  a failed key as "not this credential".

The Drizzle schema mirrors the plugin's own declaration rather than being
derived, because the plugin writes the table itself. Two details are
load-bearing and were found by running it: the table must be in the
adapter's `schema` map (better-auth's schema check rejects every call
otherwise), and `id` must be a `uuid` with a DB default — the plugin does
not declare `id`, so core injects it and expects the database to generate
it under `generateId: 'uuid'`.

`apps/chronos/tsconfig.json` loses `declaration`/`emitDeclarationOnly`:
they are dead config under `noEmit: true`, and they forced tsc to name
the plugin's internal `SchemaCheck` type (TS2883) in a declaration it
never emits.

Verified against the running server: create, `x-api-key` and Bearer
authentication, verify, list, disable, delete; an invalid key and an
anonymous call both answer 401 rather than 500.
better-auth's own api-key endpoints are session-scoped, so they can only
ever answer for the caller's own keys — an admin screen needs all of them.
These three procedures read the plugin's `apikey` table directly:

- `GET /admin/api-keys` lists every key joined with its owner, with the
  same limit/offset/search paging the users list uses, plus an `ownerId`
  filter.
- `PATCH /admin/api-keys/{keyId}` flips `enabled`.
- `DELETE /admin/api-keys/{keyId}` revokes any key.

Deleting deliberately does not go through the plugin's `deleteApiKey`,
which refuses a key the calling session does not own; revoking someone
else's key is the point of the screen.

The handler never selects the hashed `key` column, and the join casts
`reference_id` to text because the plugin stores it as `text` while
`user.id` is a `uuid` — Postgres rejects the comparison otherwise.
Two surfaces, both new:

- Settings gains an "API keys" card: the signed-in user's keys in a table
  (name, identifying `start`, created, expiry, last used, enabled), with
  create, rename, enable/disable and delete. Creating a key reveals the
  raw secret exactly once, behind a warning, with a copy button — the
  dialog refuses to dismiss itself until the user acknowledges it, because
  no later read can return that value.
- `/admin/api-keys` lists every user's keys with the owner joined, paging,
  search over owner and key name, stat cards, and the same enable/disable
  and delete actions, gated on `users:manage`.

The user surface calls better-auth's client plugin (those endpoints are
outside the oRPC contract by design); the admin surface calls the new
admin procedures, because the plugin cannot see other users' keys. Both
hook modules follow the repo's shape — queries plus per-operation
mutations with toasts, invalidation and `onSaved`.

`useCopyToClipboard` is new: the repo had no clipboard helper, and copying
a value the user can never see again must not fail silently, so the hook
owns the toast as well as the write and falls back to a selection-based
copy where the Clipboard API is unavailable over plain HTTP.

Verified in a browser against a live stack: the settings card renders and
create/list/rename/disable/delete all work through the page's own client,
with the raw key returned only by create and absent from list rows; the
admin page renders the owner, prefix, dates and actions, and its search
and owner filters return the right counts.
@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 485 files, which is 385 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 283ef6e5-e609-4f7c-90c5-62c6884d2ac1
📥 Commits

Reviewing files that changed from the base of the PR and between 4719b2a and 4adbe4f.

⛔ Files ignored due to path filters (2)
  • apps/radius/certs/ca.pem is excluded by !**/*.pem
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (485)
  • .agents/skills/chronos-route-procedure/SKILL.md
  • .agents/skills/chronos-schema-procedure/SKILL.md
  • .agents/skills/iris-data-flow/SKILL.md
  • .husky/install.ts
  • .husky/pre-commit
  • AGENTS.md
  • CONTRIBUTING.md
  • README.md
  • apps/chronos/.env.example
  • apps/chronos/drizzle.config.ts
  • apps/chronos/openapi/chronos-openapi.json
  • apps/chronos/package.json
  • apps/chronos/scripts/generate-openapi.ts
  • apps/chronos/scripts/migrate-petrikwifi.ts
  • apps/chronos/src/_types/auth.ts
  • apps/chronos/src/_types/globals.ts
  • apps/chronos/src/_types/hc.ts
  • apps/chronos/src/database/index.ts
  • apps/chronos/src/database/migrations/0022_absurd_next_avengers.sql
  • apps/chronos/src/database/migrations/0023_last_gorgon.sql
  • apps/chronos/src/database/migrations/0024_moaning_marvel_boy.sql
  • apps/chronos/src/database/migrations/meta/0022_snapshot.json
  • apps/chronos/src/database/migrations/meta/0023_snapshot.json
  • apps/chronos/src/database/migrations/meta/0024_snapshot.json
  • apps/chronos/src/database/migrations/meta/_journal.json
  • apps/chronos/src/database/schema/api-keys.ts
  • apps/chronos/src/database/schema/authentication.ts
  • apps/chronos/src/database/scripts/import-navigator.ts
  • apps/chronos/src/database/scripts/reset.ts
  • apps/chronos/src/database/scripts/seed.ts
  • apps/chronos/src/database/timetable-import-store.ts
  • apps/chronos/src/index.ts
  • apps/chronos/src/middleware/anonymous-id.ts
  • apps/chronos/src/middleware/auth.ts
  • apps/chronos/src/middleware/rate-limit.ts
  • apps/chronos/src/middleware/security.ts
  • apps/chronos/src/middleware/timing.ts
  • apps/chronos/src/middleware/wifi.ts
  • apps/chronos/src/modules/api-keys/_router.ts
  • apps/chronos/src/modules/api-keys/index.ts
  • apps/chronos/src/modules/bug-report/_router.ts
  • apps/chronos/src/modules/bug-report/index.ts
  • apps/chronos/src/modules/bug-report/schema.ts
  • apps/chronos/src/modules/cohort/_router.ts
  • apps/chronos/src/modules/cohort/index.ts
  • apps/chronos/src/modules/dashboard/_router.ts
  • apps/chronos/src/modules/dashboard/index.ts
  • apps/chronos/src/modules/doorlock/_module.ts
  • apps/chronos/src/modules/doorlock/_notifications.ts
  • apps/chronos/src/modules/doorlock/_router.ts
  • apps/chronos/src/modules/doorlock/cards.ts
  • apps/chronos/src/modules/doorlock/device-socket.ts
  • apps/chronos/src/modules/doorlock/devices.ts
  • apps/chronos/src/modules/doorlock/export.ts
  • apps/chronos/src/modules/doorlock/logs.ts
  • apps/chronos/src/modules/doorlock/ota.ts
  • apps/chronos/src/modules/doorlock/schema.ts
  • apps/chronos/src/modules/doorlock/self.ts
  • apps/chronos/src/modules/doorlock/stats.ts
  • apps/chronos/src/modules/doorlock/utils/cards.ts
  • apps/chronos/src/modules/doorlock/utils/device-sync.ts
  • apps/chronos/src/modules/doorlock/utils/schemas.ts
  • apps/chronos/src/modules/health/_router.ts
  • apps/chronos/src/modules/health/index.ts
  • apps/chronos/src/modules/index.ts
  • apps/chronos/src/modules/kiosk/_router.ts
  • apps/chronos/src/modules/kiosk/departures.ts
  • apps/chronos/src/modules/kiosk/heartbeat.ts
  • apps/chronos/src/modules/kiosk/index.ts
  • apps/chronos/src/modules/kiosk/news-image.ts
  • apps/chronos/src/modules/kiosk/news.ts
  • apps/chronos/src/modules/kiosk/petrik-news.ts
  • apps/chronos/src/modules/kiosk/schema.ts
  • apps/chronos/src/modules/kiosk/utils/feed-url.ts
  • apps/chronos/src/modules/kiosk/utils/petrik-news.ts
  • apps/chronos/src/modules/kiosk/weather.ts
  • apps/chronos/src/modules/module.ts
  • apps/chronos/src/modules/navigator/_router.ts
  • apps/chronos/src/modules/navigator/buildings.ts
  • apps/chronos/src/modules/navigator/classroom-types.ts
  • apps/chronos/src/modules/navigator/classrooms.ts
  • apps/chronos/src/modules/navigator/corridors.ts
  • apps/chronos/src/modules/navigator/graph.ts
  • apps/chronos/src/modules/navigator/lifts.ts
  • apps/chronos/src/modules/navigator/schema.ts
  • apps/chronos/src/modules/navigator/stairs.ts
  • apps/chronos/src/modules/navigator/transfer.ts
  • apps/chronos/src/modules/navigator/translations.ts
  • apps/chronos/src/modules/navigator/utils/errors.ts
  • apps/chronos/src/modules/navigator/utils/schemas.ts
  • apps/chronos/src/modules/navigator/utils/transfer.ts
  • apps/chronos/src/modules/news/_module.ts
  • apps/chronos/src/modules/news/_notifications.ts
  • apps/chronos/src/modules/news/_router.ts
  • apps/chronos/src/modules/news/announcements.ts
  • apps/chronos/src/modules/news/blogs.ts
  • apps/chronos/src/modules/news/schema.ts
  • apps/chronos/src/modules/news/system-messages.ts
  • apps/chronos/src/modules/news/utils/announcements.ts
  • apps/chronos/src/modules/news/utils/cohort.ts
  • apps/chronos/src/modules/news/utils/kiosks.ts
  • apps/chronos/src/modules/news/utils/schemas.ts
  • apps/chronos/src/modules/news/utils/shared.ts
  • apps/chronos/src/modules/notifications/_module.ts
  • apps/chronos/src/modules/notifications/_router.ts
  • apps/chronos/src/modules/notifications/notifications.ts
  • apps/chronos/src/modules/notifications/schema.ts
  • apps/chronos/src/modules/notifications/unsubscribe-html.ts
  • apps/chronos/src/modules/ping/_router.ts
  • apps/chronos/src/modules/ping/index.ts
  • apps/chronos/src/modules/ping/uptime.ts
  • apps/chronos/src/modules/roles/_router.ts
  • apps/chronos/src/modules/roles/index.ts
  • apps/chronos/src/modules/schemas.ts
  • apps/chronos/src/modules/timetable/_curriculum-router.ts
  • apps/chronos/src/modules/timetable/_module.ts
  • apps/chronos/src/modules/timetable/_notifications.ts
  • apps/chronos/src/modules/timetable/_router.ts
  • apps/chronos/src/modules/timetable/cohort.ts
  • apps/chronos/src/modules/timetable/export.ts
  • apps/chronos/src/modules/timetable/groups.ts
  • apps/chronos/src/modules/timetable/import.ts
  • apps/chronos/src/modules/timetable/index.ts
  • apps/chronos/src/modules/timetable/lesson.ts
  • apps/chronos/src/modules/timetable/moved-lesson.ts
  • apps/chronos/src/modules/timetable/period.ts
  • apps/chronos/src/modules/timetable/room.ts
  • apps/chronos/src/modules/timetable/schema.ts
  • apps/chronos/src/modules/timetable/substitution.ts
  • apps/chronos/src/modules/timetable/teacher.ts
  • apps/chronos/src/modules/timetable/utils/active.ts
  • apps/chronos/src/modules/timetable/utils/cleanup.ts
  • apps/chronos/src/modules/timetable/utils/date.ts
  • apps/chronos/src/modules/timetable/utils/enrich-lessons.ts
  • apps/chronos/src/modules/timetable/utils/remap-substitutions.ts
  • apps/chronos/src/modules/timetable/utils/weekday.ts
  • apps/chronos/src/modules/users/_router.ts
  • apps/chronos/src/modules/users/index.ts
  • apps/chronos/src/modules/wifi/_module.ts
  • apps/chronos/src/modules/wifi/_router.ts
  • apps/chronos/src/modules/wifi/admin.ts
  • apps/chronos/src/modules/wifi/radius.ts
  • apps/chronos/src/modules/wifi/schema.ts
  • apps/chronos/src/modules/wifi/self.ts
  • apps/chronos/src/modules/wifi/stats.ts
  • apps/chronos/src/modules/wifi/status.ts
  • apps/chronos/src/modules/wifi/utils/cleanup.ts
  • apps/chronos/src/modules/wifi/utils/constants.ts
  • apps/chronos/src/modules/wifi/utils/controller.ts
  • apps/chronos/src/modules/wifi/utils/encryptor.ts
  • apps/chronos/src/modules/wifi/utils/mac.ts
  • apps/chronos/src/modules/wifi/utils/radius.ts
  • apps/chronos/src/modules/wifi/utils/speed-profile.ts
  • apps/chronos/src/modules/wifi/utils/sync.ts
  • apps/chronos/src/modules/wifi/utils/unifi-api.ts
  • apps/chronos/src/modules/wifi/utils/unifi-controller.ts
  • apps/chronos/src/orpc.ts
  • apps/chronos/src/router.ts
  • apps/chronos/src/routes/bug-report/_factory.ts
  • apps/chronos/src/routes/bug-report/_router.ts
  • apps/chronos/src/routes/bug-report/index.ts
  • apps/chronos/src/routes/cohort/_factory.ts
  • apps/chronos/src/routes/cohort/_router.ts
  • apps/chronos/src/routes/cohort/index.ts
  • apps/chronos/src/routes/dashboard/_factory.ts
  • apps/chronos/src/routes/dashboard/_router.ts
  • apps/chronos/src/routes/doorlock/_factory.ts
  • apps/chronos/src/routes/doorlock/_router.ts
  • apps/chronos/src/routes/doorlock/cards.ts
  • apps/chronos/src/routes/doorlock/devices.ts
  • apps/chronos/src/routes/doorlock/ota.ts
  • apps/chronos/src/routes/doorlock/self.ts
  • apps/chronos/src/routes/health/_factory.ts
  • apps/chronos/src/routes/health/_router.ts
  • apps/chronos/src/routes/health/index.ts
  • apps/chronos/src/routes/kiosk/_factory.ts
  • apps/chronos/src/routes/kiosk/_router.ts
  • apps/chronos/src/routes/kiosk/heartbeat.ts
  • apps/chronos/src/routes/kiosk/index.ts
  • apps/chronos/src/routes/kiosk/news-image.ts
  • apps/chronos/src/routes/kiosk/news.ts
  • apps/chronos/src/routes/kiosk/weather.ts
  • apps/chronos/src/routes/navigator/_factory.ts
  • apps/chronos/src/routes/navigator/_router.ts
  • apps/chronos/src/routes/navigator/buildings.ts
  • apps/chronos/src/routes/navigator/classroom-types.ts
  • apps/chronos/src/routes/navigator/classrooms.ts
  • apps/chronos/src/routes/navigator/corridors.ts
  • apps/chronos/src/routes/navigator/graph.ts
  • apps/chronos/src/routes/navigator/lifts.ts
  • apps/chronos/src/routes/navigator/stairs.ts
  • apps/chronos/src/routes/navigator/translations.ts
  • apps/chronos/src/routes/news/_factory.ts
  • apps/chronos/src/routes/news/_router.ts
  • apps/chronos/src/routes/news/blogs.ts
  • apps/chronos/src/routes/news/system-messages.ts
  • apps/chronos/src/routes/notifications/_factory.ts
  • apps/chronos/src/routes/notifications/_router.ts
  • apps/chronos/src/routes/notifications/notifications.ts
  • apps/chronos/src/routes/ping/_factory.ts
  • apps/chronos/src/routes/ping/_router.ts
  • apps/chronos/src/routes/ping/index.ts
  • apps/chronos/src/routes/ping/uptime.ts
  • apps/chronos/src/routes/roles/_factory.ts
  • apps/chronos/src/routes/roles/_router.ts
  • apps/chronos/src/routes/roles/index.ts
  • apps/chronos/src/routes/timetable/_factory.ts
  • apps/chronos/src/routes/timetable/_router.ts
  • apps/chronos/src/routes/timetable/cohort.ts
  • apps/chronos/src/routes/timetable/period.ts
  • apps/chronos/src/routes/timetable/room.ts
  • apps/chronos/src/routes/timetable/teacher.ts
  • apps/chronos/src/routes/users/_factory.ts
  • apps/chronos/src/routes/users/_router.ts
  • apps/chronos/src/routes/users/api-keys.ts
  • apps/chronos/src/routes/users/index.ts
  • apps/chronos/src/utils/api-keys.ts
  • apps/chronos/src/utils/authentication.ts
  • apps/chronos/src/utils/authorization.ts
  • apps/chronos/src/utils/cron.ts
  • apps/chronos/src/utils/environment.ts
  • apps/chronos/src/utils/http.ts
  • apps/chronos/src/utils/kiosk/schemas.ts
  • apps/chronos/src/utils/notifications/cleanup.ts
  • apps/chronos/src/utils/notifications/engine.ts
  • apps/chronos/src/utils/notifications/initialize.ts
  • apps/chronos/src/utils/notifications/locale.ts
  • apps/chronos/src/utils/notifications/providers/fcm.ts
  • apps/chronos/src/utils/notifications/substitution-teacher.ts
  • apps/chronos/src/utils/notifications/types.ts
  • apps/chronos/src/utils/openapi-spec.ts
  • apps/chronos/src/utils/openapi.ts
  • apps/chronos/src/utils/telemetry.ts
  • apps/chronos/tsconfig.json
  • apps/iris/.env.example
  • apps/iris/Dockerfile
  • apps/iris/index.html
  • apps/iris/nginx.conf
  • apps/iris/package.json
  • apps/iris/public/healthz
  • apps/iris/public/locales/en/translation.json
  • apps/iris/public/locales/hu/translation.json
  • apps/iris/src/client.tsx
  • apps/iris/src/components/admin/admin.types.ts
  • apps/iris/src/components/admin/announcements-dialog.tsx
  • apps/iris/src/components/admin/api-keys-table.tsx
  • apps/iris/src/components/admin/dashboard.tsx
  • apps/iris/src/components/admin/kiosk-dialog.tsx
  • apps/iris/src/components/admin/moved-lesson-dialog.tsx
  • apps/iris/src/components/admin/moved-lesson-export.tsx
  • apps/iris/src/components/admin/navigator/navigator-preview.tsx
  • apps/iris/src/components/admin/navigator/transfer-actions.tsx
  • apps/iris/src/components/admin/news-item-dialog.tsx
  • apps/iris/src/components/admin/roles-table.tsx
  • apps/iris/src/components/admin/sidebar.tsx
  • apps/iris/src/components/admin/substitution-dialog.tsx
  • apps/iris/src/components/admin/substitution-export.tsx
  • apps/iris/src/components/admin/system-messages-dialog.tsx
  • apps/iris/src/components/admin/timetable-edit-dialog.tsx
  • apps/iris/src/components/admin/use-export-range.ts
  • apps/iris/src/components/admin/users-table.tsx
  • apps/iris/src/components/api-keys/api-keys-card.tsx
  • apps/iris/src/components/api-keys/create-api-key-dialog.tsx
  • apps/iris/src/components/api-keys/rename-api-key-dialog.tsx
  • apps/iris/src/components/bug-report-dialog.tsx
  • apps/iris/src/components/doorlock/device-stats-charts.tsx
  • apps/iris/src/components/doorlock/device-stats-dialog.tsx
  • apps/iris/src/components/doorlock/doorlock.types.ts
  • apps/iris/src/components/doorlock/export-logs.tsx
  • apps/iris/src/components/doorlock/user-card-list.tsx
  • apps/iris/src/components/export-button-pdf.tsx
  • apps/iris/src/components/export-button.tsx
  • apps/iris/src/components/lazy.tsx
  • apps/iris/src/components/navbar.tsx
  • apps/iris/src/components/news-panel.tsx
  • apps/iris/src/components/notification-bell.tsx
  • apps/iris/src/components/notification-history-dialog.tsx
  • apps/iris/src/components/notification-viewer-dialog.tsx
  • apps/iris/src/components/settings-dialog-store.ts
  • apps/iris/src/components/settings-dialog.tsx
  • apps/iris/src/components/settings/api-keys-pane.tsx
  • apps/iris/src/components/settings/appearance-pane.tsx
  • apps/iris/src/components/settings/general-pane.tsx
  • apps/iris/src/components/settings/groups-pane.tsx
  • apps/iris/src/components/settings/notifications-pane.tsx
  • apps/iris/src/components/subs-view.tsx
  • apps/iris/src/components/system-message-banner.tsx
  • apps/iris/src/components/timetable/filter-bar.tsx
  • apps/iris/src/components/timetable/index.tsx
  • apps/iris/src/components/timetable/timetable-selector.tsx
  • apps/iris/src/components/timetable/types.ts
  • apps/iris/src/components/timetable/week-selector.tsx
  • apps/iris/src/components/util/language-selector.tsx
  • apps/iris/src/components/util/permission-guard.tsx
  • apps/iris/src/components/util/relative-time.tsx
  • apps/iris/src/components/wifi/wifi-auth-chart.tsx
  • apps/iris/src/components/wifi/wifi-dialogs.tsx
  • apps/iris/src/components/wifi/wifi-filters.tsx
  • apps/iris/src/hooks/admin-api-keys.ts
  • apps/iris/src/hooks/admin-users.ts
  • apps/iris/src/hooks/api-keys.ts
  • apps/iris/src/hooks/bug-reports.ts
  • apps/iris/src/hooks/dev-notifications.ts
  • apps/iris/src/hooks/doorlock-admin.ts
  • apps/iris/src/hooks/kiosks.ts
  • apps/iris/src/hooks/moved-lessons.ts
  • apps/iris/src/hooks/navigator.ts
  • apps/iris/src/hooks/news.ts
  • apps/iris/src/hooks/notifications.ts
  • apps/iris/src/hooks/substitutions.ts
  • apps/iris/src/hooks/timetable-groups.ts
  • apps/iris/src/hooks/timetable-public.ts
  • apps/iris/src/hooks/timetable-teachers.ts
  • apps/iris/src/hooks/timetables-admin.ts
  • apps/iris/src/hooks/use-copy-to-clipboard.ts
  • apps/iris/src/hooks/use-timetable-view.ts
  • apps/iris/src/hooks/wifi-admin.ts
  • apps/iris/src/hooks/wifi.ts
  • apps/iris/src/main.tsx
  • apps/iris/src/routeTree.gen.ts
  • apps/iris/src/router.tsx
  • apps/iris/src/routes/__root.tsx
  • apps/iris/src/routes/_private/admin/api-keys.tsx
  • apps/iris/src/routes/_private/admin/bug-reports.tsx
  • apps/iris/src/routes/_private/admin/doorlock/cards.tsx
  • apps/iris/src/routes/_private/admin/doorlock/devices.tsx
  • apps/iris/src/routes/_private/admin/doorlock/index.tsx
  • apps/iris/src/routes/_private/admin/doorlock/logs.tsx
  • apps/iris/src/routes/_private/admin/kiosks.tsx
  • apps/iris/src/routes/_private/admin/navigator/buildings.tsx
  • apps/iris/src/routes/_private/admin/navigator/classroom-types.tsx
  • apps/iris/src/routes/_private/admin/navigator/classrooms.tsx
  • apps/iris/src/routes/_private/admin/navigator/corridors.tsx
  • apps/iris/src/routes/_private/admin/navigator/index.tsx
  • apps/iris/src/routes/_private/admin/navigator/lifts.tsx
  • apps/iris/src/routes/_private/admin/navigator/preview.tsx
  • apps/iris/src/routes/_private/admin/navigator/stairs.tsx
  • apps/iris/src/routes/_private/admin/navigator/translations.tsx
  • apps/iris/src/routes/_private/admin/news/announcements.tsx
  • apps/iris/src/routes/_private/admin/news/system-messages.tsx
  • apps/iris/src/routes/_private/admin/roles.tsx
  • apps/iris/src/routes/_private/admin/route.tsx
  • apps/iris/src/routes/_private/admin/timetable/manage.tsx
  • apps/iris/src/routes/_private/admin/timetable/moved-lessons.tsx
  • apps/iris/src/routes/_private/admin/timetable/substitutions.tsx
  • apps/iris/src/routes/_private/admin/timetable/teachers.tsx
  • apps/iris/src/routes/_private/admin/users.tsx
  • apps/iris/src/routes/_private/admin/wifi/index.tsx
  • apps/iris/src/routes/_private/admin/wifi/nas.tsx
  • apps/iris/src/routes/_private/admin/wifi/speed-profiles.tsx
  • apps/iris/src/routes/_private/admin/wifi/users.tsx
  • apps/iris/src/routes/_private/cards/index.tsx
  • apps/iris/src/routes/_private/route.tsx
  • apps/iris/src/routes/_private/settings.tsx
  • apps/iris/src/routes/_private/wifi.tsx
  • apps/iris/src/routes/_public/index.tsx
  • apps/iris/src/routes/_public/subs.tsx
  • apps/iris/src/routes/auth/login.tsx
  • apps/iris/src/routes/auth/welcome.tsx
  • apps/iris/src/utils/api.ts
  • apps/iris/src/utils/authentication.ts
  • apps/iris/src/utils/date-locale.ts
  • apps/iris/src/utils/form-schemas.ts
  • apps/iris/src/utils/hc.ts
  • apps/iris/src/utils/i18n.ts
  • apps/iris/src/utils/orpc.ts
  • apps/iris/src/utils/query-keys.ts
  • apps/iris/src/utils/request.ts
  • apps/iris/src/utils/ssr-query.tsx
  • apps/iris/src/utils/telemetry.ts
  • apps/iris/tsconfig.json
  • apps/iris/vite.config.ts
  • apps/kiosk/package.json
  • apps/kiosk/src/components/tv/news-takeover.tsx
  • apps/kiosk/src/hooks/kiosk.ts
  • apps/kiosk/src/hooks/petrik-news.ts
  • apps/kiosk/src/hooks/tv.ts
  • apps/kiosk/src/hooks/use-kiosk-translations.ts
  • apps/kiosk/src/routes/_kiosk/navigator.tsx
  • apps/kiosk/src/utils/api.ts
  • apps/kiosk/src/utils/orpc.ts
  • apps/radius/.env.example
  • apps/radius/certs/.gitignore
  • apps/radius/certs/Makefile
  • apps/radius/certs/ca.crl
  • apps/radius/certs/ca.der
  • apps/radius/certs/passwords.example.mk
  • apps/radius/docker-compose.yml
  • apps/radius/freeradius/Dockerfile
  • apps/radius/freeradius/clients.conf
  • apps/radius/freeradius/default
  • apps/radius/freeradius/docker-entrypoint.sh
  • apps/radius/freeradius/eap
  • apps/radius/freeradius/eapol_test.conf
  • apps/radius/freeradius/inner-eap
  • apps/radius/freeradius/inner-tunnel
  • apps/radius/freeradius/rest
  • biome.jsonc
  • docs/architecture.md
  • docs/freeradius-setup.md
  • lefthook.yml
  • package.json
  • packages/api/package.json
  • packages/api/src/client/index.ts
  • packages/api/src/contract/admin-api-keys.ts
  • packages/api/src/contract/bug-report.ts
  • packages/api/src/contract/cohort.ts
  • packages/api/src/contract/dashboard.ts
  • packages/api/src/contract/doorlock.ts
  • packages/api/src/contract/health.ts
  • packages/api/src/contract/index.ts
  • packages/api/src/contract/kiosk.ts
  • packages/api/src/contract/navigator.ts
  • packages/api/src/contract/news.ts
  • packages/api/src/contract/notifications.ts
  • packages/api/src/contract/ping.ts
  • packages/api/src/contract/roles.ts
  • packages/api/src/contract/route.ts
  • packages/api/src/contract/timetable/import.ts
  • packages/api/src/contract/timetable/index.ts
  • packages/api/src/contract/timetable/lessons.ts
  • packages/api/src/contract/timetable/moved-lessons.ts
  • packages/api/src/contract/timetable/people.ts
  • packages/api/src/contract/timetable/substitutions.ts
  • packages/api/src/contract/timetable/timetables.ts
  • packages/api/src/contract/users.ts
  • packages/api/src/contract/wifi.ts
  • packages/api/src/domains/admin-api-keys.ts
  • packages/api/src/domains/api-keys.ts
  • packages/api/src/domains/bug-report.ts
  • packages/api/src/domains/cohort.ts
  • packages/api/src/domains/dashboard.ts
  • packages/api/src/domains/doorlock/cards.ts
  • packages/api/src/domains/doorlock/devices.ts
  • packages/api/src/domains/doorlock/logs.ts
  • packages/api/src/domains/doorlock/self.ts
  • packages/api/src/domains/doorlock/stats.ts
  • packages/api/src/domains/health.ts
  • packages/api/src/domains/kiosk/config.ts
  • packages/api/src/domains/kiosk/heartbeat.ts
  • packages/api/src/domains/kiosk/responses.ts
  • packages/api/src/domains/navigator/building.ts
  • packages/api/src/domains/navigator/classroom-type.ts
  • packages/api/src/domains/navigator/classroom.ts
  • packages/api/src/domains/navigator/corridor.ts
  • packages/api/src/domains/navigator/lift.ts
  • packages/api/src/domains/navigator/stair.ts
  • packages/api/src/domains/navigator/transfer.ts
  • packages/api/src/domains/navigator/translation.ts
  • packages/api/src/domains/news/announcements.ts
  • packages/api/src/domains/news/blogs.ts
  • packages/api/src/domains/news/system-messages.ts
  • packages/api/src/domains/notifications.ts
  • packages/api/src/domains/ping.ts
  • packages/api/src/domains/roles.ts
  • packages/api/src/domains/timetable/groups.ts
  • packages/api/src/domains/timetable/import.ts
  • packages/api/src/domains/timetable/lesson.ts
  • packages/api/src/domains/timetable/moved-lesson.ts
  • packages/api/src/domains/timetable/period.ts
  • packages/api/src/domains/timetable/room.ts
  • packages/api/src/domains/timetable/subject.ts
  • packages/api/src/domains/timetable/substitution.ts
  • packages/api/src/domains/timetable/teacher.ts
  • packages/api/src/domains/timetable/timetables.ts
  • packages/api/src/domains/users.ts
  • packages/api/src/domains/wifi/admin.ts
  • packages/api/src/domains/wifi/radius.ts
  • packages/api/src/domains/wifi/self.ts
  • packages/api/src/domains/wifi/stats.ts
  • packages/api/src/domains/wifi/status.ts
  • packages/api/src/envelope.ts
  • packages/api/src/errors.ts
  • packages/api/src/permissions.ts
  • packages/auth/package.json
  • packages/auth/src/client.ts
  • packages/auth/src/index.ts
  • packages/auth/tsconfig.json
  • packages/navigator-3d/package.json
  • packages/timetable-import/package.json
  • packages/ui/package.json
  • packages/ui/src/components/password-input.tsx
  • packages/ui/src/components/table.tsx
  • packages/ui/src/components/toggle-group.tsx
  • packages/ui/src/styles/globals.css

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The settings dialog and the `/settings` page duplicated the same preference
controls, and the API-key table needed ~900px — more than either surface had —
so it scrolled sideways on desktop and mobile alike.

One dialog now owns every setting, in the sidebar-in-dialog shape: a section
rail on the left, the active pane on the right, and on a phone the pane
replaces the rail with a back button. It is mounted once at the app root behind
a small store, so the profile menu and the notification viewer's "change
cohort" button open the same instance instead of mounting their own. The
`/settings` route and the duplicate my-groups card are gone.

The API-key list is a list, not a table: a row shows the name, state, prefix and
the three timestamps that matter, and rename/enable/delete moved into an
actions menu. Nothing overflows at any width.
The public timetable's control bar was a showcase of unrelated styles: three
button groups, a bespoke week pill, a print button in two different sizes, and
a view toggle that only existed in the URL. It is now one bar built from a
single segmented-control primitive (`ToggleGroup`), so every control shares one
box and reads as the same object.

- The filter, week and view controls are segments of one control language.
  `ToggleGroup` is a Base UI toggle group, so a single-choice group is a real
  radio group for keyboard and screen-reader users instead of buttons that
  merely look selected.
- The week selector keeps a colour cue for A/B — as a tint matching the grid's
  week badges, not the saturated fill it had.
- On a phone the bar is two rows: the entry type as icons with its picker, then
  week, timetable and print. Nothing scrolls sideways.
- The loading placeholder is shaped like the timetable it stands in for; the
  old `h-8 w-64` strip read as a second, empty toolbar. Its borders use
  `border-border` — a bare `border` resolves to `currentColor`, which drew a
  white outline around the whole card.

The view choice moved to a new Appearance settings pane, with the theme, and
became a real preference: a module-level store so flipping it re-renders the
timetable behind the dialog with no reload, a cookie so it applies before the
first paint (and to a signed-out visitor), and the stored preference so it
follows the account. It is no longer a `?view=` search param.

Also: the navbar's language selector is gone (language lives in General), the
API-key list is a list rather than a seven-column table that could not fit, and
`<html>` carries `suppressHydrationWarning` — next-themes sets `class` and
`color-scheme` on it before hydration, which React warned about on every load.
@nembenike

Copy link
Copy Markdown
Contributor

LGTM! merge and deploy

@merenyimiklos

Copy link
Copy Markdown
Contributor

Szép munka @nemvince , elég komoly átalakítás lett! 😄 Az új API-s megoldás és a modulárisabb felépítés szerintem jó irány.

Átnézve a változtatásokat, egy dolgot azért még érdemes lenne megnézni merge előtt:

API-kulcsok: A 0023-as migráció törli a régi api_key táblát. Ha jól látom, a meglévő kulcsok nem kerülnek át az új táblába. Ez nem fog problémát okozni az éles rendszerben, például a beléptetőknél vagy más API-t használó klienseknél?

Illetve az Androidos klienssel is érdemes lenne egy teljes tesztet futtatni, mert az API válaszformátuma is változott, és jó lenne biztosra menni, hogy a régebbi verziókkal sem lesz gond. @Dasa122 meg tudnád nézni?

@nemvince
nemvince marked this pull request as ready for review October 9, 2026 02:36
Copilot AI balanced review requested due to automatic review settings October 9, 2026 02:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@filc-preview-vm

filc-preview-vm Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

✅ Preview is up

Environment pr-401.preview.filc.petrik.hu
Commit 4adbe4f
URL https://pr-401.preview.filc.petrik.hu
Edge check unreachable (getaddrinfo ENOTFOUND pr-401.preview.filc.petrik.hu)

Posted by filc-deployer. The preview database is its own container and is
destroyed when this pull request closes.

@nemvince

nemvince commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

API-kulcsok: A 0023-as migráció törli a régi api_key táblát. Ha jól látom, a meglévő kulcsok nem kerülnek át az új táblába. Ez nem fog problémát okozni az éles rendszerben, például a beléptetőknél vagy más API-t használó klienseknél?

Ezek csak a user kulcsok, szerintem nincs eleg user hogy nagy baj legyen, max a power user/deveinknek kell uj kulcsot csinalnia, ez konnyebb mint full mas rendszerbe migralni a regi kulcsokat

Illetve az Androidos klienssel is érdemes lenne egy teljes tesztet futtatni, mert az API válaszformátuma is változott, és jó lenne biztosra menni, hogy a régebbi verziókkal sem lesz gond. @Dasa122 meg tudnád nézni?

Felpusholtam erre is egy branchet, pls check out!

@merenyimiklos

merenyimiklos commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

@nemvince nem uptodate a branch.. lebuildelt amúgy, de kéne egy frissebb rész.
image

This branch was successfully deployed

1 active deployment
preview-pr-401 — 4adbe4fd Deployed Oct 9, 2026 by filc-preview-vm[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Can't export substitutions Personal api key management menu

4 participants