mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
feat(onboarding/phase-6): setup wizard polish, seed script alignment, doctor visit slots
- Setup wizard: 3-pane layout with right-side live previews, resume banner, edit/copy icons on team step, AI prompt configuration - Forms: employee-create replaces invite-member (no email invites), clinic form with address/hours/payment, doctor form with visit slots - Seed script: aligned to current SDK schema — doctors created as workspace members (HelixEngage Manager role), visitingHours replaced by doctorVisitSlot entity, clinics seeded, portalUserId linked dynamically, SUB/ORIGIN/GQL configurable via env vars - Pages: clinics + doctors CRUD updated for new schema, team settings with temp password + role assignment - New components: time-picker, day-selector, wizard-right-panes, wizard-layout-context, resume-setup-banner - Removed: invite-member-form (replaced by employee-create-form per no-email-invites rule) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
108
src/components/application/day-selector/day-selector.tsx
Normal file
108
src/components/application/day-selector/day-selector.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import { cx } from "@/utils/cx";
|
||||
|
||||
// Keys match the Clinic entity's openMonday..openSunday fields
|
||||
// directly — no translation layer needed when reading/writing the
|
||||
// form value into GraphQL mutations.
|
||||
export type DayKey =
|
||||
| "monday"
|
||||
| "tuesday"
|
||||
| "wednesday"
|
||||
| "thursday"
|
||||
| "friday"
|
||||
| "saturday"
|
||||
| "sunday";
|
||||
|
||||
export type DaySelection = Record<DayKey, boolean>;
|
||||
|
||||
const DAYS: { key: DayKey; label: string }[] = [
|
||||
{ key: "monday", label: "Mon" },
|
||||
{ key: "tuesday", label: "Tue" },
|
||||
{ key: "wednesday", label: "Wed" },
|
||||
{ key: "thursday", label: "Thu" },
|
||||
{ key: "friday", label: "Fri" },
|
||||
{ key: "saturday", label: "Sat" },
|
||||
{ key: "sunday", label: "Sun" },
|
||||
];
|
||||
|
||||
type DaySelectorProps = {
|
||||
/** Selected-state for each weekday. */
|
||||
value: DaySelection;
|
||||
/** Fires with the full updated selection whenever a pill is tapped. */
|
||||
onChange: (value: DaySelection) => void;
|
||||
/** Optional heading above the pills. */
|
||||
label?: string;
|
||||
/** Optional helper text below the pills. */
|
||||
hint?: string;
|
||||
};
|
||||
|
||||
// Seven tappable Mon–Sun pills. Used on the Clinic form to pick which
|
||||
// days the clinic is open, since the Clinic entity has seven separate
|
||||
// BOOLEAN fields (openMonday..openSunday) — SDK has no MULTI_SELECT.
|
||||
// Also reusable anywhere else we need a weekly-recurrence picker
|
||||
// (future: follow-up schedules, on-call rotations).
|
||||
export const DaySelector = ({ value, onChange, label, hint }: DaySelectorProps) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
{label && (
|
||||
<span className="text-sm font-medium text-secondary">{label}</span>
|
||||
)}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{DAYS.map(({ key, label: dayLabel }) => {
|
||||
const isSelected = !!value[key];
|
||||
return (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
onClick={() => onChange({ ...value, [key]: !isSelected })}
|
||||
className={cx(
|
||||
"flex h-10 min-w-12 items-center justify-center rounded-full border px-4 text-sm font-semibold transition duration-100 ease-linear",
|
||||
isSelected
|
||||
? "border-brand bg-brand-solid text-white hover:bg-brand-solid_hover"
|
||||
: "border-secondary bg-primary text-secondary hover:border-primary hover:bg-secondary_hover",
|
||||
)}
|
||||
aria-pressed={isSelected}
|
||||
>
|
||||
{dayLabel}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{hint && <span className="text-xs text-tertiary">{hint}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
// Helper factories — use these instead of spelling out the empty
|
||||
// object literal everywhere.
|
||||
export const emptyDaySelection = (): DaySelection => ({
|
||||
monday: false,
|
||||
tuesday: false,
|
||||
wednesday: false,
|
||||
thursday: false,
|
||||
friday: false,
|
||||
saturday: false,
|
||||
sunday: false,
|
||||
});
|
||||
|
||||
// The default new-clinic state: Mon–Sat open, Sun closed. Matches the
|
||||
// typical Indian outpatient hospital schedule.
|
||||
export const defaultDaySelection = (): DaySelection => ({
|
||||
monday: true,
|
||||
tuesday: true,
|
||||
wednesday: true,
|
||||
thursday: true,
|
||||
friday: true,
|
||||
saturday: true,
|
||||
sunday: false,
|
||||
});
|
||||
|
||||
// Format a DaySelection as a compact human-readable string for list
|
||||
// pages (e.g. "Mon–Fri", "Mon–Sat", "Mon Wed Fri"). Collapses
|
||||
// consecutive selected days into ranges.
|
||||
export const formatDaySelection = (sel: DaySelection): string => {
|
||||
const openKeys = DAYS.filter((d) => sel[d.key]).map((d) => d.label);
|
||||
if (openKeys.length === 0) return "Closed";
|
||||
if (openKeys.length === 7) return "Every day";
|
||||
// Monday-Friday, Monday-Saturday shorthand
|
||||
if (openKeys.length === 5 && openKeys.join(",") === "Mon,Tue,Wed,Thu,Fri") return "Mon–Fri";
|
||||
if (openKeys.length === 6 && openKeys.join(",") === "Mon,Tue,Wed,Thu,Fri,Sat") return "Mon–Sat";
|
||||
return openKeys.join(" ");
|
||||
};
|
||||
Reference in New Issue
Block a user