Skip to content

Timetable awareness#160

Open
nemvince wants to merge 6 commits intomainfrom
timetable-awareness
Open

Timetable awareness#160
nemvince wants to merge 6 commits intomainfrom
timetable-awareness

Conversation

@nemvince
Copy link
Member

@nemvince nemvince commented Mar 13, 2026

Closes #135

Copilot AI review requested due to automatic review settings March 13, 2026 12:16
@filc-coolify-deploy
Copy link

filc-coolify-deploy bot commented Mar 13, 2026

The preview deployment for iris is ready. 🟢

Open Preview | Open Build Logs | Open Application Logs

Last updated at: 2026-03-13 12:47:46 CET

@filc-coolify-deploy
Copy link

filc-coolify-deploy bot commented Mar 13, 2026

The preview deployment for chronos is ready. 🟢

Open Preview | Open Build Logs | Open Application Logs

Last updated at: 2026-03-13 12:47:38 CET

Copilot AI review requested due to automatic review settings March 13, 2026 12:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds multi-timetable support across Iris and Chronos by introducing timetable selection in the public timetable view and a new admin UI/API for managing imported timetables, while also consolidating common i18n strings and improving OpenAPI metadata for several endpoints.

Changes:

  • Iris: add timetable selection (URL + UI) and an admin “Manage timetables” page (edit/delete).
  • Chronos: add PATCH/DELETE timetable management endpoints and adjust timetable/cohort import & access patterns.
  • General: move repeated UI strings to common.* translations and enrich OpenAPI route parameter/response schemas.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
apps/iris/src/routes/_public/index.tsx Adds timetable to validated search params for the public timetable route.
apps/iris/src/routes/_private/admin/timetable/substitutions.tsx Switches some labels to common.* translation keys.
apps/iris/src/routes/_private/admin/timetable/moved-lessons.tsx Switches some labels to common.* translation keys.
apps/iris/src/routes/_private/admin/timetable/manage.tsx New admin page to list/edit/delete timetables.
apps/iris/src/routes/_private/admin/news/announcements.tsx Switches some labels to common.* translation keys.
apps/iris/src/route-tree.gen.ts Generated route-tree update to include the new manage page route.
apps/iris/src/hooks/use-selected-timetable.ts New hook to fetch/sort timetables and choose an effective selection.
apps/iris/src/components/timetable/index.tsx Adds timetable selection + URL sync; scopes cohorts query by timetable.
apps/iris/src/components/timetable/filter-bar.tsx Adds a timetable selector UI to the timetable filter bar.
apps/iris/src/components/admin/substitution-dialog.tsx Uses common.date / common.save for some dialog labels.
apps/iris/src/components/admin/sidebar.tsx Adds a sidebar link to the new “Manage timetables” admin page.
apps/iris/src/components/admin/roles-table.tsx Uses common.actions / common.delete for roles table labels.
apps/iris/src/components/admin/role-dialog.tsx Uses common.save when editing an existing role.
apps/iris/src/components/admin/moved-lesson-dialog.tsx Uses common.date / common.save for some dialog labels.
apps/iris/src/components/admin/announcements-dialog.tsx Uses common.save for the submit button label.
apps/iris/public/locales/hu/translation.json Adds new common.* keys and new timetable.* keys; removes duplicates from feature sections.
apps/iris/public/locales/en/translation.json Adds new common.* keys and new timetable.* keys; removes duplicates from feature sections.
apps/chronos/src/routes/users/index.ts Adds OpenAPI parameter docs; validates id via zod param validator.
apps/chronos/src/routes/timetable/manage.ts New timetable update/delete endpoints (PATCH/DELETE).
apps/chronos/src/routes/timetable/import.ts Coerces validFrom to Date in multipart form schema.
apps/chronos/src/routes/timetable/cohort.ts Simplifies query and relaxes timetableId validation to string.
apps/chronos/src/routes/timetable/_router.ts Wires new timetable PATCH/DELETE endpoints into the router.
apps/chronos/src/routes/roles/index.ts Improves OpenAPI schemas for roles/permissions CRUD routes.
apps/chronos/src/routes/news/system-messages.ts Adds OpenAPI parameter docs for pagination/id.
apps/chronos/src/routes/news/blogs.ts Adds OpenAPI parameter docs for pagination/slug/id.
apps/chronos/src/routes/news/announcements.ts Adds OpenAPI parameter docs for pagination/id/includeExpired.
apps/chronos/src/routes/doorlock/websocket-handler.ts Adds OpenAPI metadata for WS upgrade route.
apps/chronos/src/routes/doorlock/stats.ts Adds OpenAPI path parameter docs for device stats.
apps/chronos/src/routes/doorlock/self.ts Adds OpenAPI path parameter docs for card operations.
apps/chronos/src/routes/doorlock/ota.ts Adds OpenAPI path parameter docs for triggering OTA.
apps/chronos/src/routes/doorlock/devices.ts Adds OpenAPI path parameter docs for device update/delete.
apps/chronos/src/routes/doorlock/cards.ts Adds OpenAPI path parameter docs for card update/delete.
.devcontainer/Dockerfile Adds ripgrep to the devcontainer image.

You can also share your feedback on Copilot code review. Take the survey.

updateMutation.mutate({
id: editTarget.id,
name: editName.trim(),
validFrom: editValidFrom ? dateToYYYYMMDD(editValidFrom) : '',
Comment on lines +109 to 129
// Timetable selection
const {
currentTimetableId,
isLoading: timetablesLoading,
selectedTimetableId,
setSelectedTimetableId,
timetables,
} = useSelectedTimetable(search.timetable);

// Queries — cohorts are now scoped to the selected timetable
const cohortsQuery = useQuery({
...QUERY_OPTIONS,
queryFn: fetchCohorts,
queryKey: ['cohorts'],
enabled: !!selectedTimetableId,
queryFn: () => {
if (!selectedTimetableId) {
return Promise.resolve([] as CohortItem[]);
}
return fetchCohortsForTimetable(selectedTimetableId);
},
queryKey: ['cohorts', selectedTimetableId],
});
Comment on lines +73 to +79
const [selectedId, setSelectedId] = useState<string | null>(
initialTimetableId ?? null
);

// Effective selection: use explicit selection, or fall back to current
const effectiveId = selectedId ?? currentTimetableId;

Comment on lines 45 to 58
@@ -54,8 +54,7 @@ export const getCohortsForTimetable = timetableFactory.createHandlers(
const cohorts = await db
.select()
.from(cohort)
.leftJoin(timetable, eq(cohort.timetableId, timetable.id))
.where(eq(timetable.id, timetableId));
.where(eq(cohort.timetableId, timetableId));

Comment on lines +57 to +66
tags: ['Timetable'],
}),
zValidator('param', z.object({ id: z.string() })),
zValidator('json', updateSchema),
requireAuthentication,
requireAuthorization('import:timetable'),
async (c) => {
const { id } = c.req.valid('param');
const body = c.req.valid('json');

Comment on lines +19 to +21
const updateSchema = z.object({
name: z.string().optional(),
validFrom: z.string().optional(),
Comment on lines +179 to +196
// Reset selections when timetable changes
useEffect(() => {
if (!initialized) {
previousTimetableIdRef.current = selectedTimetableId ?? null;
return;
}

const previousTimetableId = previousTimetableIdRef.current;
const nextTimetableId = selectedTimetableId ?? null;

if (previousTimetableId === nextTimetableId) {
return;
}

previousTimetableIdRef.current = nextTimetableId;
setSelections({ class: null, classroom: null, teacher: null });
setInitialized(false);
}, [initialized, selectedTimetableId]);
Comment on lines +195 to +204
const formatDate = (dateStr: string | null) => {
if (!dateStr) {
return '—';
}
return new Date(dateStr).toLocaleDateString(undefined, {
day: 'numeric',
month: 'short',
year: 'numeric',
});
};
Comment on lines +119 to +127
const formatTimetableLabel = (tt: TimetableItem): string => {
if (tt.validFrom) {
const date = new Date(tt.validFrom);
const formatted = date.toLocaleDateString(undefined, {
day: 'numeric',
month: 'short',
year: 'numeric',
});
return `${tt.name} (${formatted})`;
Comment on lines +73 to +75
const [selectedId, setSelectedId] = useState<string | null>(
initialTimetableId ?? null
);
Copy link
Contributor

@hrustinszkiadam hrustinszkiadam left a comment

Choose a reason for hiding this comment

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

Lgtm

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.

make the frontend timetable aware

3 participants