1- ' use client' ;
1+ " use client" ;
22
33import { Progress } from "@comp/ui/progress" ;
44import { cn } from "@comp/ui/cn" ;
55import Link from "next/link" ;
66import { Button } from "@comp/ui/button" ;
7- import type { ChecklistItemProps , OnboardingStep } from "@/app/[locale]/(app)/(dashboard)/[orgId]/implementation/types" ;
8- import { Circle , CheckCircle2 } from "lucide-react" ;
7+ import type {
8+ ChecklistItemProps ,
9+ OnboardingStep ,
10+ } from "@/app/[locale]/(app)/(dashboard)/[orgId]/implementation/types" ;
911import { usePathname , useRouter } from "next/navigation" ;
1012import { Checkbox } from "@comp/ui/checkbox" ;
1113import { markOnboardingStep } from "@/app/[locale]/(app)/(dashboard)/[orgId]/implementation/actions" ;
@@ -39,17 +41,22 @@ export function FloatingOnboardingChecklist({
3941 const remainingItems = totalItems - completedItems ;
4042 const progressPercentage = ( completedItems / totalItems ) * 100 ;
4143
42- const implementationPathRegex = / \/ [ ^ / ] + \/ i m p l e m e n t a t i o n $ / ;
44+ const implementationPathRegex = / \/ [ ^ / ] + \/ i m p l e m e n t a t i o n ( \/ . * ) ? $ / ;
4345
4446 if ( remainingItems === 0 || implementationPathRegex . test ( pathname ) ) {
4547 return null ;
4648 }
4749
48- const handleCheckedChange = ( step : OnboardingStep , newCompletedState : boolean ) => {
50+ const handleCheckedChange = (
51+ step : OnboardingStep ,
52+ newCompletedState : boolean ,
53+ ) => {
4954 // Optimistic update
5055 setChecklistItems ( ( prevItems ) =>
5156 prevItems . map ( ( item ) =>
52- item . dbColumn === step ? { ...item , completed : newCompletedState } : item ,
57+ item . dbColumn === step
58+ ? { ...item , completed : newCompletedState }
59+ : item ,
5360 ) ,
5461 ) ;
5562 setCompletedItems ( ( prevCount ) =>
@@ -58,15 +65,21 @@ export function FloatingOnboardingChecklist({
5865
5966 startTransition ( async ( ) => {
6067 try {
61- const result = await markOnboardingStep ( { orgId, step, completed : newCompletedState } ) ;
68+ const result = await markOnboardingStep ( {
69+ orgId,
70+ step,
71+ completed : newCompletedState ,
72+ } ) ;
6273 if ( ! result . success ) {
6374 throw new Error ( result . error || "Failed to update step." ) ;
6475 }
6576 // On successful action, refresh server data
66- router . refresh ( ) ;
77+ router . refresh ( ) ;
6778 } catch ( error ) {
6879 console . error ( "Onboarding step update failed:" , error ) ;
69- toast . error ( `Error: ${ error instanceof Error ? error . message : "Could not update step." } ` ) ;
80+ toast . error (
81+ `Error: ${ error instanceof Error ? error . message : "Could not update step." } ` ,
82+ ) ;
7083 // Revert optimistic update on error
7184 setChecklistItems ( initialChecklistItems ) ;
7285 setCompletedItems ( initialCompletedItems ) ;
@@ -82,7 +95,9 @@ export function FloatingOnboardingChecklist({
8295 ) }
8396 >
8497 < div className = "mb-3" >
85- < h4 className = "mb-1 font-medium leading-none" > Implementation Progress</ h4 >
98+ < h4 className = "mb-1 font-medium leading-none" >
99+ Implementation Progress
100+ </ h4 >
86101 < div className = "flex justify-between text-xs text-muted-foreground mb-2" >
87102 < span > { completedItems } Completed</ span >
88103 < span > { remainingItems } Remaining</ span >
@@ -91,35 +106,51 @@ export function FloatingOnboardingChecklist({
91106 </ div >
92107
93108 < div className = "mb-3 grid gap-2 max-h-40 overflow-y-auto" >
94- { checklistItems . map ( ( item ) => (
95- < div
96- key = { item . dbColumn }
97- className = "group flex items-center gap-3"
98- >
99- < Checkbox
100- id = { `checklist-${ item . dbColumn } ` }
101- checked = { item . completed }
102- onCheckedChange = { ( checked ) => {
103- handleCheckedChange ( item . dbColumn , ! ! checked ) ;
104- } }
105- disabled = { isPending }
106- aria-label = { `Mark ${ item . title } as ${ item . completed ? 'incomplete' : 'complete' } ` }
107- className = "shrink-0"
108- />
109- < Link
110- href = { item . href }
111- className = { cn (
112- "flex-1 cursor-pointer text-sm font-medium hover:text-primary" ,
113- item . completed && "line-through text-muted-foreground hover:text-muted-foreground" ,
114- isPending && "opacity-50 cursor-not-allowed"
115- ) }
116- tabIndex = { isPending ? - 1 : 0 }
117- aria-disabled = { isPending }
109+ { checklistItems . map ( ( item ) => {
110+ const isWizard = item . type === "wizard" ;
111+ const href = isWizard
112+ ? ( item . wizardPath ?? "#" )
113+ : ( item . href ?? "#" ) ;
114+ return (
115+ < div
116+ key = { item . dbColumn }
117+ className = "group flex items-center gap-3"
118118 >
119- { item . title }
120- </ Link >
121- </ div >
122- ) ) }
119+ < Checkbox
120+ id = { `checklist-${ item . dbColumn } ` }
121+ checked = { item . completed }
122+ onCheckedChange = { ( checked ) => {
123+ if ( isWizard ) return ; // Prevent marking wizard as done/undone
124+ handleCheckedChange (
125+ item . dbColumn as OnboardingStep ,
126+ ! ! checked ,
127+ ) ;
128+ } }
129+ disabled = { isPending || isWizard }
130+ aria-label = {
131+ isWizard
132+ ? `${ item . title } (complete in wizard)`
133+ : `Mark ${ item . title } as ${ item . completed ? "incomplete" : "complete" } `
134+ }
135+ className = "shrink-0"
136+ />
137+ < Link
138+ href = { href }
139+ className = { cn (
140+ "flex-1 cursor-pointer text-sm font-medium hover:text-primary" ,
141+ item . completed &&
142+ "line-through text-muted-foreground hover:text-muted-foreground" ,
143+ isPending &&
144+ "opacity-50 cursor-not-allowed" ,
145+ ) }
146+ tabIndex = { isPending ? - 1 : 0 }
147+ aria-disabled = { isPending }
148+ >
149+ { item . title }
150+ </ Link >
151+ </ div >
152+ ) ;
153+ } ) }
123154 </ div >
124155
125156 < Link href = { `/${ orgId } /implementation` } passHref >
@@ -135,4 +166,4 @@ export function FloatingOnboardingChecklist({
135166 </ Link >
136167 </ div >
137168 ) ;
138- }
169+ }
0 commit comments