11import { useToast } from '@/hooks/use-toast' ;
22import { Button } from '@/components/ui/button' ;
33import { useTeams } from '@/hooks/api/useTeams' ;
4+ import { useUsers } from '@/hooks/api/useUsers' ;
45import React , { useState , useEffect } from 'react' ;
56import { useUpdateProject } from '@/hooks/api/useProjects' ;
67import { Project , AccessControlList } from '@/types/types' ;
7- import { useUsers , SanitizedUser } from '@/hooks/api/useUsers' ;
88import { X , ChevronsUpDown , Users as TeamsIcon } from 'lucide-react' ;
99import { Avatar , AvatarFallback , AvatarImage } from '@/components/ui/avatar' ;
1010import { Table , TableBody , TableCell , TableRow } from '@/components/ui/table' ;
@@ -57,8 +57,7 @@ const Selector = ({
5757 onSelect = { ( ) => {
5858 onSelect ( item . id ) ;
5959 setOpen ( false ) ;
60- } }
61- >
60+ } } >
6261 { item . name }
6362 </ CommandItem >
6463 ) ) }
@@ -98,8 +97,7 @@ const ProjectAccessForm: React.FC<ProjectAccessFormProps> = ({ project, isOpen,
9897
9998 if ( action === 'add' && ! currentList . includes ( value ) ) {
10099 currentList . push ( value ) ;
101- }
102- else if ( action === 'remove' ) {
100+ } else if ( action === 'remove' ) {
103101 ( newAccess [ list ] as any ) [ type ] = currentList . filter ( item => item !== value ) ;
104102 }
105103 return newAccess ;
@@ -120,8 +118,7 @@ const ProjectAccessForm: React.FC<ProjectAccessFormProps> = ({ project, isOpen,
120118
121119 toast ( { title : 'Success' , description : 'Project access updated successfully.' } ) ;
122120 onClose ( ) ;
123- }
124- catch ( error : any ) {
121+ } catch ( error : any ) {
125122 toast ( {
126123 title : 'Error' ,
127124 description : error . error || 'Failed to update project access.' ,
@@ -155,76 +152,79 @@ const ProjectAccessForm: React.FC<ProjectAccessFormProps> = ({ project, isOpen,
155152 />
156153 </ div >
157154 </ CardHeader >
158- < CardContent >
159- < Table >
160- < TableBody >
161- { userItems . length === 0 && teamItems . length === 0 && (
162- < TableRow >
163- < TableCell className = "text-center text-muted-foreground" > No entries.</ TableCell >
164- </ TableRow >
165- ) }
166- { userItems . map ( userId => {
167- const user = getUserById ( userId ) ;
168- return (
169- < TableRow key = { `user-${ userId } ` } >
170- < TableCell className = "flex items-center gap-2" >
171- < Avatar className = "h-6 w-6" >
172- < AvatarImage src = { user ?. profileImage } />
173- < AvatarFallback > { user ?. username . substring ( 0 , 2 ) . toUpperCase ( ) } </ AvatarFallback >
174- </ Avatar >
175- < span > { user ?. username || 'Unknown User' } </ span >
176- </ TableCell >
177- < TableCell className = "text-right" >
178- < Button
179- variant = "ghost"
180- size = "icon"
181- onClick = { ( ) => handleModify ( 'remove' , list , 'users' , userId ) }
182- >
183- < X className = "h-4 w-4" />
184- </ Button >
185- </ TableCell >
186- </ TableRow >
187- ) ;
188- } ) }
189- { teamItems . map ( teamId => {
190- const team = allTeams . find ( t => t . id === teamId ) ;
191- return (
192- < TableRow key = { `team-${ teamId } ` } >
193- < TableCell className = "flex items-center gap-2" >
194- < TeamsIcon className = "h-5 w-5 text-muted-foreground" />
195- < span > { team ?. name || 'Unknown Team' } </ span >
196- </ TableCell >
197- < TableCell className = "text-right" >
198- < Button
199- variant = "ghost"
200- size = "icon"
201- onClick = { ( ) => handleModify ( 'remove' , list , 'teams' , teamId ) }
202- >
203- < X className = "h-4 w-4" />
204- </ Button >
205- </ TableCell >
206- </ TableRow >
207- ) ;
208- } ) }
209- </ TableBody >
210- </ Table >
155+ < CardContent className = "p-0" >
156+ < div className = "max-h-32 overflow-y-auto border-t" >
157+ { userItems . length === 0 && teamItems . length === 0 ? (
158+ < div className = "p-4 text-center text-muted-foreground text-sm" > No entries.</ div >
159+ ) : (
160+ < Table >
161+ < TableBody >
162+ { userItems . map ( userId => {
163+ const user = getUserById ( userId ) ;
164+ return (
165+ < TableRow key = { `user-${ userId } ` } >
166+ < TableCell className = "flex items-center gap-2 py-2" >
167+ < Avatar className = "h-6 w-6" >
168+ < AvatarImage src = { user ?. profileImage } />
169+ < AvatarFallback className = "text-xs" >
170+ { user ?. username . substring ( 0 , 2 ) . toUpperCase ( ) }
171+ </ AvatarFallback >
172+ </ Avatar >
173+ < span className = "text-sm" > { user ?. username || 'Unknown User' } </ span >
174+ </ TableCell >
175+ < TableCell className = "text-right py-2" >
176+ < Button
177+ variant = "ghost"
178+ size = "sm"
179+ className = "h-6 w-6 p-0"
180+ onClick = { ( ) => handleModify ( 'remove' , list , 'users' , userId ) } >
181+ < X className = "h-3 w-3" />
182+ </ Button >
183+ </ TableCell >
184+ </ TableRow >
185+ ) ;
186+ } ) }
187+
188+ { teamItems . map ( teamId => {
189+ const team = allTeams . find ( t => t . id === teamId ) ;
190+ return (
191+ < TableRow key = { `team-${ teamId } ` } >
192+ < TableCell className = "flex items-center gap-2 py-2" >
193+ < TeamsIcon className = "h-4 w-4 text-muted-foreground" />
194+ < span className = "text-sm" > { team ?. name || 'Unknown Team' } </ span >
195+ </ TableCell >
196+ < TableCell className = "text-right py-2" >
197+ < Button
198+ variant = "ghost"
199+ size = "sm"
200+ className = "h-6 w-6 p-0"
201+ onClick = { ( ) => handleModify ( 'remove' , list , 'teams' , teamId ) } >
202+ < X className = "h-3 w-3" />
203+ </ Button >
204+ </ TableCell >
205+ </ TableRow >
206+ ) ;
207+ } ) }
208+ </ TableBody >
209+ </ Table >
210+ ) }
211+ </ div >
211212 </ CardContent >
212213 </ Card >
213214 ) ;
214215 } ;
215216
216217 return (
217218 < Dialog open = { isOpen } onOpenChange = { onClose } >
218- < DialogContent className = "max-w-4xl" >
219- < DialogHeader >
219+ < DialogContent className = "max-w-4xl max-h-[90vh] overflow-hidden flex flex-col " >
220+ < DialogHeader className = "flex-shrink-0" >
220221 < DialogTitle > Manage Access for "{ project . name } "</ DialogTitle >
221-
222222 < DialogDescription >
223223 Control who can see and manage this project. Changes are not saved until you click the save button.
224224 </ DialogDescription >
225225 </ DialogHeader >
226226
227- < div className = "space-y-6 py-4" >
227+ < div className = "flex-1 overflow-y-auto space-y-4 py-4" >
228228 { renderAccessList ( 'Project Owners' , 'owners' ) }
229229 { renderAccessList ( 'General Access' , 'allow' ) }
230230
@@ -239,52 +239,50 @@ const ProjectAccessForm: React.FC<ProjectAccessFormProps> = ({ project, isOpen,
239239 />
240240 </ CardHeader >
241241
242- < CardContent >
243- < Table >
244- < TableBody >
245- { access . deny . users . length > 0 ? (
246- access . deny . users . map ( userId => {
247- const user = getUserById ( userId ) ;
248-
249- return (
250- < TableRow key = { `deny-${ userId } ` } >
251- < TableCell className = "flex items-center gap-2" >
252- < Avatar className = "h-6 w-6" >
253- < AvatarImage src = { user ?. profileImage } />
254- < AvatarFallback > { user ?. username . substring ( 0 , 2 ) . toUpperCase ( ) } </ AvatarFallback >
255- </ Avatar >
256-
257- < span > { user ?. username || 'Unknown User' } </ span >
258- </ TableCell >
259-
260- < TableCell className = "text-right" >
261- < Button
262- variant = "ghost"
263- size = "icon"
264- onClick = { ( ) => handleModify ( 'remove' , 'deny' , 'users' , userId ) }
265- >
266- < X className = "h-4 w-4" />
267- </ Button >
268- </ TableCell >
269- </ TableRow >
270- ) ;
271- } )
272- ) : (
273- < TableRow >
274- < TableCell className = "text-center text-muted-foreground" > No entries.</ TableCell >
275- </ TableRow >
276- ) }
277- </ TableBody >
278- </ Table >
242+ < CardContent className = "p-0" >
243+ < div className = "max-h-32 overflow-y-auto border-t" >
244+ { access . deny . users . length > 0 ? (
245+ < Table >
246+ < TableBody >
247+ { access . deny . users . map ( userId => {
248+ const user = getUserById ( userId ) ;
249+ return (
250+ < TableRow key = { `deny-${ userId } ` } >
251+ < TableCell className = "flex items-center gap-2 py-2" >
252+ < Avatar className = "h-6 w-6" >
253+ < AvatarImage src = { user ?. profileImage } />
254+ < AvatarFallback className = "text-xs" >
255+ { user ?. username . substring ( 0 , 2 ) . toUpperCase ( ) }
256+ </ AvatarFallback >
257+ </ Avatar >
258+ < span className = "text-sm" > { user ?. username || 'Unknown User' } </ span >
259+ </ TableCell >
260+ < TableCell className = "text-right py-2" >
261+ < Button
262+ variant = "ghost"
263+ size = "sm"
264+ className = "h-6 w-6 p-0"
265+ onClick = { ( ) => handleModify ( 'remove' , 'deny' , 'users' , userId ) } >
266+ < X className = "h-3 w-3" />
267+ </ Button >
268+ </ TableCell >
269+ </ TableRow >
270+ ) ;
271+ } ) }
272+ </ TableBody >
273+ </ Table >
274+ ) : (
275+ < div className = "p-4 text-center text-muted-foreground text-sm" > No entries.</ div >
276+ ) }
277+ </ div >
279278 </ CardContent >
280279 </ Card >
281280 </ div >
282281
283- < DialogFooter >
282+ < DialogFooter className = "flex-shrink-0" >
284283 < Button variant = "outline" onClick = { onClose } >
285284 Cancel
286285 </ Button >
287-
288286 < Button onClick = { handleSubmit } disabled = { updateProjectMutation . isPending } >
289287 { updateProjectMutation . isPending ? 'Saving...' : 'Save Changes' }
290288 </ Button >
0 commit comments