mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 18:28:15 +00:00
- 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>
109 lines
4.0 KiB
TypeScript
109 lines
4.0 KiB
TypeScript
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(" ");
|
||
};
|