feat(tourism): add tour crew assignments (leader/driver/guide)#7606
Conversation
Reviewer's GuideImplements tour crew assignments by adding a reusable TourGuidesField form section, wiring it into create/edit/duplicate flows, extending the tour detail type and GraphQL query to include guides, and enforcing validation of crew role and member selections. Sequence diagram for creating a tour with crew assignmentssequenceDiagram
actor User
participant TourCreateForm
participant TourGuidesField
participant ZodValidator
participant GraphQLClient
participant BackendAPI
User->>TourCreateForm: Open create tour page
TourCreateForm->>TourGuidesField: Render with control
User->>TourGuidesField: Click Add Crew Member
TourGuidesField->>TourGuidesField: useFieldArray.append { guideId: '', type: 'guide' }
User->>TourGuidesField: Select role (type)
TourGuidesField->>TourGuidesField: Update guides[index].type
User->>TourGuidesField: Select team member
TourGuidesField->>TourGuidesField: Update guides[index].guideId
User->>TourCreateForm: Submit form
TourCreateForm->>ZodValidator: Validate TourCreateFormSchema with guides
ZodValidator-->>TourCreateForm: guides[] valid or errors
alt Validation_fails
ZodValidator-->>TourGuidesField: Errors for role or team member
TourGuidesField-->>User: Show Role_is_required and Team_member_is_required
else Validation_succeeds
TourCreateForm->>GraphQLClient: mutate createTour with guides[{ guideId, type }]
GraphQLClient->>BackendAPI: POST createTour variables
BackendAPI-->>GraphQLClient: Created tour with guides
GraphQLClient-->>TourCreateForm: Response data
TourCreateForm-->>User: Navigate or show success
end
Sequence diagram for duplicating a tour including crew assignmentssequenceDiagram
actor User
participant TourDuplicateSheet
participant cloneTourGuides
participant GraphQLClient
participant BackendAPI
User->>TourDuplicateSheet: Open duplicate tour sheet
TourDuplicateSheet->>BackendAPI: Fetch existing tour detail (includes guides)
BackendAPI-->>TourDuplicateSheet: ITourDetail with guides[]
TourDuplicateSheet->>cloneTourGuides: Prepare guides for duplication
cloneTourGuides-->>TourDuplicateSheet: Array<{ guideId, type }>
User->>TourDuplicateSheet: Adjust crew assignments if needed
User->>TourDuplicateSheet: Confirm duplicate
TourDuplicateSheet->>GraphQLClient: mutate createTour with guides from cloneTourGuides
GraphQLClient->>BackendAPI: POST createTour variables including guides
BackendAPI-->>GraphQLClient: New duplicated tour with guides
GraphQLClient-->>TourDuplicateSheet: Response data
TourDuplicateSheet-->>User: Show duplicated tour
Class diagram for updated tour guide and crew assignment typesclassDiagram
class ITourGuide {
+string guideId
+string type
}
class ITourDetail {
+string _id
+string branchId
+string name
+number advancePercent
+string content
+number cost
+ITourGuide[] guides
}
class GuideFormValue {
+string guideId
+string type
}
class GuideSchema {
}
class GuideType {
}
class GUIDE_TYPES {
<<constant>>
+GuideType value
+string label
}
class GUIDE_TYPE_LABELS {
<<constant>>
+Record~string,string~ entries
}
class ICreateTourVariables {
+string branchId
+string name
+number groupSize
+number duration
+number cost
+Array guides
}
class IEditTourVariables {
+string _id
+string name
+number groupSize
+number duration
+number cost
+Array guides
}
class CreateTourGuideItem {
+string guideId
+string type
}
class EditTourGuideItem {
+string guideId
+string type
}
class TourGuidesField {
+Control control
+handleAdd()
}
class TourCreateForm {
+TourFormValues defaultValues
}
class TourEditForm {
+TourFormValues defaultValues
}
class TourFormValues {
+ITourGuide[] guides
}
ITourDetail "0..1" --> "0..*" ITourGuide : guides
GuideSchema <|-- GuideFormValue
GUIDE_TYPES --> GuideType
GUIDE_TYPE_LABELS --> GuideType
ICreateTourVariables "0..1" --> "0..*" CreateTourGuideItem : guides
IEditTourVariables "0..1" --> "0..*" EditTourGuideItem : guides
TourFormValues "0..1" --> "0..*" ITourGuide : guides
TourCreateForm --> TourFormValues
TourEditForm --> TourFormValues
TourCreateForm --> TourGuidesField
TourEditForm --> TourGuidesField
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
📝 WalkthroughWalkthroughThis PR adds guide and crew member management to the tour creation and editing workflows. It introduces a new ChangesTour Guide Management
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- Consider reusing the
cloneTourGuideshelper (or a shared normalizer) inTourEditFormwhen mappingtour.guidesso the normalization logic for guides/crew stays consistent in one place. - You introduced a
GuideTypeunion but theGuideSchema,ITourGuide, and form fields still use plainstringfortype; tightening these toGuideType(and possibly az.enum) would make role handling more type-safe and aligned withGUIDE_TYPES. - The guides/crew-related types in
useCreateTouranduseEditTourare manually duplicated; you might reduce drift by reusing a shared guide/crew type (e.g., derived fromGuideSchemaorITourGuide).
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider reusing the `cloneTourGuides` helper (or a shared normalizer) in `TourEditForm` when mapping `tour.guides` so the normalization logic for guides/crew stays consistent in one place.
- You introduced a `GuideType` union but the `GuideSchema`, `ITourGuide`, and form fields still use plain `string` for `type`; tightening these to `GuideType` (and possibly a `z.enum`) would make role handling more type-safe and aligned with `GUIDE_TYPES`.
- The guides/crew-related types in `useCreateTour` and `useEditTour` are manually duplicated; you might reduce drift by reusing a shared guide/crew type (e.g., derived from `GuideSchema` or `ITourGuide`).
## Individual Comments
### Comment 1
<location path="frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/formSchema.ts" line_range="111-113" />
<code_context>
/* ================= GUIDE ================= */
const GuideSchema = z.object({
- guideId: z.string(),
- name: z.string().optional(),
+ guideId: z.string().min(1, 'Team member is required'),
+ type: z.string().min(1, 'Role is required'),
});
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Constrain guide `type` to the known guide type values instead of any string
`type` is currently `z.string().min(1, ...)`, so any non-empty string passes. Since guide types are fixed in `GUIDE_TYPES`, use `z.enum([...])` (or a union derived from `GUIDE_TYPES`) so validation only accepts valid types and stays aligned with the select options.
Suggested implementation:
```typescript
guideId: z.string().min(1, 'Team member is required'),
type: z.enum(GUIDE_TYPES, {
required_error: 'Role is required',
invalid_type_error: 'Role is required',
}),
```
1. Ensure `GUIDE_TYPES` is imported into this file, e.g.:
`import { GUIDE_TYPES } from './path/to/guideConstants';`
(adjust the import path to match your existing constants).
2. `GUIDE_TYPES` must be a readonly tuple or array of string literals compatible with `z.enum`, e.g.
`export const GUIDE_TYPES = ['LEAD', 'ASSISTANT'] as const;`
so that `z.enum(GUIDE_TYPES, ...)` works correctly.
</issue_to_address>
### Comment 2
<location path="frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/guideTypes.ts" line_range="14-19" />
<code_context>
+
+export type GuideType = (typeof GUIDE_TYPES)[number]['value'];
+
+export const GUIDE_TYPE_LABELS: Record<string, string> = GUIDE_TYPES.reduce(
+ (acc, opt) => {
+ acc[opt.value] = opt.label;
+ return acc;
+ },
+ {} as Record<string, string>,
+);
</code_context>
<issue_to_address>
**suggestion:** Tighten the typing of `GUIDE_TYPE_LABELS` to the specific guide type union
You already export `GuideType = (typeof GUIDE_TYPES)[number]['value']`, but `GUIDE_TYPE_LABELS` is typed as `Record<string, string>`. Use `Record<GuideType, string>` for both the constant and the reducer accumulator so missing or extra keys are flagged at compile time.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const GuideSchema = z.object({ | ||
| guideId: z.string(), | ||
| name: z.string().optional(), | ||
| guideId: z.string().min(1, 'Team member is required'), | ||
| type: z.string().min(1, 'Role is required'), |
There was a problem hiding this comment.
suggestion (bug_risk): Constrain guide type to the known guide type values instead of any string
type is currently z.string().min(1, ...), so any non-empty string passes. Since guide types are fixed in GUIDE_TYPES, use z.enum([...]) (or a union derived from GUIDE_TYPES) so validation only accepts valid types and stays aligned with the select options.
Suggested implementation:
guideId: z.string().min(1, 'Team member is required'),
type: z.enum(GUIDE_TYPES, {
required_error: 'Role is required',
invalid_type_error: 'Role is required',
}),- Ensure
GUIDE_TYPESis imported into this file, e.g.:
import { GUIDE_TYPES } from './path/to/guideConstants';
(adjust the import path to match your existing constants). GUIDE_TYPESmust be a readonly tuple or array of string literals compatible withz.enum, e.g.
export const GUIDE_TYPES = ['LEAD', 'ASSISTANT'] as const;
so thatz.enum(GUIDE_TYPES, ...)works correctly.
| export const GUIDE_TYPE_LABELS: Record<string, string> = GUIDE_TYPES.reduce( | ||
| (acc, opt) => { | ||
| acc[opt.value] = opt.label; | ||
| return acc; | ||
| }, | ||
| {} as Record<string, string>, |
There was a problem hiding this comment.
suggestion: Tighten the typing of GUIDE_TYPE_LABELS to the specific guide type union
You already export GuideType = (typeof GUIDE_TYPES)[number]['value'], but GUIDE_TYPE_LABELS is typed as Record<string, string>. Use Record<GuideType, string> for both the constant and the reducer accumulator so missing or extra keys are flagged at compile time.
🌗 Pull Request OverviewThis PR implements tour crew assignment functionality for the tourism plugin, allowing users to assign team members (leaders, drivers, guides, etc.) to tours. It adds a new Reviewed Changes Show a summary per file
📋 Review Findings📄
|
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/guideTypes.ts (1)
1-20: 💤 Low valueLGTM – clean centralized definition;
as const+ indexed access type forGuideTypeis idiomatic.One optional improvement worth noting:
GuideTypeis exported but never applied back toITourGuide.type, the mutation variableguides[].type, orGUIDE_TYPE_LABELS— narrowing those toGuideType/Record<GuideType, string>would catch accidental invalid role strings at compile time.🤖 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 `@frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/guideTypes.ts` around lines 1 - 20, Update usages to tighten types: change the ITourGuide.type property and any mutable guides[].type to use the exported GuideType, and change GUIDE_TYPE_LABELS to Record<GuideType, string> so invalid role strings are caught at compile time; when updating GUIDE_TYPE_LABELS also adjust the reduce accumulator typing from Record<string,string> to Record<GuideType,string> (or initialize with an empty object cast as that type) to keep type safety consistent with GUIDE_TYPES.
🤖 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
`@frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/_components/TourCreateForm.tsx`:
- Around line 485-488: This component currently renders three hardcoded labels
("Crew", "Pricing Info", "More Info"); import useTranslation from
'react-i18next' at the top and call const { t } = useTranslation() inside the
TourCreateForm component, then replace the literal strings in the JSX with
translation calls (e.g., replace "Crew" with t('tms.tour.crew'), "Pricing Info"
with t('tms.tour.pricingInfo'), and "More Info" with t('tms.tour.moreInfo'));
ensure the import and const are added near existing imports and component
initialization so the references in the JSX (the div containing Form.Label and
the other label locations) resolve correctly.
---
Nitpick comments:
In
`@frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/guideTypes.ts`:
- Around line 1-20: Update usages to tighten types: change the ITourGuide.type
property and any mutable guides[].type to use the exported GuideType, and change
GUIDE_TYPE_LABELS to Record<GuideType, string> so invalid role strings are
caught at compile time; when updating GUIDE_TYPE_LABELS also adjust the reduce
accumulator typing from Record<string,string> to Record<GuideType,string> (or
initialize with an empty object cast as that type) to keep type safety
consistent with GUIDE_TYPES.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: a02756ca-d326-4914-a1ce-72eb006d8bd3
📒 Files selected for processing (10)
frontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/_components/TourCreateForm.tsxfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/_components/TourDuplicateSheet.tsxfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/_components/TourEditForm.tsxfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/_components/TourFormFields.tsxfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/formSchema.tsfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/constants/guideTypes.tsfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/graphql/queries.tsfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/hooks/useCreateTour.tsfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/hooks/useEditTour.tsfrontend/plugins/tourism_ui/src/modules/tms/branch-detail/dashboard/tours/hooks/useTourDetail.ts


Summary by Sourcery
Add support for assigning and managing tour crew roles (leader, driver, guide, etc.) on tours across create, edit, view, and duplicate flows.
New Features:
Enhancements:
Summary by CodeRabbit
Release Notes