Skip to content

timetable import dialog#187

Merged
nemvince merged 6 commits into
mainfrom
timetable-import
May 20, 2026
Merged

timetable import dialog#187
nemvince merged 6 commits into
mainfrom
timetable-import

Conversation

@KoZsombat
Copy link
Copy Markdown
Contributor

@KoZsombat KoZsombat commented May 20, 2026

Summary by CodeRabbit

  • New Features

    • Timetable items can have editable names that are saved.
    • Timetable import dialog: upload XML via drag-and-drop or picker, provide import name and validity dates, client-side validation, progress/status, success/error feedback, and disabled Import while uploading or when required fields are missing.
  • Refactor

    • Timetable import now lives inside the management interface; the old standalone import page redirects to management.
  • UI

    • Admin sidebar no longer shows a separate timetable "import" menu entry.

Review Change Stack

@KoZsombat KoZsombat requested a review from nemvince May 20, 2026 09:49
@KoZsombat KoZsombat linked an issue May 20, 2026 that may be closed by this pull request
KoZsombat and others added 2 commits May 20, 2026 11:50
# Conflicts:
#	apps/iris/src/routes/_private/admin/timetable/import.tsx
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a346350-214e-41b9-9421-0643bc0eed52

📥 Commits

Reviewing files that changed from the base of the PR and between 27a3d2d and e302913.

📒 Files selected for processing (1)
  • apps/iris/src/utils/form-schemas.ts

📝 Walkthrough

Walkthrough

Backend update accepts optional timetable name. Frontend adds name editing to the edit dialog, introduces a new TimetableImportDialog, integrates it into the manage page with an import button, and replaces the standalone import route and sidebar import item with a redirect and menu cleanup.

Changes

Timetable Management Features

Layer / File(s) Summary
Backend timetable name schema and persistence
apps/chronos/src/routes/timetable/index.ts
updateTimetable request schema and database persistence extended to optionally accept and write name field.
Edit dialog with name field support
apps/iris/src/components/admin/timetable-edit-dialog.tsx
TimetableEditDialog adds controlled name state, labeled text Input, resets on open, and includes name (trimmed / empty→undefined) in submit payload.
New import dialog component
apps/iris/src/components/admin/timetable-import-dialog.tsx
TimetableImportDialog implements XML file upload with drag-and-drop, name and date inputs, MIME/extension validation, React Query import mutation, status/error UI, and query key invalidation on success.
Manage page dialog integration and name handling
apps/iris/src/routes/_private/admin/timetable/manage.tsx
Manage route imports TimetableImportDialog, adds importDialogOpen state, renders an import button in the header to open the dialog, and updates mutation/edit payload types to include optional name.
Route redirect and sidebar menu cleanup
apps/iris/src/routes/_private/admin/timetable/import.tsx, apps/iris/src/components/admin/sidebar.tsx
Dedicated import route replaced with a beforeLoad redirect to /admin/timetable/manage; sidebar removal of Calendar icon import and deletion of the timetable import menu item.
Import form schema update
apps/iris/src/utils/form-schemas.ts
timetableImportSchema now requires a file property validated as a File instance via z.instanceof(File).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • busyLambda
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'timetable import dialog' directly corresponds to the primary change: adding a TimetableImportDialog component and related import functionality to the timetable management system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

nemvince
nemvince previously approved these changes May 20, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/iris/src/components/admin/timetable-edit-dialog.tsx`:
- Around line 73-79: The Label and Input need to be programmatically associated
for accessibility: add a stable id (e.g., "timetable-name") to the Input
component and set the Label's htmlFor prop to that same id so screen readers and
form tools can link them; update the JSX around Label and Input (the elements
currently using setName and value={name}) to include these props and keep
existing onChange/value behavior intact.

In `@apps/iris/src/components/admin/timetable-import-dialog.tsx`:
- Around line 35-143: The component TimetableImportDialog currently uses local
useState hooks (selectedFile, importName, validStartDate, validEndDate,
importStatus, errorMessage) and imperative handlers (validateAndSetFile,
handleFileSelect, handleImport, resetForm) — refactor it to TanStack Form:
create a form via useForm(...) with fields for name, file, validFrom, validTo
and move validation into the form schema; replace direct state reads with
reactive selectors using useStore(form.store, selector) for each slice instead
of the individual useState hooks; render inputs using <form.Field>{(field) =>
...}</form.Field> so the dialog UI binds to the form fields; update
importMutation to read values from form.getValues()/onSubmit and call mutate in
the form submit handler; and remove/reset local state by using form.reset() and
the form store selectors instead of resetForm/imperative fileInput manipulation.
- Around line 44-45: The form shows a date (DatePicker uses validStartDate ??
new Date()) while validation checks only validStartDate, causing submit to
remain disabled; update the validation used in the submit/required checks to use
the same effective value as the DatePicker (i.e., use (validStartDate ?? new
Date()) and (validEndDate ?? new Date()) or alternatively initialize
validStartDate/validEndDate to new Date()) so the displayed date and the
validation source match; apply the same change to the other occurrences
referenced (around the other DatePicker usages at the indicated locations).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9eda90b8-38ec-4feb-98cc-c8df94dafd0f

📥 Commits

Reviewing files that changed from the base of the PR and between 2d264d4 and de0f6e6.

📒 Files selected for processing (6)
  • apps/chronos/src/routes/timetable/index.ts
  • apps/iris/src/components/admin/sidebar.tsx
  • apps/iris/src/components/admin/timetable-edit-dialog.tsx
  • apps/iris/src/components/admin/timetable-import-dialog.tsx
  • apps/iris/src/routes/_private/admin/timetable/import.tsx
  • apps/iris/src/routes/_private/admin/timetable/manage.tsx

Comment thread apps/iris/src/components/admin/timetable-edit-dialog.tsx Outdated
Comment thread apps/iris/src/components/admin/timetable-import-dialog.tsx
Comment thread apps/iris/src/components/admin/timetable-import-dialog.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/iris/src/components/admin/timetable-edit-dialog.tsx (1)

17-27: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Migrate to TanStack Form pattern to match reference dialogs.

Both card-dialog.tsx and user-dialog.tsx use useForm, useStore(form.store, selector), and <form.Field> as prescribed by coding guidelines. This dialog uses plain useState/useEffect instead. Refactor to follow the TanStack Form pattern for consistency and to comply with the mandatory dialog structure guideline.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/iris/src/components/admin/timetable-edit-dialog.tsx` around lines 17 -
27, The TimetableEditDialog currently uses local state/effects instead of the
project's TanStack Form pattern; refactor the component that uses
TimetableEditDialogProps to create a form via useForm(...) (with initial
defaultValues derived from item), read form values with useStore(form.store,
selector) where needed, replace existing input wiring with <form.Field>
components bound to the form, and wire submission to form.handleSubmit to call
the existing onSubmit(payload) with the mapped {name, validFrom, validTo} shape;
ensure you remove useState/useEffect used for form fields and keep the prop
names (item, isSubmitting, open, onOpenChange, onSubmit) unchanged.
♻️ Duplicate comments (1)
apps/iris/src/components/admin/timetable-import-dialog.tsx (1)

44-46: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

validStartDate default is still inconsistent after reset.

Line 44 initializes correctly, but Line 98 resets to undefined while Line 174 still renders a fallback date and Line 292 requires validStartDate truthy. After reopen, users can see a date but cannot submit until re-selecting it.

Suggested minimal fix
-const [validStartDate, setValidStartDate] = useState<Date | undefined>(
-  new Date()
-);
+const [validStartDate, setValidStartDate] = useState<Date>(new Date());
...
-    setValidStartDate(undefined);
+    setValidStartDate(new Date());
...
-            <DatePicker
-              date={validStartDate ?? new Date()}
+            <DatePicker
+              date={validStartDate}
               onDateChange={setValidStartDate}
               placeholder={t('timetable.validFromPlaceholder')}
             />
...
-    if (!(selectedFile && importName.trim() && validStartDate)) {
+    if (!(selectedFile && importName.trim())) {
       return;
     }
...
-                !(selectedFile && importName.trim() && validStartDate) ||
+                !(selectedFile && importName.trim()) ||
                 importStatus === 'uploading'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/iris/src/components/admin/timetable-import-dialog.tsx` around lines 44 -
46, The issue is that validStartDate is initialized to new Date() but reset to
undefined, causing the picker to show a fallback date while submit logic (and
rendering) expects a truthy date; update the reset logic to
setValidStartDate(new Date()) (instead of undefined) so the state, the date
renderer (where validStartDate is used) and the submit validation (where a
truthy validStartDate is required) remain consistent across open/reset/reopen;
also verify any place that clears the picker uses setValidStartDate(new Date())
or explicitly handles undefined in the submit path (prefer keeping the single
default behavior via setValidStartDate).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/iris/src/components/admin/timetable-edit-dialog.tsx`:
- Around line 17-27: The TimetableEditDialog currently uses local state/effects
instead of the project's TanStack Form pattern; refactor the component that uses
TimetableEditDialogProps to create a form via useForm(...) (with initial
defaultValues derived from item), read form values with useStore(form.store,
selector) where needed, replace existing input wiring with <form.Field>
components bound to the form, and wire submission to form.handleSubmit to call
the existing onSubmit(payload) with the mapped {name, validFrom, validTo} shape;
ensure you remove useState/useEffect used for form fields and keep the prop
names (item, isSubmitting, open, onOpenChange, onSubmit) unchanged.

---

Duplicate comments:
In `@apps/iris/src/components/admin/timetable-import-dialog.tsx`:
- Around line 44-46: The issue is that validStartDate is initialized to new
Date() but reset to undefined, causing the picker to show a fallback date while
submit logic (and rendering) expects a truthy date; update the reset logic to
setValidStartDate(new Date()) (instead of undefined) so the state, the date
renderer (where validStartDate is used) and the submit validation (where a
truthy validStartDate is required) remain consistent across open/reset/reopen;
also verify any place that clears the picker uses setValidStartDate(new Date())
or explicitly handles undefined in the submit path (prefer keeping the single
default behavior via setValidStartDate).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c91d708f-40fa-4de3-8440-9a765ae482f5

📥 Commits

Reviewing files that changed from the base of the PR and between de0f6e6 and c4d4c0d.

📒 Files selected for processing (2)
  • apps/iris/src/components/admin/timetable-edit-dialog.tsx
  • apps/iris/src/components/admin/timetable-import-dialog.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/iris/src/components/admin/timetable-import-dialog.tsx (2)

66-74: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid surfacing non-localized raw error text to users.

throw new Error('Failed to import timetable') can flow to AlertDescription via error.message, which bypasses localization. Use translated UI-safe text for displayed errors (and keep raw details out of user-facing strings).

As per coding guidelines, "New user-facing error and success messages should go through t(...) and the locale files, even when surfaced through toasts".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/iris/src/components/admin/timetable-import-dialog.tsx` around lines 66 -
74, The code currently throws a raw Error('Failed to import timetable') and then
surfaces error.message to the UI in the onError block; replace this flow so
user-facing text always uses localization. At the throw site (where
Error('Failed to import timetable') is created) either throw a non-user-facing
error (with a machine code or leave the original message for logs only) or
return a rejected value, and in the onError handler use t(...) for UI strings
(replace setErrorMessage(error.message) and toast.error(...) with
setErrorMessage(t('timetable.importError')) /
toast.error(t('timetable.importError')) or a localized detailed message key),
ensuring only localized strings reach AlertDescription and other UI components
while raw error details are kept for logs only.

326-345: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit button types and localize the error message displayed in the alert.

The Import button without type="submit" will default to form submission (standard HTML behavior) and the onClick={() => form.handleSubmit()} is redundant since the form's onSubmit already calls form.handleSubmit(). The Cancel button similarly needs type="button" to prevent unintended form submission. Additionally, line 322 displays {errorMessage} (the raw hardcoded English error string) in the Alert; this should use the localized key instead, consistent with how the toast surfaces t('timetable.importError').

Suggested fix pattern
<Button
  className="flex-1"
  disabled={!canImport}
  type="submit"
>
  {/* ... */}
</Button>
<Button onClick={() => handleOpenChange(false)} type="button" variant="outline">
  {t('common.cancel')}
</Button>

And in the error alert:

<AlertDescription>{t('timetable.importError')}</AlertDescription>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/iris/src/components/admin/timetable-import-dialog.tsx` around lines 326
- 345, The Import and Cancel buttons in the timetable import dialog should use
explicit types and the alert should use the localized error string: remove the
redundant onClick that calls form.handleSubmit and set the Import <Button> to
type="submit" so the form's onSubmit triggers form.handleSubmit; set the Cancel
<Button> to type="button" and keep its onClick calling handleOpenChange(false);
and replace the raw errorMessage shown in <AlertDescription> with the localized
key via t('timetable.importError') so the Alert uses the translated message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/iris/src/utils/form-schemas.ts`:
- Line 66: The schema's file field currently uses z.instanceof(File).nullable(),
allowing null to pass validation and causing the onSubmit handler's guard (if
(!file) return) to silently abort; update the schema's file field to require a
File (remove .nullable() or add a .refine(...) that enforces non-null and a
valid File) so validation fails when no file is provided, and ensure any error
messages propagate to the submit handler/UI; target the "file" schema entry in
apps/iris/src/utils/form-schemas.ts (the z.instanceof(File).nullable() symbol)
and adjust it rather than relying solely on the onSubmit check.

---

Outside diff comments:
In `@apps/iris/src/components/admin/timetable-import-dialog.tsx`:
- Around line 66-74: The code currently throws a raw Error('Failed to import
timetable') and then surfaces error.message to the UI in the onError block;
replace this flow so user-facing text always uses localization. At the throw
site (where Error('Failed to import timetable') is created) either throw a
non-user-facing error (with a machine code or leave the original message for
logs only) or return a rejected value, and in the onError handler use t(...) for
UI strings (replace setErrorMessage(error.message) and toast.error(...) with
setErrorMessage(t('timetable.importError')) /
toast.error(t('timetable.importError')) or a localized detailed message key),
ensuring only localized strings reach AlertDescription and other UI components
while raw error details are kept for logs only.
- Around line 326-345: The Import and Cancel buttons in the timetable import
dialog should use explicit types and the alert should use the localized error
string: remove the redundant onClick that calls form.handleSubmit and set the
Import <Button> to type="submit" so the form's onSubmit triggers
form.handleSubmit; set the Cancel <Button> to type="button" and keep its onClick
calling handleOpenChange(false); and replace the raw errorMessage shown in
<AlertDescription> with the localized key via t('timetable.importError') so the
Alert uses the translated message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ee0e0891-ebd2-4421-8f51-dc7e1aa45b49

📥 Commits

Reviewing files that changed from the base of the PR and between c4d4c0d and 27a3d2d.

📒 Files selected for processing (2)
  • apps/iris/src/components/admin/timetable-import-dialog.tsx
  • apps/iris/src/utils/form-schemas.ts

Comment thread apps/iris/src/utils/form-schemas.ts Outdated
@nemvince nemvince merged commit 6f4248d into main May 20, 2026
7 checks passed
@nemvince nemvince deleted the timetable-import branch May 20, 2026 13:16
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Filc Issue Tracker May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Refactor timetable import flow

2 participants