From 2eb5359d82e5712b33d4f3f5130c7818c4c87694 Mon Sep 17 00:00:00 2001 From: Arthur Bassi <201806768+bassi-arthurr@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:12:13 -0300 Subject: [PATCH 01/14] fix(work-orders): address wizard create review findings [recover] remove malicious eslint payload (was 166c63e4) --- .../detail/use-work-order-slide-over.ts | 3 +- .../wizard/mini-calendar-cell-style.ts | 71 +++++++ .../_components/wizard/mini-calendar.tsx | 40 ++-- .../_components/wizard/new-wo-wizard.tsx | 201 +++++++----------- .../wizard/use-new-wo-wizard-draft.ts | 49 +++++ .../wizard/use-wizard-date-field-state.ts | 87 ++++++++ .../wizard/use-wizard-poc-autofill.ts | 51 +++++ .../wizard/wizard-date-field-trigger.tsx | 68 ++++++ .../_components/wizard/wizard-date-field.tsx | 150 +++++++------ .../wizard/wizard-date-mode-tabs.tsx | 57 +++++ .../wizard/wizard-date-range-panel.tsx | 65 ++++++ .../wizard/wizard-field-select-options.tsx | 86 ++++++++ .../wizard/wizard-field-select-types.ts | 9 + .../wizard/wizard-field-select.tsx | 102 +++------ .../wizard/wizard-sidebar-summary-rows.ts | 45 ++++ .../wizard/wizard-sidebar-summary.tsx | 82 +++++++ .../_components/wizard/wizard-sidebar.tsx | 116 +--------- .../_components/wizard/wizard-step-header.tsx | 42 ++++ .../wizard-step-location-poc-fields.tsx | 51 +++++ .../wizard-step-location-service-select.tsx | 109 ++++++++++ .../wizard/wizard-step-location-service.tsx | 178 ++++------------ .../wizard-step-location-site-details.tsx | 88 ++++++++ .../wizard-step-location-site-select.tsx | 60 ++++++ .../wizard/wizard-step-type-schedule.tsx | 60 +++++- .../wizard/wizard-step-vendor-time.tsx | 53 +++-- .../_components/work-order-form.tsx | 24 ++- .../mappers/assign-wizard-schedule-fields.ts | 8 +- .../use-wizard-poc-autofill.test.tsx | 78 +++++++ .../wizard-schedule-date-range.test.tsx | 110 ++++++++++ .../wizard-service-clears-vendor.test.tsx | 48 +++++ .../mappers/work-order-board-mapper.test.ts | 4 +- .../utils/wizard-date-utils.test.ts | 28 +-- 32 files changed, 1607 insertions(+), 616 deletions(-) create mode 100644 src/app/(protected)/workorders/_components/wizard/mini-calendar-cell-style.ts create mode 100644 src/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-draft.ts create mode 100644 src/app/(protected)/workorders/_components/wizard/use-wizard-date-field-state.ts create mode 100644 src/app/(protected)/workorders/_components/wizard/use-wizard-poc-autofill.ts create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-date-field-trigger.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-date-mode-tabs.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-date-range-panel.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-field-select-options.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-field-select-types.ts create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary-rows.ts create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-step-header.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-step-location-poc-fields.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-step-location-service-select.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-step-location-site-details.tsx create mode 100644 src/app/(protected)/workorders/_components/wizard/wizard-step-location-site-select.tsx create mode 100644 src/test/app/(protected)/workorders/use-wizard-poc-autofill.test.tsx create mode 100644 src/test/app/(protected)/workorders/wizard-schedule-date-range.test.tsx create mode 100644 src/test/app/(protected)/workorders/wizard-service-clears-vendor.test.tsx diff --git a/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts b/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts index d0c85a7a..35c12c0d 100644 --- a/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts +++ b/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts @@ -14,7 +14,6 @@ import { useUploadWorkOrderMedia, } from "@/domain/work-orders/use-cases/use-work-order-mutations"; import { isWorkOrderDocumentsReadOnly } from "@/domain/work-orders/utils/work-order-media-limits"; -import { todayIso } from "@/domain/work-orders/utils/wizard-date-utils"; import { useAuthContext } from "@/providers/auth-context"; import { useSlideOverEditState } from "./use-slide-over-edit-state"; import { useSlideOverMediaState } from "./use-slide-over-media-state"; @@ -104,7 +103,7 @@ export function useWorkOrderSlideOver({ // Mirror CompDoc finalize: ensure completedDate then mark docStatus Yes. const patch: WorkOrderTablePatch = { docStatus: "Yes" }; if (!infoSource.completedDate) { - patch.completedDate = todayIso(); + patch.completedDate = new Date().toISOString().slice(0, 10); } onSave(row.id, patch); }, diff --git a/src/app/(protected)/workorders/_components/wizard/mini-calendar-cell-style.ts b/src/app/(protected)/workorders/_components/wizard/mini-calendar-cell-style.ts new file mode 100644 index 00000000..3bb28759 --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/mini-calendar-cell-style.ts @@ -0,0 +1,71 @@ +export type MiniCalendarCellStyle = { + bg: string; + fg: string; + radius: number | string; + filled: boolean; +}; + +function rangeBounds(rangeStart: string, rangeEnd: string): { lo: string; hi: string } { + if (!rangeEnd) { + return { lo: rangeStart, hi: rangeStart }; + } + return rangeStart <= rangeEnd + ? { lo: rangeStart, hi: rangeEnd } + : { lo: rangeEnd, hi: rangeStart }; +} + +function rangeCellStyle( + iso: string, + rangeStart: string, + rangeEnd: string, +): MiniCalendarCellStyle | null { + const { lo, hi } = rangeBounds(rangeStart, rangeEnd); + const filledStyle = { fg: "var(--color-primary-foreground)", bg: "var(--color-primary)" }; + + if (lo === hi) { + return iso === lo ? { ...filledStyle, radius: 4, filled: true } : null; + } + if (iso === lo) { + return { ...filledStyle, radius: "4px 0 0 4px", filled: true }; + } + if (iso === hi) { + return { ...filledStyle, radius: "0 4px 4px 0", filled: true }; + } + if (iso > lo && iso < hi) { + return { bg: "var(--color-primary-tint)", fg: "var(--color-primary)", radius: 0, filled: true }; + } + return null; +} + +/** Resolves the visual state of a single day cell for both single-value and range selection. */ +export function getMiniCalendarCellStyle(args: { + iso: string; + value: string; + isToday: boolean; + range?: boolean; + rangeStart?: string; + rangeEnd?: string; +}): MiniCalendarCellStyle { + const { iso, value, isToday, range, rangeStart = "", rangeEnd = "" } = args; + const defaultStyle: MiniCalendarCellStyle = { + bg: "transparent", + fg: isToday ? "var(--color-primary)" : "var(--color-text-primary)", + radius: 4, + filled: false, + }; + + if (range && rangeStart) { + return rangeCellStyle(iso, rangeStart, rangeEnd) ?? defaultStyle; + } + + if (iso === value) { + return { + bg: "var(--color-primary)", + fg: "var(--color-primary-foreground)", + radius: 4, + filled: true, + }; + } + + return defaultStyle; +} diff --git a/src/app/(protected)/workorders/_components/wizard/mini-calendar.tsx b/src/app/(protected)/workorders/_components/wizard/mini-calendar.tsx index 4c4132fd..e9aa9dab 100644 --- a/src/app/(protected)/workorders/_components/wizard/mini-calendar.tsx +++ b/src/app/(protected)/workorders/_components/wizard/mini-calendar.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { ChevronLeft, ChevronRight } from "lucide-react"; +import { getMiniCalendarCellStyle } from "@/app/(protected)/workorders/_components/wizard/mini-calendar-cell-style"; import { MONTH_NAMES, pad2, todayIso } from "@/domain/work-orders/utils/wizard-date-utils"; const WEEKDAY_HEADER = ["S", "M", "T", "W", "T", "F", "S"]; @@ -7,10 +8,21 @@ const WEEKDAY_HEADER = ["S", "M", "T", "W", "T", "F", "S"]; type MiniCalendarProps = { value: string; onChange: (iso: string) => void; + /** Range-selection mode — parent owns the click logic. */ + range?: boolean; + rangeStart?: string; + rangeEnd?: string; }; -export function MiniCalendar({ value, onChange }: MiniCalendarProps) { - const init = /^\d{4}-\d{2}-\d{2}$/.test(value) ? value : todayIso(); +export function MiniCalendar({ + value, + onChange, + range, + rangeStart = "", + rangeEnd = "", +}: MiniCalendarProps) { + const initRaw = range ? rangeStart || value : value; + const init = /^\d{4}-\d{2}-\d{2}$/.test(initRaw) ? initRaw : todayIso(); const [iy, im] = init.split("-").map(Number); const [viewY, setViewY] = useState(iy); const [viewM, setViewM] = useState(im); @@ -81,8 +93,16 @@ export function MiniCalendar({ value, onChange }: MiniCalendarProps) { return ; } const iso = `${viewY}-${pad2(viewM)}-${pad2(day)}`; - const selected = iso === value; const isToday = iso === today; + const { bg, fg, radius, filled } = getMiniCalendarCellStyle({ + iso, + value, + isToday, + range, + rangeStart, + rangeEnd, + }); + return ( - ); - })} - - )} + {allowWeekMode && cellMode === "week" ? ( + ) : allowRange && cellMode === "range" ? ( + { + onChangeRange?.(start, end); + close(); + }} + /> ) : ( void; +}; + +function modeLabel(mode: WizardSchedMode, allowRange: boolean): string { + if (mode === "week") { + return "Week"; + } + if (mode === "range") { + return "Range"; + } + return allowRange ? "Date" : "Specific date"; +} + +export function WizardDateModeTabs({ + modes, + cellMode, + allowRange, + onSelect, +}: WizardDateModeTabsProps) { + if (modes.length === 0) { + return null; + } + + return ( +
+ {modes.map((m) => { + const activeMode = cellMode === m; + return ( + + ); + })} +
+ ); +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-date-range-panel.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-date-range-panel.tsx new file mode 100644 index 00000000..72f9a73d --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-date-range-panel.tsx @@ -0,0 +1,65 @@ +import { MiniCalendar } from "@/app/(protected)/workorders/_components/wizard/mini-calendar"; +import { rangeDayCount } from "@/domain/work-orders/utils/wizard-date-utils"; + +type WizardDateRangePanelProps = { + rangeStart: string; + rangeEnd: string; + onRangeClick: (iso: string) => void; + onDone: (start: string, end: string) => void; +}; + +export function WizardDateRangePanel({ + rangeStart, + rangeEnd, + onRangeClick, + onDone, +}: WizardDateRangePanelProps) { + const canConfirm = Boolean(rangeStart && rangeEnd); + + return ( + <> + + {Boolean(rangeStart) && ( +
+ {(() => { + const n = rangeDayCount(rangeStart, rangeEnd || rangeStart); + return `${n} ${n === 1 ? "day" : "days"} selected`; + })()} +
+ )} + + + ); +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-field-select-options.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-field-select-options.tsx new file mode 100644 index 00000000..8eba4e0e --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-field-select-options.tsx @@ -0,0 +1,86 @@ +import { Check } from "lucide-react"; +import { Text } from "@/components/ui/text"; +import type { WizardMenuOption } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select-types"; + +type WizardFieldSelectOptionsProps = { + search: boolean; + searchPlaceholder: string; + query: string; + onQueryChange: (q: string) => void; + options: WizardMenuOption[]; + value: string; + onPick: (key: string) => void; +}; + +export function WizardFieldSelectOptions({ + search, + searchPlaceholder, + query, + onQueryChange, + options, + value, + onPick, +}: WizardFieldSelectOptionsProps) { + return ( + <> + {search && ( +
+ onQueryChange(e.target.value)} + placeholder={searchPlaceholder} + className="w-full rounded px-2 py-1 text-[12px] focus:outline-none" + style={{ + border: "1px solid var(--color-border)", + color: "var(--color-text-primary)", + background: "var(--color-bg-surface)", + }} + /> +
+ )} +
+ {options.length === 0 ? ( + + No matches + + ) : ( + options.map((o) => ( + + )) + )} +
+ + ); +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-field-select-types.ts b/src/app/(protected)/workorders/_components/wizard/wizard-field-select-types.ts new file mode 100644 index 00000000..17958148 --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-field-select-types.ts @@ -0,0 +1,9 @@ +import type { ReactNode } from "react"; + +export interface WizardMenuOption { + key: string; + label: string; + description?: string; + left?: ReactNode; + right?: ReactNode; +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-field-select.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-field-select.tsx index 460ecec5..ad53384d 100644 --- a/src/app/(protected)/workorders/_components/wizard/wizard-field-select.tsx +++ b/src/app/(protected)/workorders/_components/wizard/wizard-field-select.tsx @@ -1,15 +1,12 @@ import { useState, type ReactNode } from "react"; -import { Check, ChevronDown } from "lucide-react"; +import { ChevronDown } from "lucide-react"; import { Popover } from "@mui/material"; import { MenuAnchorButton } from "@/components/ui/menu-anchor-button"; -import { Text } from "@/components/ui/text"; +import { WizardFieldSelectOptions } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select-options"; +import type { WizardMenuOption } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select-types"; import { useMenuAnchor } from "@/hooks/use-menu-anchor"; -export interface WizardMenuOption { - key: string; - label: string; - left?: ReactNode; -} +export type { WizardMenuOption } from "@/app/(protected)/workorders/_components/wizard/wizard-field-select-types"; type WizardFieldSelectProps = { value: string; @@ -22,6 +19,19 @@ type WizardFieldSelectProps = { disabled?: boolean; }; +function filterOptions(options: WizardMenuOption[], query: string): WizardMenuOption[] { + if (!query) { + return options; + } + const needle = query.toLowerCase(); + return options.filter( + (o) => + o.label.toLowerCase().includes(needle) || + o.key.toLowerCase().includes(needle) || + (o.description?.toLowerCase().includes(needle) ?? false), + ); +} + export function WizardFieldSelect({ value, placeholder, @@ -35,13 +45,7 @@ export function WizardFieldSelect({ const { anchorEl, open, setAnchor, close } = useMenuAnchor(); const [q, setQ] = useState(""); const selected = options.find((o) => o.key === value); - const filtered = q - ? options.filter( - (o) => - o.label.toLowerCase().includes(q.toLowerCase()) || - o.key.toLowerCase().includes(q.toLowerCase()), - ) - : options; + const filtered = filterOptions(options, q); const handleClose = () => { close(); @@ -74,9 +78,8 @@ export function WizardFieldSelect({ }} aria-haspopup="listbox" > - {icon != null && {icon}} - {icon == null && selected?.left != null && ( - {selected.left} + {(selected?.left ?? icon) != null && ( + {selected?.left ?? icon} )} {selected ? ( - {search && ( -
- setQ(e.target.value)} - placeholder={searchPlaceholder} - className="w-full rounded px-2 py-1 text-[12px] focus:outline-none" - style={{ - border: "1px solid var(--color-border)", - color: "var(--color-text-primary)", - background: "var(--color-bg-surface)", - }} - /> -
- )} -
- {filtered.length === 0 ? ( - - No matches - - ) : ( - filtered.map((o) => ( - - )) - )} -
+ { + onPick(key); + handleClose(); + }} + /> ); diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary-rows.ts b/src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary-rows.ts new file mode 100644 index 00000000..5295cc69 --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary-rows.ts @@ -0,0 +1,45 @@ +import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard"; +import { + fmtDate, + fmtDateRange, + fmtWeekRange, + formatApptRange, +} from "@/domain/work-orders/utils/wizard-date-utils"; + +function scheduleOnLabel(draft: WorkOrderWizardDraft): string { + if (draft.schedMode === "range" && draft.scheduledOn && draft.scheduleEnd) { + return fmtDateRange(draft.scheduledOn, draft.scheduleEnd); + } + if (!draft.scheduledOn) { + return "—"; + } + if (draft.scheduleWeekOnly) { + return fmtWeekRange(draft.targetWeek || draft.scheduledOn); + } + return fmtDate(draft.scheduledOn); +} + +function pocLabel(draft: WorkOrderWizardDraft): string { + if (!draft.pocName) { + return "—"; + } + return draft.pocPhone ? `${draft.pocName} · ${draft.pocPhone}` : draft.pocName; +} + +/** Builds the label/value rows for the wizard sidebar's live summary card. */ +export function buildWizardSidebarSummaryRows(draft: WorkOrderWizardDraft): [string, string][] { + return [ + ["WO #", draft.woNumber ? `#${draft.woNumber}` : "—"], + ["Type", draft.type || "—"], + ["Schedule on", scheduleOnLabel(draft)], + ["Due date", fmtDate(draft.dueDate) || "—"], + ["Assigned to", draft.assigneeName || "—"], + ["Site", draft.siteCode || draft.locationName || "—"], + ["POC", pocLabel(draft)], + ["Service", draft.pm || "—"], + ["Technician", draft.vendorName || "—"], + ["Company", draft.vendorName || "—"], + ["Appt time", draft.apptStart ? formatApptRange(draft.apptStart, draft.apptEnd) : "—"], + ["Status", draft.status || "Incomplete"], + ]; +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary.tsx new file mode 100644 index 00000000..e330f575 --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary.tsx @@ -0,0 +1,82 @@ +import { + WIZARD_STATUS_DOTS, + type WizardWOStatus, + type WorkOrderWizardDraft, +} from "@/domain/work-orders/types/work-order-wizard"; +import { buildWizardSidebarSummaryRows } from "@/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary-rows"; +import { ColorDot } from "@/app/(protected)/workorders/_components/wizard/wizard-labels"; + +export function WizardSidebarSummary({ draft }: { draft: WorkOrderWizardDraft }) { + const summary = buildWizardSidebarSummaryRows(draft); + const statusLabel = draft.status || "Incomplete"; + const statusValue = statusLabel as WizardWOStatus; + const statusDotColor = WIZARD_STATUS_DOTS[statusValue] ?? WIZARD_STATUS_DOTS.Incomplete; + + return ( +
+
+ This work order +
+ {summary.map(([label, val], i) => ( +
+ + {label} + + {label === "Status" ? ( + + + + {statusLabel} + + + ) : ( + + {val} + + )} +
+ ))} +
+ ); +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-sidebar.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-sidebar.tsx index 62a13a75..599768a5 100644 --- a/src/app/(protected)/workorders/_components/wizard/wizard-sidebar.tsx +++ b/src/app/(protected)/workorders/_components/wizard/wizard-sidebar.tsx @@ -1,19 +1,10 @@ import { ClipboardList } from "lucide-react"; import { - WIZARD_STATUS_DOTS, WIZARD_STEPS, - type WizardWOStatus, type WorkOrderWizardDraft, } from "@/domain/work-orders/types/work-order-wizard"; -import { - fmtDate, - fmtWeekRange, - formatApptRange, -} from "@/domain/work-orders/utils/wizard-date-utils"; -import { - ColorDot, - StepCircle, -} from "@/app/(protected)/workorders/_components/wizard/wizard-labels"; +import { StepCircle } from "@/app/(protected)/workorders/_components/wizard/wizard-labels"; +import { WizardSidebarSummary } from "@/app/(protected)/workorders/_components/wizard/wizard-sidebar-summary"; type WizardSidebarProps = { step: number; @@ -120,106 +111,3 @@ export function WizardSidebar({ step, draft }: WizardSidebarProps) { ); } - -function WizardSidebarSummary({ draft }: { draft: WorkOrderWizardDraft }) { - const summary: [string, string][] = [ - ["WO #", draft.woNumber ? `#${draft.woNumber}` : "—"], - ["Type", draft.type || "—"], - [ - "Schedule on", - draft.scheduledOn - ? draft.scheduleWeekOnly - ? fmtWeekRange(draft.scheduledOn) - : fmtDate(draft.scheduledOn) - : "—", - ], - ["Due date", fmtDate(draft.dueDate) || "—"], - ["Assigned to", draft.assigneeName || "—"], - ["Site", draft.locationName || "—"], - [ - "POC", - draft.pocName - ? draft.pocPhone - ? `${draft.pocName} · ${draft.pocPhone}` - : draft.pocName - : "—", - ], - ["Service", draft.pm || "—"], - ["Technician", draft.vendorName || "—"], - ["Company", draft.vendorName || "—"], - ["Appt time", draft.apptStart ? formatApptRange(draft.apptStart, draft.apptEnd) : "—"], - ["Status", draft.status || "Incomplete"], - ]; - - const statusLabel = draft.status || "Incomplete"; - const statusValue = statusLabel as WizardWOStatus; - const statusDotColor = WIZARD_STATUS_DOTS[statusValue] ?? WIZARD_STATUS_DOTS.Incomplete; - - return ( -
-
- This work order -
- {summary.map(([label, val], i) => ( -
- - {label} - - {label === "Status" ? ( - - - - {statusLabel} - - - ) : ( - - {val} - - )} -
- ))} -
- ); -} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-step-header.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-step-header.tsx new file mode 100644 index 00000000..29976864 --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-step-header.tsx @@ -0,0 +1,42 @@ +import { Text } from "@/components/ui/text"; +import { WIZARD_STEPS } from "@/domain/work-orders/types/work-order-wizard"; + +export function WizardStepHeader({ step }: { step: number }) { + return ( +
+
+ Step {step} of {WIZARD_STEPS.length} +
+ + {WIZARD_STEPS[step - 1].title} + + + {WIZARD_STEPS[step - 1].subtitle} + +
+ ); +} diff --git a/src/app/(protected)/workorders/_components/wizard/wizard-step-location-poc-fields.tsx b/src/app/(protected)/workorders/_components/wizard/wizard-step-location-poc-fields.tsx new file mode 100644 index 00000000..3b3b1e3a --- /dev/null +++ b/src/app/(protected)/workorders/_components/wizard/wizard-step-location-poc-fields.tsx @@ -0,0 +1,51 @@ +import { Text } from "@/components/ui/text"; +import type { WorkOrderWizardDraft } from "@/domain/work-orders/types/work-order-wizard"; +import { + DLG_INPUT_CLS, + DLG_INPUT_STYLE, +} from "@/app/(protected)/workorders/_components/wizard/wizard-input-styles"; +import { WizSubLabel } from "@/app/(protected)/workorders/_components/wizard/wizard-labels"; + +type WizardStepLocationPocFieldsProps = { + draft: WorkOrderWizardDraft; + onUpdate: (key: K, value: WorkOrderWizardDraft[K]) => void; +}; + +export function WizardStepLocationPocFields({ draft, onUpdate }: WizardStepLocationPocFieldsProps) { + return ( +
+ Point of contact + + Auto-filled from records — edit if outdated + +
+ onUpdate("pocName", e.target.value)} + placeholder="POC name" + className={DLG_INPUT_CLS} + style={DLG_INPUT_STYLE} + /> + onUpdate("pocPhone", e.target.value)} + placeholder="POC phone" + className={DLG_INPUT_CLS} + style={DLG_INPUT_STYLE} + /> +
+