Skip to content

only same lessons can be merged#196

Merged
nemvince merged 3 commits into
mainfrom
merged-lesson-update
May 23, 2026
Merged

only same lessons can be merged#196
nemvince merged 3 commits into
mainfrom
merged-lesson-update

Conversation

@KoZsombat
Copy link
Copy Markdown
Contributor

@KoZsombat KoZsombat commented May 22, 2026

Summary by CodeRabbit

  • New Features
    • Substitution dialog now supports selecting multiple merged/parallel teachers instead of a single inferred teacher.
    • Selection list shows a separate merged option for each available parallel teacher with clear merged labels.
    • Form submission correctly resolves merged selections so the chosen underlying teacher is applied.

Review Change Stack

@KoZsombat KoZsombat requested a review from nemvince May 22, 2026 20:03
@KoZsombat KoZsombat linked an issue May 22, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 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: 892edadb-208c-44a3-abc4-c20b06071512

📥 Commits

Reviewing files that changed from the base of the PR and between 107f4c7 and 58bd924.

📒 Files selected for processing (1)
  • apps/iris/src/components/admin/substitution-dialog.tsx
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: typecheck
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (4)
apps/{chronos,iris}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/instructions/filc-reuse.instructions.md)

apps/{chronos,iris}/src/**/*.{ts,tsx}: Reuse existing helpers, types, schemas, and hooks before adding new ones. Check nearby feature folders first, then shared files such as apps/chronos/src/database/helpers.ts, apps/iris/src/utils/query-keys.ts, apps/iris/src/hooks/use-has-permission.ts, apps/iris/src/components/admin/admin.types.ts, and apps/iris/src/components/doorlock/doorlock.types.ts
When a second call site needs the same logic, prefer extracting or extending the existing abstraction instead of creating a parallel helper with a slightly different name
Keep abstractions local to the narrowest shared boundary that already exists. Do not create cross-app utilities for one feature-specific use
Extend existing dialog props, response shapes, and query key families instead of re-declaring near-identical types in each file
Prefer the smallest root-cause fix that matches neighboring code over broad rewrites or speculative cleanup
Keep imports on the app alias boundary: #... for Chronos and @/... for Iris

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/{routes,components}/**/*.tsx

📄 CodeRabbit inference engine (.github/instructions/iris-data-flow.instructions.md)

apps/iris/src/{routes,components}/**/*.tsx: Always use centralized keys from apps/iris/src/utils/query-keys.ts for React Query. Do not introduce inline array query keys for existing domains
Use parseResponse(...) and the generated Hono client from apps/iris/src/utils/hc.ts for API requests when that is the local pattern
When a mutation changes server state, invalidate every affected query family, not just the page-local list. Follow the multi-invalidation pattern already used in admin news and doorlock screens
Reuse apps/iris/src/hooks/use-has-permission.ts and existing permission guard components instead of duplicating permission logic in the view
New user-facing error and success messages should go through t(...) and the locale files, even when surfaced through toasts

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/components/**/*dialog.tsx

📄 CodeRabbit inference engine (.github/instructions/iris-dialog-form.instructions.md)

apps/iris/src/components/**/*dialog.tsx: Follow the dialog structure used in files like card-dialog.tsx and user-dialog.tsx: create the form near the top of the component, derive reactive slices with useStore(form.store, selector), and render fields with <form.Field>{(field) => ...}</form.Field>
Reuse validation schemas from apps/iris/src/utils/form-schemas.ts when available. If a schema becomes shared by multiple dialogs, move it there instead of copying validation logic
form.reset takes raw values, not { values: ... }. Because form.reset and form.setFieldValue are not stable dependencies, do not add them to useEffect arrays when synchronizing dialog state
Reuse or extend shared dialog prop types such as admin.types.ts and doorlock.types.ts instead of defining near-duplicate props in each dialog
Keep submit side effects together: mutation success should close the dialog, invalidate the relevant query keys, and surface translated success or failure feedback
New labels, button text, placeholders, and empty states belong in t(...) and the locale files, even if older dialogs still have hardcoded strings

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/instructions/iris.instructions.md)

apps/iris/src/**/*.{ts,tsx}: Keep user-facing text in t(...) and update both locale trees under apps/iris/public/locales/en and apps/iris/public/locales/hu
TanStack Form is the default form pattern; follow examples with useForm, useStore(form.store, selector), and <form.Field>{(field) => ...}</form.Field>
form.reset(values) takes raw values, not { values }. form.reset and form.setFieldValue are not stable useEffect dependencies, so omit them from dependency arrays when needed
Base UI dropdown wrappers use onClick, not Radix-style onSelect, unless the local component explicitly exposes a different API
apps/iris/src/components/ui/chart.tsx already owns ResponsiveContainer; do not wrap chart children in another one
Keep public timetable filter state in TanStack Router search params instead of duplicating it in unrelated local state

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
🔇 Additional comments (2)
apps/iris/src/components/admin/substitution-dialog.tsx (2)

9-9: LGTM!


104-106: LGTM!

Also applies to: 173-193, 343-346


📝 Walkthrough

Walkthrough

This PR updates the substitution dialog to support multiple merged teachers: it computes a deduplicated parallelTeachers list from matching parallel lessons, renders one merged combobox option per teacher with values __merged__:<teacherId>, and on submit strips the __merged__: prefix to send the underlying teacher id.

Changes

Multiple Parallel Teachers Support

Layer / File(s) Summary
Import adjustment
apps/iris/src/components/admin/substitution-dialog.tsx
Removed unused React import useRef, keeping useEffect, useMemo, and useState.
Parallel teachers computation
apps/iris/src/components/admin/substitution-dialog.tsx
Replaced single derived parallelTeacher with parallelTeachers: filter parallelLessons by subject ids from selected availableLessons, then deduplicate teachers by id into a list.
Combobox options rendering
apps/iris/src/components/admin/substitution-dialog.tsx
Render one merged option per parallelTeachers entry with value __merged__:<teacherId> and label including merged tag and teacher name.
Form submission value resolution
apps/iris/src/components/admin/substitution-dialog.tsx
Update onSubmit to detect substituter values starting with __merged__: and strip the prefix to submit only the underlying teacher id.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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 accurately captures the main change: the substitution dialog now only merges teachers from parallel lessons with the same subject, enforcing the constraint that 'only same lessons can be merged'.
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.


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

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

🤖 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/substitution-dialog.tsx`:
- Line 101: Remove the unused indirection: delete the parallelTeachersRef
useRef<Map<string,string>> declaration and the effect that syncs it, then change
any code that looks up a teacherId via the map (used in onSubmit / handleSubmit
logic) to extract the id directly from the encoded value by parsing the
"__merged__:${teacherId}" string (e.g., value.startsWith("__merged__") ?
value.split("__:")[1] or value.replace("__merged__:", "") /
value.slice("__merged__:".length)). Update onSubmit/submit handler to use this
direct extraction for both single and parallel teacher selections and remove any
map lookups or related state synchronization.
🪄 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: adef8b81-9824-4072-af63-36f27dac1313

📥 Commits

Reviewing files that changed from the base of the PR and between 9bc05ad and f8a8d99.

📒 Files selected for processing (1)
  • apps/iris/src/components/admin/substitution-dialog.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: typecheck
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (4)
apps/{chronos,iris}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/instructions/filc-reuse.instructions.md)

apps/{chronos,iris}/src/**/*.{ts,tsx}: Reuse existing helpers, types, schemas, and hooks before adding new ones. Check nearby feature folders first, then shared files such as apps/chronos/src/database/helpers.ts, apps/iris/src/utils/query-keys.ts, apps/iris/src/hooks/use-has-permission.ts, apps/iris/src/components/admin/admin.types.ts, and apps/iris/src/components/doorlock/doorlock.types.ts
When a second call site needs the same logic, prefer extracting or extending the existing abstraction instead of creating a parallel helper with a slightly different name
Keep abstractions local to the narrowest shared boundary that already exists. Do not create cross-app utilities for one feature-specific use
Extend existing dialog props, response shapes, and query key families instead of re-declaring near-identical types in each file
Prefer the smallest root-cause fix that matches neighboring code over broad rewrites or speculative cleanup
Keep imports on the app alias boundary: #... for Chronos and @/... for Iris

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/{routes,components}/**/*.tsx

📄 CodeRabbit inference engine (.github/instructions/iris-data-flow.instructions.md)

apps/iris/src/{routes,components}/**/*.tsx: Always use centralized keys from apps/iris/src/utils/query-keys.ts for React Query. Do not introduce inline array query keys for existing domains
Use parseResponse(...) and the generated Hono client from apps/iris/src/utils/hc.ts for API requests when that is the local pattern
When a mutation changes server state, invalidate every affected query family, not just the page-local list. Follow the multi-invalidation pattern already used in admin news and doorlock screens
Reuse apps/iris/src/hooks/use-has-permission.ts and existing permission guard components instead of duplicating permission logic in the view
New user-facing error and success messages should go through t(...) and the locale files, even when surfaced through toasts

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/components/**/*dialog.tsx

📄 CodeRabbit inference engine (.github/instructions/iris-dialog-form.instructions.md)

apps/iris/src/components/**/*dialog.tsx: Follow the dialog structure used in files like card-dialog.tsx and user-dialog.tsx: create the form near the top of the component, derive reactive slices with useStore(form.store, selector), and render fields with <form.Field>{(field) => ...}</form.Field>
Reuse validation schemas from apps/iris/src/utils/form-schemas.ts when available. If a schema becomes shared by multiple dialogs, move it there instead of copying validation logic
form.reset takes raw values, not { values: ... }. Because form.reset and form.setFieldValue are not stable dependencies, do not add them to useEffect arrays when synchronizing dialog state
Reuse or extend shared dialog prop types such as admin.types.ts and doorlock.types.ts instead of defining near-duplicate props in each dialog
Keep submit side effects together: mutation success should close the dialog, invalidate the relevant query keys, and surface translated success or failure feedback
New labels, button text, placeholders, and empty states belong in t(...) and the locale files, even if older dialogs still have hardcoded strings

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/instructions/iris.instructions.md)

apps/iris/src/**/*.{ts,tsx}: Keep user-facing text in t(...) and update both locale trees under apps/iris/public/locales/en and apps/iris/public/locales/hu
TanStack Form is the default form pattern; follow examples with useForm, useStore(form.store, selector), and <form.Field>{(field) => ...}</form.Field>
form.reset(values) takes raw values, not { values }. form.reset and form.setFieldValue are not stable useEffect dependencies, so omit them from dependency arrays when needed
Base UI dropdown wrappers use onClick, not Radix-style onSelect, unless the local component explicitly exposes a different API
apps/iris/src/components/ui/chart.tsx already owns ResponsiveContainer; do not wrap chart children in another one
Keep public timetable filter state in TanStack Router search params instead of duplicating it in unrelated local state

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
🔇 Additional comments (2)
apps/iris/src/components/admin/substitution-dialog.tsx (2)

175-195: LGTM!


351-354: LGTM!

Comment thread apps/iris/src/components/admin/substitution-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/substitution-dialog.tsx (1)

186-186: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add null/undefined guard for lesson.teachers.

The code iterates lesson.teachers without checking if it exists or is an array. If the API returns a parallel lesson without a teachers property or with null/undefined, this will throw a TypeError and break the dialog.

🛡️ Proposed fix
     for (const teacher of lesson.teachers) {
+    for (const teacher of lesson.teachers ?? []) {
       if (!seen.has(teacher.id)) {
         seen.set(teacher.id, { id: teacher.id, name: teacher.name });
       }
     }
🤖 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/substitution-dialog.tsx` at line 186, The loop
"for (const teacher of lesson.teachers)" can throw if lesson.teachers is
null/undefined or not an array; update the surrounding code in
substitution-dialog.tsx to guard or normalize before iterating (e.g., check
Array.isArray(lesson.teachers) and skip the loop when false, or assign const
teachers = Array.isArray(lesson.teachers) ? lesson.teachers : [] and iterate
over teachers) so parallel lessons without a teachers property no longer cause a
TypeError.
🤖 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/substitution-dialog.tsx`:
- Line 186: The loop "for (const teacher of lesson.teachers)" can throw if
lesson.teachers is null/undefined or not an array; update the surrounding code
in substitution-dialog.tsx to guard or normalize before iterating (e.g., check
Array.isArray(lesson.teachers) and skip the loop when false, or assign const
teachers = Array.isArray(lesson.teachers) ? lesson.teachers : [] and iterate
over teachers) so parallel lessons without a teachers property no longer cause a
TypeError.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c4c1e61e-e774-4422-bdfa-84a9cdd9b0df

📥 Commits

Reviewing files that changed from the base of the PR and between f8a8d99 and 107f4c7.

📒 Files selected for processing (1)
  • apps/iris/src/components/admin/substitution-dialog.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (4)
apps/{chronos,iris}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/instructions/filc-reuse.instructions.md)

apps/{chronos,iris}/src/**/*.{ts,tsx}: Reuse existing helpers, types, schemas, and hooks before adding new ones. Check nearby feature folders first, then shared files such as apps/chronos/src/database/helpers.ts, apps/iris/src/utils/query-keys.ts, apps/iris/src/hooks/use-has-permission.ts, apps/iris/src/components/admin/admin.types.ts, and apps/iris/src/components/doorlock/doorlock.types.ts
When a second call site needs the same logic, prefer extracting or extending the existing abstraction instead of creating a parallel helper with a slightly different name
Keep abstractions local to the narrowest shared boundary that already exists. Do not create cross-app utilities for one feature-specific use
Extend existing dialog props, response shapes, and query key families instead of re-declaring near-identical types in each file
Prefer the smallest root-cause fix that matches neighboring code over broad rewrites or speculative cleanup
Keep imports on the app alias boundary: #... for Chronos and @/... for Iris

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/{routes,components}/**/*.tsx

📄 CodeRabbit inference engine (.github/instructions/iris-data-flow.instructions.md)

apps/iris/src/{routes,components}/**/*.tsx: Always use centralized keys from apps/iris/src/utils/query-keys.ts for React Query. Do not introduce inline array query keys for existing domains
Use parseResponse(...) and the generated Hono client from apps/iris/src/utils/hc.ts for API requests when that is the local pattern
When a mutation changes server state, invalidate every affected query family, not just the page-local list. Follow the multi-invalidation pattern already used in admin news and doorlock screens
Reuse apps/iris/src/hooks/use-has-permission.ts and existing permission guard components instead of duplicating permission logic in the view
New user-facing error and success messages should go through t(...) and the locale files, even when surfaced through toasts

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/components/**/*dialog.tsx

📄 CodeRabbit inference engine (.github/instructions/iris-dialog-form.instructions.md)

apps/iris/src/components/**/*dialog.tsx: Follow the dialog structure used in files like card-dialog.tsx and user-dialog.tsx: create the form near the top of the component, derive reactive slices with useStore(form.store, selector), and render fields with <form.Field>{(field) => ...}</form.Field>
Reuse validation schemas from apps/iris/src/utils/form-schemas.ts when available. If a schema becomes shared by multiple dialogs, move it there instead of copying validation logic
form.reset takes raw values, not { values: ... }. Because form.reset and form.setFieldValue are not stable dependencies, do not add them to useEffect arrays when synchronizing dialog state
Reuse or extend shared dialog prop types such as admin.types.ts and doorlock.types.ts instead of defining near-duplicate props in each dialog
Keep submit side effects together: mutation success should close the dialog, invalidate the relevant query keys, and surface translated success or failure feedback
New labels, button text, placeholders, and empty states belong in t(...) and the locale files, even if older dialogs still have hardcoded strings

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
apps/iris/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/instructions/iris.instructions.md)

apps/iris/src/**/*.{ts,tsx}: Keep user-facing text in t(...) and update both locale trees under apps/iris/public/locales/en and apps/iris/public/locales/hu
TanStack Form is the default form pattern; follow examples with useForm, useStore(form.store, selector), and <form.Field>{(field) => ...}</form.Field>
form.reset(values) takes raw values, not { values }. form.reset and form.setFieldValue are not stable useEffect dependencies, so omit them from dependency arrays when needed
Base UI dropdown wrappers use onClick, not Radix-style onSelect, unless the local component explicitly exposes a different API
apps/iris/src/components/ui/chart.tsx already owns ResponsiveContainer; do not wrap chart children in another one
Keep public timetable filter state in TanStack Router search params instead of duplicating it in unrelated local state

Files:

  • apps/iris/src/components/admin/substitution-dialog.tsx
🔇 Additional comments (3)
apps/iris/src/components/admin/substitution-dialog.tsx (3)

9-9: LGTM!


104-105: LGTM!


343-346: LGTM!

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026
@nemvince nemvince merged commit a0a0725 into main May 23, 2026
7 checks passed
@nemvince nemvince deleted the merged-lesson-update branch May 23, 2026 10:33
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Filc Issue Tracker May 23, 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.

Óra összevonás

2 participants