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:
2026-04-10 08:37:34 +05:30
parent efe67dc28b
commit f57fbc1f24
25 changed files with 3461 additions and 706 deletions

View File

@@ -1,8 +1,10 @@
import type { ReactNode } from 'react';
import { useContext, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft, faArrowRight, faCircleCheck } from '@fortawesome/pro-duotone-svg-icons';
import { Button } from '@/components/base/buttons/button';
import { SETUP_STEP_LABELS, type SetupStepName } from '@/lib/setup-state';
import { WizardLayoutContext } from './wizard-layout-context';
type WizardStepProps = {
step: SetupStepName;
@@ -14,6 +16,11 @@ type WizardStepProps = {
onFinish: () => void;
saving?: boolean;
children: ReactNode;
// Optional content for the wizard shell's right preview pane.
// Portaled into the shell's <aside> via WizardLayoutContext when
// both are mounted. Each step component declares this inline so
// the per-step data fetching stays in one place.
rightPane?: ReactNode;
};
// Single-step wrapper. The parent picks which step is active and supplies
@@ -31,10 +38,14 @@ export const WizardStep = ({
onFinish,
saving = false,
children,
rightPane,
}: WizardStepProps) => {
const meta = SETUP_STEP_LABELS[step];
const { rightPaneEl } = useContext(WizardLayoutContext);
return (
<>
{rightPane && rightPaneEl && createPortal(rightPane, rightPaneEl)}
<div className="rounded-xl border border-secondary bg-primary p-8 shadow-xs">
<div className="mb-6 flex items-start justify-between gap-4">
<div>
@@ -64,30 +75,55 @@ export const WizardStep = ({
Previous
</Button>
{/* One primary action at the bottom — never two
competing buttons. Previously the wizard showed
Mark complete + Next side-by-side, and users
naturally clicked Next (rightmost = "continue"),
skipping the save+complete chain entirely. Result
was every step staying at 0/6.
New behaviour: a single button whose label and
handler depend on completion state.
- !isCompleted, not last → "Save and continue"
calls onMarkComplete (which does save +
complete + advance via the step component's
handleSave). Forces the agent through the
completion path.
- !isCompleted, last → "Save and finish"
same chain, plus onFinish at the end.
- isCompleted, not last → "Continue"
calls onNext (pure navigation).
- isCompleted, last → "Finish setup"
calls onFinish.
Free-form navigation is still available via the
left-side step nav, so users can revisit completed
steps without re-saving. */}
<div className="flex items-center gap-3">
{!isCompleted && (
{!isCompleted ? (
<Button
color="primary"
size="md"
isLoading={saving}
showTextWhileLoading
onClick={onMarkComplete}
iconTrailing={
isLast
? undefined
: ({ className }: { className?: string }) => (
<FontAwesomeIcon icon={faArrowRight} className={className} />
)
}
>
Mark complete
{isLast ? 'Save and finish' : 'Save and continue'}
</Button>
)}
{isLast ? (
<Button
color="primary"
size="md"
isDisabled={!isCompleted}
onClick={onFinish}
>
) : isLast ? (
<Button color="primary" size="md" onClick={onFinish}>
Finish setup
</Button>
) : (
<Button
color={isCompleted ? 'primary' : 'secondary'}
color="primary"
size="md"
isDisabled={!onNext}
onClick={onNext ?? undefined}
@@ -95,11 +131,12 @@ export const WizardStep = ({
<FontAwesomeIcon icon={faArrowRight} className={className} />
)}
>
Next
Continue
</Button>
)}
</div>
</div>
</div>
</>
);
};