@@ -8,10 +8,14 @@ import {
88 SpinnerGapIcon ,
99 UserIcon ,
1010} from "@phosphor-icons/react" ;
11- import { useEffect , useRef , useState } from "react" ;
11+ import { useQueryClient } from "@tanstack/react-query" ;
12+ import { useState } from "react" ;
1213import { toast } from "sonner" ;
1314import { CreateOrganizationDialog } from "@/components/organizations/create-organization-dialog" ;
14- import { useOrganizationsContext } from "@/components/providers/organizations-provider" ;
15+ import {
16+ AUTH_QUERY_KEYS ,
17+ useOrganizationsContext ,
18+ } from "@/components/providers/organizations-provider" ;
1519import { Avatar , AvatarFallback , AvatarImage } from "@/components/ui/avatar" ;
1620import { Button } from "@/components/ui/button" ;
1721import {
@@ -32,26 +36,26 @@ const getOrganizationInitials = (name: string) =>
3236 . toUpperCase ( )
3337 . slice ( 0 , 2 ) ;
3438
39+ const MENU_ITEM_BASE_CLASSES =
40+ "flex cursor-pointer items-center gap-3 px-4 py-2.5 text-sm transition-colors text-sidebar-foreground/70 hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground" ;
41+ const MENU_ITEM_ACTIVE_CLASSES =
42+ "bg-sidebar-accent font-medium text-sidebar-accent-foreground" ;
43+
3544function filterOrganizations < T extends { name : string ; slug ?: string | null } > (
3645 orgs : T [ ] | undefined ,
3746 query : string
3847) : T [ ] {
39- if ( ! orgs || orgs . length === 0 ) {
48+ if ( ! orgs ? .length ) {
4049 return [ ] ;
4150 }
4251 if ( ! query ) {
4352 return orgs ;
4453 }
4554 const q = query . toLowerCase ( ) ;
46- const filtered : T [ ] = [ ] ;
47- for ( const org of orgs ) {
48- const nameMatch = org . name . toLowerCase ( ) . includes ( q ) ;
49- const slugMatch = org . slug ? org . slug . toLowerCase ( ) . includes ( q ) : false ;
50- if ( nameMatch || slugMatch ) {
51- filtered . push ( org ) ;
52- }
53- }
54- return filtered ;
55+ return orgs . filter (
56+ ( org ) =>
57+ org . name . toLowerCase ( ) . includes ( q ) || org . slug ?. toLowerCase ( ) . includes ( q )
58+ ) ;
5559}
5660
5761type OrganizationSelectorTriggerProps = {
@@ -80,22 +84,20 @@ function OrganizationSelectorTrigger({
8084 >
8185 < div className = "flex w-full items-center justify-between" >
8286 < div className = "flex items-center gap-3" >
83- < div className = "rounded" >
84- < Avatar className = "h-5 w-5" >
85- < AvatarImage
86- alt = { activeOrganization ?. name || "Personal" }
87- className = "rounded"
88- src = { activeOrganization ?. logo || undefined }
89- />
90- < AvatarFallback className = "bg-transparent font-medium text-xs" >
91- { activeOrganization ?. name ? (
92- getOrganizationInitials ( activeOrganization . name )
93- ) : (
94- < UserIcon className = "text-sidebar-ring" weight = "duotone" />
95- ) }
96- </ AvatarFallback >
97- </ Avatar >
98- </ div >
87+ < Avatar className = "h-5 w-5" >
88+ < AvatarImage
89+ alt = { activeOrganization ?. name || "Personal" }
90+ className = "rounded"
91+ src = { activeOrganization ?. logo || undefined }
92+ />
93+ < AvatarFallback className = "bg-transparent font-medium text-xs" >
94+ { activeOrganization ?. name ? (
95+ getOrganizationInitials ( activeOrganization . name )
96+ ) : (
97+ < UserIcon className = "text-sidebar-ring" weight = "duotone" />
98+ ) }
99+ </ AvatarFallback >
100+ </ Avatar >
99101 < div className = "flex min-w-0 flex-1 flex-col items-start" >
100102 < span className = "truncate text-left font-semibold text-sidebar-accent-foreground text-sm" >
101103 { activeOrganization ?. name || "Personal" }
@@ -126,46 +128,20 @@ function OrganizationSelectorTrigger({
126128}
127129
128130export function OrganizationSelector ( ) {
131+ const queryClient = useQueryClient ( ) ;
129132 const { organizations, activeOrganization, isLoading } =
130133 useOrganizationsContext ( ) ;
131134 const [ isOpen , setIsOpen ] = useState ( false ) ;
132135 const [ showCreateDialog , setShowCreateDialog ] = useState ( false ) ;
133136 const [ query , setQuery ] = useState ( "" ) ;
134137 const [ isSwitching , setIsSwitching ] = useState ( false ) ;
135138
136- const prevStateRef = useRef < {
137- isLoading : boolean ;
138- organizationsCount : number ;
139- hasActiveOrg : boolean ;
140- activeOrgName ?: string ;
141- } | null > ( null ) ;
142-
143- useEffect ( ( ) => {
144- const currentState = {
145- isLoading,
146- organizationsCount : organizations . length ,
147- hasActiveOrg : ! ! activeOrganization ,
148- activeOrgName : activeOrganization ?. name ,
149- } ;
150-
151- const prevState = prevStateRef . current ;
152- if (
153- ! prevState ||
154- prevState . isLoading !== currentState . isLoading ||
155- prevState . organizationsCount !== currentState . organizationsCount ||
156- prevState . hasActiveOrg !== currentState . hasActiveOrg ||
157- prevState . activeOrgName !== currentState . activeOrgName
158- ) {
159- console . log ( "[OrganizationSelector] State changed:" , currentState ) ;
160- prevStateRef . current = currentState ;
161- }
162- } , [ isLoading , organizations . length , activeOrganization ] ) ;
163-
164139 const handleSelectOrganization = async ( organizationId : string | null ) => {
165- if ( organizationId === activeOrganization ?. id ) {
166- return ;
167- }
168- if ( organizationId === null && ! activeOrganization ) {
140+ const isAlreadySelected =
141+ organizationId === activeOrganization ?. id ||
142+ ( organizationId === null && ! activeOrganization ) ;
143+
144+ if ( isAlreadySelected ) {
169145 return ;
170146 }
171147
@@ -178,19 +154,20 @@ export function OrganizationSelector() {
178154
179155 if ( error ) {
180156 toast . error ( error . message || "Failed to switch workspace" ) ;
181- } else {
182- toast . success ( "Workspace updated" ) ;
157+ setIsSwitching ( false ) ;
158+ return ;
183159 }
184160
185- setIsSwitching ( false ) ;
186- } ;
161+ await queryClient . invalidateQueries ( {
162+ queryKey : AUTH_QUERY_KEYS . activeOrganization ,
163+ } ) ;
164+ queryClient . invalidateQueries ( ) ;
187165
188- const handleCreateOrganization = ( ) => {
189- setShowCreateDialog ( true ) ;
190- setIsOpen ( false ) ;
166+ setIsSwitching ( false ) ;
167+ toast . success ( "Workspace updated" ) ;
191168 } ;
192169
193- const filteredOrganizations = filterOrganizations ( organizations || [ ] , query ) ;
170+ const filteredOrganizations = filterOrganizations ( organizations , query ) ;
194171
195172 if ( isLoading ) {
196173 return (
@@ -245,10 +222,8 @@ export function OrganizationSelector() {
245222 >
246223 < DropdownMenuItem
247224 className = { cn (
248- "flex cursor-pointer items-center gap-3 px-4 py-2.5 text-sm transition-colors" ,
249- "text-sidebar-foreground/70 hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground" ,
250- ! activeOrganization &&
251- "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
225+ MENU_ITEM_BASE_CLASSES ,
226+ ! activeOrganization && MENU_ITEM_ACTIVE_CLASSES
252227 ) }
253228 onClick = { ( ) => handleSelectOrganization ( null ) }
254229 >
@@ -270,16 +245,15 @@ export function OrganizationSelector() {
270245 ) }
271246 </ DropdownMenuItem >
272247
273- { filteredOrganizations && filteredOrganizations . length > 0 && (
248+ { filteredOrganizations . length > 0 && (
274249 < div className = "flex flex-col" >
275250 < DropdownMenuSeparator className = "m-0 bg-sidebar-border p-0" />
276251 { filteredOrganizations . map ( ( org ) => (
277252 < DropdownMenuItem
278253 className = { cn (
279- "flex cursor-pointer items-center gap-3 px-4 py-2.5 text-sm transition-colors" ,
280- "text-sidebar-foreground/70 hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground" ,
254+ MENU_ITEM_BASE_CLASSES ,
281255 activeOrganization ?. id === org . id &&
282- "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
256+ MENU_ITEM_ACTIVE_CLASSES
283257 ) }
284258 key = { org . id }
285259 onClick = { ( ) => handleSelectOrganization ( org . id ) }
@@ -311,8 +285,11 @@ export function OrganizationSelector() {
311285
312286 < DropdownMenuSeparator className = "m-0 bg-sidebar-border p-0" />
313287 < DropdownMenuItem
314- className = "flex cursor-pointer items-center gap-3 px-4 py-2.5 text-sidebar-foreground/70 text-sm transition-colors hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground"
315- onClick = { handleCreateOrganization }
288+ className = { MENU_ITEM_BASE_CLASSES }
289+ onClick = { ( ) => {
290+ setShowCreateDialog ( true ) ;
291+ setIsOpen ( false ) ;
292+ } }
316293 >
317294 < PlusIcon className = "h-5 w-5 not-dark:text-primary" />
318295 < span className = "font-medium text-sm" > Create Organization</ span >
0 commit comments