mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
Compare commits
8 commits
8ab56d9782
...
1a54cbd2af
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a54cbd2af | ||
|
|
959dda08fe | ||
|
|
0c75bd42f4 | ||
|
|
871b7b0964 | ||
|
|
d5a04d318a | ||
|
|
4b73af9688 | ||
|
|
defdc4c75b | ||
|
|
54c90e0e36 |
19 changed files with 266 additions and 51 deletions
25
src/app/(auth)/_components/auth-brand-logo.tsx
Normal file
25
src/app/(auth)/_components/auth-brand-logo.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import { BrandLockup } from "@/components/common/brand-lockup";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export type AuthBrandLogoProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function AuthBrandLogo({ className }: AuthBrandLogoProps) {
|
||||
const [useFallback, setUseFallback] = useState(false);
|
||||
|
||||
if (useFallback) {
|
||||
return <BrandLockup className={className} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
src="/seahaven-logo.svg"
|
||||
alt="Seahaven"
|
||||
onError={() => setUseFallback(true)}
|
||||
className={cn("mx-auto block h-10 w-auto", className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,14 +11,17 @@ export type AuthCardHeaderProps = ComponentPropsWithoutRef<"div"> & {
|
|||
export function AuthCardHeader({ title, subtitle, className, ...props }: AuthCardHeaderProps) {
|
||||
return (
|
||||
<div className={cn(className)} {...props}>
|
||||
<Text variant="title" className="m-0 text-center font-bold text-foreground">
|
||||
<Text
|
||||
variant="display"
|
||||
className="m-0 text-center !font-bold ![font-size:var(--text-xl)] !leading-[1.5] text-foreground"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
<Text
|
||||
variant="description"
|
||||
tone="muted"
|
||||
when={Boolean(subtitle)}
|
||||
className="mb-6! mt-1.5! text-center font-normal"
|
||||
className="mb-6! mt-1.5! text-center font-normal ![font-size:var(--text-base)]"
|
||||
>
|
||||
{subtitle}
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
import { AuthBrandLogo } from "@/app/(auth)/_components/auth-brand-logo";
|
||||
import { AuthCardHeader } from "@/app/(auth)/_components/auth-card-header";
|
||||
import { AuthPageShell } from "@/app/(auth)/_components/auth-page-shell";
|
||||
import { LoginForm } from "@/app/(auth)/_components/login-form";
|
||||
import { BrandLockup } from "@/components/common/brand-lockup";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { loginSchema, type LoginFormValues } from "@/domain/auth/schemas/login-schema";
|
||||
import { useAuthContext } from "@/providers/auth-context";
|
||||
|
|
@ -27,7 +27,7 @@ export default function LoginPage() {
|
|||
|
||||
return (
|
||||
<AuthPageShell>
|
||||
<BrandLockup className="mb-8" />
|
||||
<AuthBrandLogo className="mb-8" />
|
||||
<AuthCardHeader title="Welcome back" subtitle="Sign in to your account" />
|
||||
<LoginForm
|
||||
register={register}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,7 @@ export function DayTabs({ weekMonday, selectedDayIso, onChange, disabled = false
|
|||
const days = weekdaysMonFri(weekMonday);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-1.5 overflow-x-auto px-4 pb-2"
|
||||
role="tablist"
|
||||
aria-label="Weekdays"
|
||||
>
|
||||
<div className="flex items-center gap-1.5 overflow-x-auto" role="tablist" aria-label="Weekdays">
|
||||
{days.map((iso) => {
|
||||
const label = formatDayTabLabel(iso);
|
||||
const active = iso === selectedDayIso;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export function WeekSelector({ weekMonday, onChange, disabled = false }: WeekSel
|
|||
)}
|
||||
>
|
||||
{label}
|
||||
{live && <WeekLiveBadge />}
|
||||
{Boolean(live) && <WeekLiveBadge />}
|
||||
<ChevronDown size={14} aria-hidden />
|
||||
</MenuAnchorButton>
|
||||
<Popover
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { DayTabs } from "@/app/(protected)/workorders/_components/list/filter-ba
|
|||
import { DispatcherFilter } from "@/app/(protected)/workorders/_components/list/filter-bar/dispatcher-filter";
|
||||
import { WeekSelector } from "@/app/(protected)/workorders/_components/list/filter-bar/week-selector";
|
||||
import { WoTypeSegment } from "@/app/(protected)/workorders/_components/list/filter-bar/wo-type-segment";
|
||||
import { Text } from "@/components/ui/text";
|
||||
|
||||
type WorkOrdersFilterBarProps = {
|
||||
weekMonday: string;
|
||||
|
|
@ -45,7 +46,7 @@ export function WorkOrdersFilterBar({
|
|||
className="-mx-2.5 shrink-0 border-b border-(--color-border) bg-(--color-bg-surface)"
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
<div className="flex h-14 items-center gap-3 px-4">
|
||||
<div className="flex items-center gap-3 px-4 pt-2">
|
||||
<DispatcherFilter
|
||||
selectedIds={dispatcherIds}
|
||||
onChange={onDispatcherChange}
|
||||
|
|
@ -54,17 +55,23 @@ export function WorkOrdersFilterBar({
|
|||
currentUserId={currentUserId}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<WeekSelector weekMonday={weekMonday} onChange={onWeekChange} disabled={disabled} />
|
||||
<WoTypeSegment value={typeFilter} onChange={onTypeChange} disabled={disabled} />
|
||||
<div className="flex-1" />
|
||||
<span className="shrink-0 text-xs font-medium text-muted-foreground">{resultLabel}</span>
|
||||
<Text variant="caption" tone="muted" className="shrink-0">
|
||||
{resultLabel}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 px-4 pt-1" aria-label="Week and day">
|
||||
<WeekSelector weekMonday={weekMonday} onChange={onWeekChange} disabled={disabled} />
|
||||
<DayTabs
|
||||
weekMonday={weekMonday}
|
||||
selectedDayIso={selectedDayIso}
|
||||
onChange={onSelectedDayChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 px-4 pt-1 pb-2">
|
||||
<WoTypeSegment value={typeFilter} onChange={onTypeChange} disabled={disabled} />
|
||||
</div>
|
||||
<DayTabs
|
||||
weekMonday={weekMonday}
|
||||
selectedDayIso={selectedDayIso}
|
||||
onChange={onSelectedDayChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import type {
|
|||
} from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { applySchedulePatch } from "@/domain/work-orders/use-cases/use-work-order-table-mutations";
|
||||
import { shouldNotifyCrossWeek } from "@/domain/work-orders/utils/should-notify-cross-week";
|
||||
import { mondayOf } from "@/domain/work-orders/utils/wizard-date-utils";
|
||||
import type { ReturnTypeOfUseWorkOrdersTableData } from "./wo-table-types";
|
||||
import { rescheduleNotify, weekMoveNotify } from "./reschedule-notify";
|
||||
import type { WoTableRowHandlers } from "./wo-table-row";
|
||||
|
|
@ -13,7 +12,8 @@ import type { WoTableRowHandlers } from "./wo-table-row";
|
|||
type UseWoTableRowHandlersOptions = {
|
||||
tableData: ReturnTypeOfUseWorkOrdersTableData;
|
||||
weekMonday: string;
|
||||
onWeekChange: (mondayIso: string) => void;
|
||||
onNavigateToDay: (dateIso: string) => void;
|
||||
onOpenAdvancedFilters?: () => void;
|
||||
onOpenSlideOver: (row: WorkOrderTableRow, edit?: boolean) => void;
|
||||
onOpenCompDoc?: (row: WorkOrderTableRow) => void;
|
||||
onOpenUplifts?: (row: WorkOrderTableRow) => void;
|
||||
|
|
@ -26,7 +26,8 @@ type UseWoTableRowHandlersOptions = {
|
|||
export function useWoTableRowHandlers({
|
||||
tableData,
|
||||
weekMonday,
|
||||
onWeekChange,
|
||||
onNavigateToDay,
|
||||
onOpenAdvancedFilters,
|
||||
onOpenSlideOver,
|
||||
onOpenCompDoc,
|
||||
onOpenUplifts,
|
||||
|
|
@ -35,14 +36,7 @@ export function useWoTableRowHandlers({
|
|||
onScheduleHighlight,
|
||||
onDuplicateFound,
|
||||
}: UseWoTableRowHandlersOptions): WoTableRowHandlers {
|
||||
const {
|
||||
dragState: dragStateRef,
|
||||
setDragViz,
|
||||
reorderRows,
|
||||
endDrag,
|
||||
setUnschedOpen,
|
||||
triggerFlash,
|
||||
} = tableData;
|
||||
const { dragState: dragStateRef, setDragViz, reorderRows, endDrag, triggerFlash } = tableData;
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
|
|
@ -80,14 +74,16 @@ export function useWoTableRowHandlers({
|
|||
onScheduleDate: (id: string | number, date: string) => {
|
||||
onPatch(id, applySchedulePatch(date, false));
|
||||
if (shouldNotifyCrossWeek(date, weekMonday)) {
|
||||
const targetMonday = mondayOf(date);
|
||||
rescheduleNotify(date, {
|
||||
onNavigate: () => {
|
||||
onWeekChange(targetMonday);
|
||||
onNavigateToDay(date);
|
||||
setTimeout(() => triggerFlash(id), 180);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
onNavigateToDay(date);
|
||||
triggerFlash(id);
|
||||
},
|
||||
onScheduleWeek: (id: string | number, week: string) => {
|
||||
onPatch(id, {
|
||||
|
|
@ -97,12 +93,10 @@ export function useWoTableRowHandlers({
|
|||
dayGroup: "",
|
||||
dayLabel: "",
|
||||
});
|
||||
setUnschedOpen(true);
|
||||
triggerFlash(id);
|
||||
weekMoveNotify(week, {
|
||||
onNavigate: () => {
|
||||
setUnschedOpen(true);
|
||||
setTimeout(() => triggerFlash(id), 180);
|
||||
onNavigateToDay(week);
|
||||
onOpenAdvancedFilters?.();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
@ -138,10 +132,10 @@ export function useWoTableRowHandlers({
|
|||
setDragViz,
|
||||
reorderRows,
|
||||
endDrag,
|
||||
setUnschedOpen,
|
||||
triggerFlash,
|
||||
weekMonday,
|
||||
onWeekChange,
|
||||
onNavigateToDay,
|
||||
onOpenAdvancedFilters,
|
||||
onOpenSlideOver,
|
||||
onOpenCompDoc,
|
||||
onOpenUplifts,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type WoTableProps = {
|
|||
vendors: VendorDropdownItem[];
|
||||
pmTypes: string[];
|
||||
weekMonday: string;
|
||||
onWeekChange: (mondayIso: string) => void;
|
||||
onNavigateToDay: (dateIso: string) => void;
|
||||
onOpenSlideOver: (row: WorkOrderTableRow, edit?: boolean) => void;
|
||||
/** Omit until completion-doc slice mounts CompDocDialog. */
|
||||
onOpenCompDoc?: (row: WorkOrderTableRow) => void;
|
||||
|
|
@ -75,7 +75,7 @@ export function WoTable({
|
|||
vendors,
|
||||
pmTypes,
|
||||
weekMonday,
|
||||
onWeekChange,
|
||||
onNavigateToDay,
|
||||
onOpenSlideOver,
|
||||
onOpenCompDoc,
|
||||
onOpenUplifts,
|
||||
|
|
@ -98,7 +98,8 @@ export function WoTable({
|
|||
const handlers = useWoTableRowHandlers({
|
||||
tableData,
|
||||
weekMonday,
|
||||
onWeekChange,
|
||||
onNavigateToDay,
|
||||
onOpenAdvancedFilters,
|
||||
onOpenSlideOver,
|
||||
onOpenCompDoc,
|
||||
onOpenUplifts,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { compactWeekLabel, currentWeekMonday } from "@/domain/work-orders/utils/
|
|||
import {
|
||||
firstWeekday,
|
||||
initialSelectedDay,
|
||||
scheduledDayTab,
|
||||
weekdaysMonFri,
|
||||
} from "@/domain/work-orders/utils/week-day-nav-utils";
|
||||
|
||||
|
|
@ -72,6 +73,13 @@ export function useWorkOrdersListFilters() {
|
|||
setSelectedDayIsoState(value);
|
||||
};
|
||||
|
||||
const navigateToDay = (dateIso: string) => {
|
||||
const tab = scheduledDayTab(dateIso);
|
||||
setWeekMondayState(tab.weekMonday);
|
||||
setSelectedDayIsoState(tab.selectedDayIso);
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
const setDispatcherIds = (value: Set<string>) => {
|
||||
setDispatcherIdsState(new Set(value));
|
||||
setPage(0);
|
||||
|
|
@ -156,6 +164,7 @@ export function useWorkOrdersListFilters() {
|
|||
setPageSize,
|
||||
setWeekMonday,
|
||||
setSelectedDayIso,
|
||||
navigateToDay,
|
||||
setDispatcherIds,
|
||||
setTypeFilter,
|
||||
setSortBy,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ import { useWorkOrdersTableLocalState } from "@/app/(protected)/workorders/_hook
|
|||
import { currentWeekMonday } from "@/domain/work-orders/utils/list-week-utils";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { deriveDayFields } from "@/domain/work-orders/utils/table-day-utils";
|
||||
import { buildSelectedDayGroup } from "@/domain/work-orders/utils/week-day-nav-utils";
|
||||
import {
|
||||
buildSelectedDayGroup,
|
||||
isDayScopedSearchEmpty,
|
||||
} from "@/domain/work-orders/utils/week-day-nav-utils";
|
||||
import {
|
||||
composeBoardSections,
|
||||
filterByBarAndSearch,
|
||||
|
|
@ -83,12 +86,17 @@ export function useWorkOrdersTableData(filters: FiltersReturn) {
|
|||
);
|
||||
|
||||
const searchActive = filters.search.trim() !== "";
|
||||
const visibleRows = advActive ? advResults : filtered;
|
||||
const noSearchResults = visibleRows.length === 0 && searchActive;
|
||||
const isEmptyWeek =
|
||||
!advActive && scheduled.length === 0 && unassigned.length === 0 && !searchActive;
|
||||
const displayGroups = dayGroups;
|
||||
const dayVisibleCount = displayGroups[0]?.items.length ?? 0;
|
||||
const noSearchResults =
|
||||
!advActive &&
|
||||
isDayScopedSearchEmpty({
|
||||
searchActive,
|
||||
unassignedCount: unassigned.length,
|
||||
dayItemCount: dayVisibleCount,
|
||||
});
|
||||
const isEmptyWeek =
|
||||
!advActive && scheduled.length === 0 && unassigned.length === 0 && !searchActive;
|
||||
|
||||
const todayKey =
|
||||
filters.weekMonday === currentWeekMonday()
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export default function WorkOrdersListPage() {
|
|||
vendors={page.vendors}
|
||||
pmTypes={page.pmTypes}
|
||||
weekMonday={filters.weekMonday}
|
||||
onWeekChange={filters.setWeekMonday}
|
||||
onNavigateToDay={filters.navigateToDay}
|
||||
onOpenSlideOver={page.handleOpenSlideOver}
|
||||
onOpenCompDoc={(row) => openCompletionDoc(row, page.setDocRow)}
|
||||
onOpenMedia={page.setMediaRow}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type FormFieldProps = FormControlProps & {
|
|||
|
||||
const appearanceClassName: Record<FormFieldAppearance, string> = {
|
||||
default: "gap-1",
|
||||
auth: "gap-1.5 [&_.MuiFormLabel-root]:mb-0 [&_.MuiFormLabel-root]:!font-sans [&_.MuiFormLabel-root]:![font-size:var(--text-base-sm)] [&_.MuiFormLabel-root]:!font-medium [&_.MuiFormLabel-root]:!leading-[1.5] [&_.MuiFormLabel-root]:!normal-case [&_.MuiFormLabel-root]:!tracking-normal",
|
||||
auth: "gap-1.5 [&_.MuiFormLabel-root]:mb-0 [&_.MuiFormLabel-root]:!font-sans [&_.MuiFormLabel-root]:![font-size:var(--text-base-sm)] [&_.MuiFormLabel-root]:!font-medium [&_.MuiFormLabel-root]:!leading-[1.5] [&_.MuiFormLabel-root]:!normal-case [&_.MuiFormLabel-root]:!text-foreground [&_.MuiFormLabel-root]:!tracking-normal",
|
||||
};
|
||||
|
||||
const appearanceLabelClassName: Record<FormFieldAppearance, string> = {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type {
|
|||
} from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { isLiveWeek } from "@/domain/work-orders/utils/list-week-utils";
|
||||
import { isDateInWeek } from "@/domain/work-orders/utils/table-day-utils";
|
||||
import { addDaysIso, todayIso } from "@/domain/work-orders/utils/wizard-date-utils";
|
||||
import { addDaysIso, mondayOf, todayIso } from "@/domain/work-orders/utils/wizard-date-utils";
|
||||
|
||||
const DAY_TAB_WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as const;
|
||||
|
||||
|
|
@ -16,6 +16,25 @@ export function firstWeekday(weekMonday: string): string {
|
|||
return weekMonday;
|
||||
}
|
||||
|
||||
/** Maps a scheduled date onto the Mon–Fri tab that can show it (weekends fall back to Monday). */
|
||||
export function scheduledDayTab(dateIso: string): { weekMonday: string; selectedDayIso: string } {
|
||||
const day = dateIso.slice(0, 10);
|
||||
const weekMonday = mondayOf(day);
|
||||
const days = weekdaysMonFri(weekMonday);
|
||||
return {
|
||||
weekMonday,
|
||||
selectedDayIso: days.includes(day) ? day : firstWeekday(weekMonday),
|
||||
};
|
||||
}
|
||||
|
||||
export function isDayScopedSearchEmpty(args: {
|
||||
searchActive: boolean;
|
||||
unassignedCount: number;
|
||||
dayItemCount: number;
|
||||
}): boolean {
|
||||
return args.searchActive && args.unassignedCount === 0 && args.dayItemCount === 0;
|
||||
}
|
||||
|
||||
export function initialSelectedDay(weekMonday: string, today = todayIso()): string {
|
||||
if (isLiveWeek(weekMonday) && isDateInWeek(today, weekMonday)) {
|
||||
return today;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe("useWoTableRowHandlers onDuplicateFound", () => {
|
|||
useWoTableRowHandlers({
|
||||
tableData: buildTableData(),
|
||||
weekMonday: "2026-07-20",
|
||||
onWeekChange: vi.fn(),
|
||||
onNavigateToDay: vi.fn(),
|
||||
onOpenSlideOver,
|
||||
onOpenMedia: vi.fn(),
|
||||
onPatch: vi.fn(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
import { renderHook } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { useWoTableRowHandlers } from "@/app/(protected)/workorders/_components/list/table/use-wo-table-row-handlers";
|
||||
import { applySchedulePatch } from "@/domain/work-orders/use-cases/use-work-order-table-mutations";
|
||||
|
||||
const { rescheduleNotify, weekMoveNotify } = vi.hoisted(() => ({
|
||||
rescheduleNotify: vi.fn(),
|
||||
weekMoveNotify: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/app/(protected)/workorders/_components/list/table/reschedule-notify", () => ({
|
||||
rescheduleNotify,
|
||||
weekMoveNotify,
|
||||
}));
|
||||
|
||||
function buildTableData(triggerFlash = vi.fn()) {
|
||||
return {
|
||||
dragState: { current: { dragId: null, overId: null } },
|
||||
setDragViz: vi.fn(),
|
||||
reorderRows: vi.fn(),
|
||||
endDrag: vi.fn(),
|
||||
triggerFlash,
|
||||
setSelectedId: vi.fn(),
|
||||
} as never;
|
||||
}
|
||||
|
||||
describe("useWoTableRowHandlers schedule navigation (SH-240)", () => {
|
||||
it("selects the scheduled weekday in the same week so the row stays visible", () => {
|
||||
const onNavigateToDay = vi.fn();
|
||||
const onPatch = vi.fn();
|
||||
const triggerFlash = vi.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useWoTableRowHandlers({
|
||||
tableData: buildTableData(triggerFlash),
|
||||
weekMonday: "2026-05-18",
|
||||
onNavigateToDay,
|
||||
onOpenSlideOver: vi.fn(),
|
||||
onOpenMedia: vi.fn(),
|
||||
onPatch,
|
||||
onScheduleHighlight: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
result.current.onScheduleDate("wo-1", "2026-05-21");
|
||||
|
||||
expect(onPatch).toHaveBeenCalledWith("wo-1", applySchedulePatch("2026-05-21", false));
|
||||
expect(onNavigateToDay).toHaveBeenCalledWith("2026-05-21");
|
||||
expect(triggerFlash).toHaveBeenCalledWith("wo-1");
|
||||
expect(rescheduleNotify).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sends cross-week view-it navigation to the target day, not Monday", () => {
|
||||
const onNavigateToDay = vi.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useWoTableRowHandlers({
|
||||
tableData: buildTableData(),
|
||||
weekMonday: "2026-05-18",
|
||||
onNavigateToDay,
|
||||
onOpenSlideOver: vi.fn(),
|
||||
onOpenMedia: vi.fn(),
|
||||
onPatch: vi.fn(),
|
||||
onScheduleHighlight: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
result.current.onScheduleDate("wo-1", "2026-05-27");
|
||||
|
||||
expect(onNavigateToDay).not.toHaveBeenCalled();
|
||||
expect(rescheduleNotify).toHaveBeenCalledWith(
|
||||
"2026-05-27",
|
||||
expect.objectContaining({ onNavigate: expect.any(Function) }),
|
||||
);
|
||||
|
||||
const { onNavigate } = rescheduleNotify.mock.calls[0][1] as { onNavigate: () => void };
|
||||
onNavigate();
|
||||
expect(onNavigateToDay).toHaveBeenCalledWith("2026-05-27");
|
||||
});
|
||||
|
||||
it("opens Advanced Filters for week-only moves instead of the removed Unscheduled section", () => {
|
||||
const onNavigateToDay = vi.fn();
|
||||
const onOpenAdvancedFilters = vi.fn();
|
||||
const onPatch = vi.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useWoTableRowHandlers({
|
||||
tableData: buildTableData(),
|
||||
weekMonday: "2026-05-18",
|
||||
onNavigateToDay,
|
||||
onOpenAdvancedFilters,
|
||||
onOpenSlideOver: vi.fn(),
|
||||
onOpenMedia: vi.fn(),
|
||||
onPatch,
|
||||
onScheduleHighlight: vi.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
result.current.onScheduleWeek("wo-1", "2026-05-25");
|
||||
|
||||
expect(onPatch).toHaveBeenCalledWith(
|
||||
"wo-1",
|
||||
expect.objectContaining({
|
||||
scheduledOn: "",
|
||||
scheduleWeekOnly: true,
|
||||
targetWeek: "2026-05-25",
|
||||
}),
|
||||
);
|
||||
expect(weekMoveNotify).toHaveBeenCalled();
|
||||
|
||||
const { onNavigate } = weekMoveNotify.mock.calls[0][1] as { onNavigate: () => void };
|
||||
onNavigate();
|
||||
expect(onNavigateToDay).toHaveBeenCalledWith("2026-05-25");
|
||||
expect(onOpenAdvancedFilters).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -99,4 +99,15 @@ describe("useWorkOrdersListFilters day state (SH-240)", () => {
|
|||
expect(result.current.weekMonday).toBe("2026-08-10");
|
||||
expect(result.current.selectedDayIso).toBe("2026-08-10");
|
||||
});
|
||||
|
||||
it("follows a scheduled date onto that weekday instead of resetting to Monday", () => {
|
||||
const { result } = renderHook(() => useWorkOrdersListFilters());
|
||||
|
||||
act(() => {
|
||||
result.current.navigateToDay("2026-05-20");
|
||||
});
|
||||
|
||||
expect(result.current.weekMonday).toBe("2026-05-18");
|
||||
expect(result.current.selectedDayIso).toBe("2026-05-20");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ vi.mock("@/app/(protected)/workorders/_hooks/use-work-orders-list-filters", () =
|
|||
selectedDayIso: "2026-07-20",
|
||||
setWeekMonday: vi.fn(),
|
||||
setSelectedDayIso: vi.fn(),
|
||||
navigateToDay: vi.fn(),
|
||||
dispatcherIds: new Set<string>(),
|
||||
setDispatcherIds: vi.fn(),
|
||||
typeFilter: "All",
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ vi.mock("@/app/(protected)/workorders/_hooks/use-work-orders-list-filters", () =
|
|||
selectedDayIso: "2026-07-20",
|
||||
setWeekMonday: vi.fn(),
|
||||
setSelectedDayIso: vi.fn(),
|
||||
navigateToDay: vi.fn(),
|
||||
dispatcherIds: new Set<string>(),
|
||||
setDispatcherIds: vi.fn(),
|
||||
typeFilter: "All",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import {
|
|||
firstWeekday,
|
||||
formatDayTabLabel,
|
||||
initialSelectedDay,
|
||||
isDayScopedSearchEmpty,
|
||||
rowMatchesSelectedDay,
|
||||
scheduledDayTab,
|
||||
weekdaysMonFri,
|
||||
} from "@/domain/work-orders/utils/week-day-nav-utils";
|
||||
import { addDaysIso } from "@/domain/work-orders/utils/wizard-date-utils";
|
||||
|
|
@ -119,4 +121,26 @@ describe("week-day-nav-utils (SH-240)", () => {
|
|||
expect(group.label).toBe("Mon · 05/18");
|
||||
expect(group.items.map((item) => item.id)).toEqual(["mon"]);
|
||||
});
|
||||
|
||||
it("maps a scheduled weekday onto its week and day tab", () => {
|
||||
expect(scheduledDayTab("2026-05-20")).toEqual({
|
||||
weekMonday: "2026-05-18",
|
||||
selectedDayIso: "2026-05-20",
|
||||
});
|
||||
});
|
||||
|
||||
it("treats search as empty only when the selected day and Unassigned have no hits", () => {
|
||||
expect(
|
||||
isDayScopedSearchEmpty({ searchActive: true, unassignedCount: 0, dayItemCount: 0 }),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isDayScopedSearchEmpty({ searchActive: true, unassignedCount: 0, dayItemCount: 1 }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isDayScopedSearchEmpty({ searchActive: true, unassignedCount: 2, dayItemCount: 0 }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isDayScopedSearchEmpty({ searchActive: false, unassignedCount: 0, dayItemCount: 0 }),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue