@@ -4,6 +4,12 @@ import { Progress } from "@comp/ui/progress";
44import { cn } from "@comp/ui/cn" ;
55import Link from "next/link" ;
66import { Button } from "@comp/ui/button" ;
7+ import {
8+ Collapsible ,
9+ CollapsibleTrigger ,
10+ CollapsibleContent ,
11+ } from "@comp/ui/collapsible" ;
12+ import { ChevronDown } from "lucide-react" ;
713import type {
814 ChecklistItemProps ,
915 OnboardingStep ,
@@ -29,6 +35,7 @@ export function FloatingOnboardingChecklist({
2935 checklistItems : initialChecklistItems ,
3036 className,
3137} : FloatingOnboardingChecklistProps ) {
38+ const [ open , setOpen ] = useState ( true ) ;
3239 const pathname = usePathname ( ) ;
3340 const router = useRouter ( ) ;
3441 const [ isPending , startTransition ] = useTransition ( ) ;
@@ -88,82 +95,114 @@ export function FloatingOnboardingChecklist({
8895 } ;
8996
9097 return (
91- < div
92- className = { cn (
93- "fixed bottom-4 right-4 z-50 w-80 rounded-sm border bg-card p-4 text-card-foreground shadow-lg" ,
94- className ,
95- ) }
96- >
97- < div className = "mb-3" >
98- < h4 className = "mb-1 font-medium leading-none" >
99- Implementation Progress
100- </ h4 >
101- < div className = "flex justify-between text-xs text-muted-foreground mb-2" >
102- < span > { completedItems } Completed</ span >
103- < span > { remainingItems } Remaining</ span >
98+ < Collapsible open = { open } onOpenChange = { setOpen } >
99+ < div
100+ className = { cn (
101+ "bottom-4 right-4 z-50 w-80 rounded-sm border bg-card text-card-foreground shadow-lg relative" ,
102+ className ,
103+ ) }
104+ style = { { position : "fixed" } }
105+ >
106+ { /* Chevron at true top right, outside padding */ }
107+ < CollapsibleTrigger asChild >
108+ < span
109+ role = "button"
110+ tabIndex = { 0 }
111+ aria-label = {
112+ open ? "Minimize checklist" : "Expand checklist"
113+ }
114+ className = "absolute top-1 right-1 m-1 z-10 cursor-pointer outline-none rounded hover:bg-muted focus:bg-muted transition-colors"
115+ style = { {
116+ padding : 2 ,
117+ display : "inline-flex" ,
118+ alignItems : "center" ,
119+ justifyContent : "center" ,
120+ } }
121+ onKeyDown = { ( e ) => {
122+ if ( e . key === "Enter" || e . key === " " ) {
123+ e . preventDefault ( ) ;
124+ setOpen ( ! open ) ;
125+ }
126+ } }
127+ >
128+ < ChevronDown
129+ className = { `size-5 transition-transform ${ open ? "" : "-rotate-180" } ` }
130+ />
131+ </ span >
132+ </ CollapsibleTrigger >
133+ { /* Padded card content below */ }
134+ < div className = "p-4" >
135+ < h4 className = "mb-1 font-medium leading-none" >
136+ Implementation Progress
137+ </ h4 >
138+ < div className = "flex justify-between text-xs text-muted-foreground mb-2" >
139+ < span > { completedItems } Completed</ span >
140+ < span > { remainingItems } Remaining</ span >
141+ </ div >
142+ < Progress value = { progressPercentage } className = "h-1.5" />
104143 </ div >
105- < Progress value = { progressPercentage } className = "h-1.5" />
106- </ div >
144+ < CollapsibleContent className = "p-4" >
145+ < div className = "mb-3 grid gap-2 max-h-40 overflow-y-auto" >
146+ { checklistItems . map ( ( item ) => {
147+ const isWizard = item . type === "wizard" ;
148+ const href = isWizard
149+ ? ( item . wizardPath ?? "#" )
150+ : ( item . href ?? "#" ) ;
151+ return (
152+ < div
153+ key = { `checklist-${ item . dbColumn } ` }
154+ className = "group flex items-center gap-3"
155+ >
156+ < Checkbox
157+ id = { `checklist-${ item . dbColumn } ` }
158+ checked = { item . completed }
159+ onCheckedChange = { ( checked ) => {
160+ if ( isWizard ) return ; // Prevent marking wizard as done/undone
161+ handleCheckedChange (
162+ item . dbColumn as OnboardingStep ,
163+ ! ! checked ,
164+ ) ;
165+ } }
166+ disabled = { isPending || isWizard }
167+ aria-label = {
168+ isWizard
169+ ? `${ item . title } (complete in wizard)`
170+ : `Mark ${ item . title } as ${ item . completed ? "incomplete" : "complete" } `
171+ }
172+ className = "shrink-0"
173+ />
174+ < Link
175+ href = { href }
176+ className = { cn (
177+ "flex-1 cursor-pointer text-sm font-medium hover:text-primary" ,
178+ item . completed &&
179+ "line-through text-muted-foreground hover:text-muted-foreground" ,
180+ isPending &&
181+ "opacity-50 cursor-not-allowed" ,
182+ ) }
183+ tabIndex = { isPending ? - 1 : 0 }
184+ aria-disabled = { isPending }
185+ >
186+ { item . title }
187+ </ Link >
188+ </ div >
189+ ) ;
190+ } ) }
191+ </ div >
107192
108- < div className = "mb-3 grid gap-2 max-h-40 overflow-y-auto" >
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 = { `checklist-${ item . dbColumn } ` }
117- className = "group flex items-center gap-3"
193+ < Link href = { `/${ orgId } /implementation` } passHref >
194+ < Button
195+ variant = "secondary"
196+ size = "sm"
197+ className = "w-full"
198+ aria-disabled = { isPending }
199+ disabled = { isPending }
118200 >
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- } ) }
201+ View All Steps
202+ </ Button >
203+ </ Link >
204+ </ CollapsibleContent >
154205 </ div >
155-
156- < Link href = { `/${ orgId } /implementation` } passHref >
157- < Button
158- variant = "secondary"
159- size = "sm"
160- className = "w-full"
161- aria-disabled = { isPending }
162- disabled = { isPending }
163- >
164- View All Steps
165- </ Button >
166- </ Link >
167- </ div >
206+ </ Collapsible >
168207 ) ;
169208}
0 commit comments