11"use client"
22
33import React from "react" ;
4- import type { Namespace , NamespaceRole , NamespaceRoleAbility } from "@code0-tech/sagittarius-graphql-types" ;
4+ import type { Namespace , NamespaceRole , NamespaceRoleAbility , Scalars } from "@code0-tech/sagittarius-graphql-types" ;
55import { useParams } from "next/navigation" ;
66import {
7+ Alert ,
78 AuroraBackground ,
89 Button ,
910 Card ,
1011 CheckboxInput ,
1112 Col ,
1213 DLayout ,
14+ DNamespaceProjectView ,
1315 Flex ,
1416 Row ,
1517 Spacing ,
@@ -24,6 +26,9 @@ import {ProjectService} from "@edition/project/Project.service";
2426import { Tab , TabContent , TabList , TabTrigger } from "@code0-tech/pictor/dist/components/tab/Tab" ;
2527import CardSection from "@code0-tech/pictor/dist/components/card/CardSection" ;
2628import { DNamespaceRolePermissions } from "@code0-tech/pictor/dist/components/d-role/DNamespaceRolePermissions" ;
29+ import DNamespaceProjectMenu from "@code0-tech/pictor/dist/components/d-project/DNamespaceProjectMenu" ;
30+ import { DNamespaceProjectContent } from "@code0-tech/pictor/dist/components/d-project/DNamespaceProjectContent" ;
31+ import { IconTrash } from "@tabler/icons-react" ;
2732
2833type Permission = {
2934 label : string
@@ -199,8 +204,14 @@ export const RoleSettingsPage: React.FC = () => {
199204 }
200205 } , [ role ] )
201206
207+ const roleAssignedProjects = React . useMemo ( ( ) => role ?. assignedProjects ?. nodes ?. map ( p => p ?. id ! ! ) ?? [ ] , [ role ] )
208+ const [ assignedProjectIds , setAssignedProjectIds ] = React . useState < Scalars [ 'NamespaceProjectID' ] [ 'output' ] [ ] > ( roleAssignedProjects )
202209 const [ initialValues , setInitialValues ] = React . useState ( roleAbilities )
203210
211+ React . useEffect ( ( ) => {
212+ setAssignedProjectIds ( roleAssignedProjects )
213+ } , [ role ] )
214+
204215 React . useEffect ( ( ) => {
205216 setInitialValues ( roleAbilities )
206217 } , [ role ] )
@@ -229,6 +240,39 @@ export const RoleSettingsPage: React.FC = () => {
229240 }
230241 } )
231242
243+ const assignProjects = ( ) => {
244+ startTransition ( ( ) => {
245+ roleService . roleAssignProject ( {
246+ roleId : roleId ,
247+ projectIds : assignedProjectIds as Scalars [ 'NamespaceProjectID' ] [ 'output' ] [ ]
248+ } ) . then ( payload => {
249+ if ( ( payload ?. errors ?. length ?? 0 ) <= 0 ) {
250+ toast ( {
251+ title : "All projects were successfully assigned to the role." ,
252+ color : "success" ,
253+ dismissible : true ,
254+ } )
255+ }
256+ } )
257+ } )
258+ }
259+
260+ const addAssignedProject = ( projectId : Scalars [ 'NamespaceProjectID' ] [ 'output' ] ) => {
261+ setAssignedProjectIds ( prevState => {
262+ return [ ...prevState , projectId ]
263+ } )
264+ }
265+
266+ const removeAssignedProject = ( projectId : Scalars [ 'NamespaceProjectID' ] [ 'output' ] ) => {
267+ setAssignedProjectIds ( prevState => {
268+ return prevState . filter ( id => id !== projectId )
269+ } )
270+ }
271+
272+ const filterProjects = React . useCallback ( ( project : DNamespaceProjectView ) => {
273+ return ! assignedProjectIds . find ( projectId => projectId == project . id ! ! )
274+ } , [ assignedProjectIds ] )
275+
232276 return < >
233277 < Spacing spacing = { "xl" } />
234278 < Flex style = { { gap : "0.7rem" } } align = { "center" } >
@@ -247,23 +291,21 @@ export const RoleSettingsPage: React.FC = () => {
247291 </ TabTrigger >
248292 < TabTrigger value = { "project" } asChild >
249293 < Button paddingSize = { "xxs" } variant = { "none" } >
250- < Text size = { "md" } hierarchy = { "primary" } > Assigned projects</ Text >
294+ < Text size = { "md" } hierarchy = { "primary" } > Limit to projects</ Text >
251295 </ Button >
252296 </ TabTrigger >
253297 </ TabList >
254298 } >
255299 < >
256300 < TabContent value = { "permission" } style = { { overflow : "hidden" } } >
257- < Text size = { "xl" } hierarchy = { "primary" } style = { { fontWeight : 600 } } > Select from role
258- templates</ Text >
301+ < Text size = { "xl" } hierarchy = { "primary" } style = { { fontWeight : 600 } } >
302+ Select from role templates
303+ </ Text >
259304 < Spacing spacing = { "xl" } />
260305 { React . useMemo ( ( ) => {
261306 return < Row >
262307 { permissionTemplates . map ( permissionTemplate => {
263308
264- //schreibe mir hier eine variable isSelected die true ist wenn die permissionTemplate.abilities genau den gleichen inhalten entsprechen wie die aktuellen rule abilities hat
265-
266-
267309 const templateAbilities = Object . entries ( permissionTemplate . abilities )
268310 . filter ( ( [ _ , enabled ] ) => enabled )
269311 . map ( ( [ ability ] ) => ability as NamespaceRoleAbility ) ;
@@ -298,10 +340,17 @@ export const RoleSettingsPage: React.FC = () => {
298340 </ Row >
299341 } , [ initialValues , role ] ) }
300342 < Spacing spacing = { "xl" } />
301- < Text size = { "xl" } hierarchy = { "primary" } style = { { fontWeight : 600 } } > Current stored
302- permissions</ Text >
303- < Spacing spacing = { "xs" } />
304- < DNamespaceRolePermissions abilities = { role ?. abilities ! ! } />
343+ < Flex align = { "center" } justify = { "space-between" } >
344+ < div >
345+ < Text size = { "xl" } hierarchy = { "primary" } style = { { fontWeight : 600 } } > Current stored
346+ permissions</ Text >
347+ < Spacing spacing = { "xs" } />
348+ < DNamespaceRolePermissions abilities = { role ?. abilities ! ! } />
349+ </ div >
350+ < Button color = { "success" } onClick = { validate } >
351+ Update role permissions
352+ </ Button >
353+ </ Flex >
305354 < Spacing spacing = { "xl" } />
306355 < Card p = { 1.3 } >
307356 { permissions . map ( permissionGroup => {
@@ -322,12 +371,44 @@ export const RoleSettingsPage: React.FC = () => {
322371 </ CardSection >
323372 } ) }
324373 </ Card >
325- < Spacing spacing = { "xl" } />
326- < Flex justify = { "end" } >
327- < Button color = { "success" } onClick = { validate } >
328- Update role permissions
329- </ Button >
374+ </ TabContent >
375+ < TabContent value = { "project" } style = { { overflow : "hidden" } } >
376+ < Flex align = { "center" } justify = { "space-between" } >
377+ < Text size = { "xl" } hierarchy = { "primary" } style = { { fontWeight : 600 } } > Projects that members can
378+ access</ Text >
379+ < Flex align = { "center" } style = { { gap : ".7rem" } } >
380+ < Button color = { "success" } onClick = { assignProjects } > Update assigned projects</ Button >
381+ < DNamespaceProjectMenu namespaceId = { namespaceId }
382+ key = { String ( assignedProjectIds ) }
383+ filter = { filterProjects }
384+ onProjectSelect = { ( project ) => addAssignedProject ( project . id ! ! ) } >
385+ < Button > Add project</ Button >
386+ </ DNamespaceProjectMenu >
387+ </ Flex >
330388 </ Flex >
389+
390+ < Spacing spacing = { "xl" } />
391+ { ( assignedProjectIds . length ?? 0 ) <= 0 ? (
392+ < Alert color = { "info" } >
393+ < Text style = { { textAlign : "center" } } size = { "md" } hierarchy = { "secondary" } >
394+ This role has no project assignments. Members with this role will have access to all
395+ projects in the organization namespace.
396+ </ Text >
397+ </ Alert >
398+ ) : (
399+ < Card >
400+ { assignedProjectIds . map ( projectId => {
401+ return < CardSection key = { projectId } border >
402+ < Flex align = { "center" } style = { { gap : "1.3rem" } } justify = { "space-between" } >
403+ < DNamespaceProjectContent minimized projectId = { projectId } />
404+ < Button color = { "error" } variant = { "filled" } onClick = { ( ) => removeAssignedProject ( projectId ) } >
405+ < IconTrash size = { 16 } />
406+ </ Button >
407+ </ Flex >
408+ </ CardSection >
409+ } ) }
410+ </ Card >
411+ ) }
331412 </ TabContent >
332413 </ >
333414 </ DLayout >
0 commit comments