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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

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
Copy Markdown
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
Copy Markdown
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

@nemvince
Copy link
Copy Markdown
Member Author

@busyLambda could you take a look at the slopbot suggestions please?

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