Skip to content

Commit d451e89

Browse files
authored
Merge pull request #539 from code0-tech/feat/adjust-member-content
Adjust member content
2 parents 2aeeda1 + f1e858a commit d451e89

File tree

6 files changed

+133
-73
lines changed

6 files changed

+133
-73
lines changed

src/components/d-member/DNamespaceMember.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ export const MemberListExample = () => {
5050
}]
5151
},
5252
userAbilities: {
53-
deleteMember: true
53+
deleteMember: true,
54+
assignMemberRoles: true
5455
}
5556
})
5657
])

src/components/d-member/DNamespaceMemberCard.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {Card} from "../card/Card";
33
import React from "react";
44
import {DNamespaceMemberContent} from "./DNamespaceMemberContent";
55
import {DNamespaceMemberView} from "./DNamespaceMember.view";
6+
import {DNamespaceRoleView} from "../d-role";
67

78
export interface DNamespaceMemberCardProps {
89
memberId: NamespaceMember["id"]
910
onRemove?: (member: DNamespaceMemberView) => void
10-
onAssignRole?: (member: DNamespaceMemberView) => void
11+
onAssignRole?: (member: DNamespaceMemberView, roles: DNamespaceRoleView[]) => void
1112
}
1213

1314
export const DNamespaceMemberCard: React.FC<DNamespaceMemberCardProps> = (props) => {

src/components/d-member/DNamespaceMemberContent.tsx

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
import React from "react";
2-
import {Flex} from "../flex/Flex";
3-
import {NamespaceMember} from "@code0-tech/sagittarius-graphql-types";
4-
import {useService, useStore} from "../../utils";
5-
import {DNamespaceMemberReactiveService} from "./DNamespaceMember.service";
6-
import {DUserReactiveService} from "../d-user";
7-
import {DNamespaceRoleReactiveService} from "../d-role";
8-
import {Avatar} from "../avatar/Avatar";
9-
import {Text} from "../text/Text";
10-
import {Badge} from "../badge/Badge";
11-
import {IconDots, IconMailCheck, IconUserCog, IconUserOff} from "@tabler/icons-react";
12-
import {Button} from "../button/Button";
13-
import {Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger} from "../tooltip/Tooltip";
14-
import {DNamespaceRolePermissions} from "../d-role/DNamespaceRolePermissions";
15-
import {Menu, MenuContent, MenuItem, MenuLabel, MenuPortal, MenuTrigger} from "../menu/Menu";
16-
import {DNamespaceMemberView} from "./DNamespaceMember.view";
1+
import React from "react"
2+
import {Flex} from "../flex/Flex"
3+
import {NamespaceMember} from "@code0-tech/sagittarius-graphql-types"
4+
import {useService, useStore} from "../../utils"
5+
import {DNamespaceMemberReactiveService} from "./DNamespaceMember.service"
6+
import {DUserReactiveService} from "../d-user"
7+
import {DNamespaceRoleReactiveService, DNamespaceRoleView} from "../d-role"
8+
import {Avatar} from "../avatar/Avatar"
9+
import {Text} from "../text/Text"
10+
import {Badge} from "../badge/Badge"
11+
import {IconDots, IconMailCheck, IconTrash, IconUserCog, IconUserOff} from "@tabler/icons-react"
12+
import {Button} from "../button/Button"
13+
import {Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger} from "../tooltip/Tooltip"
14+
import {DNamespaceRolePermissions} from "../d-role/DNamespaceRolePermissions"
15+
import {Menu, MenuContent, MenuItem, MenuLabel, MenuPortal, MenuSeparator, MenuTrigger} from "../menu/Menu"
16+
import {DNamespaceMemberView} from "./DNamespaceMember.view"
17+
import {Dialog, DialogClose, DialogContent, DialogPortal} from "../dialog/Dialog"
18+
import {Card} from "../card/Card"
19+
import CardSection from "../card/CardSection"
1720

1821
export interface DNamespaceMemberContentProps {
1922
memberId: NamespaceMember['id']
2023
onRemove?: (member: DNamespaceMemberView) => void
21-
onAssignRole?: (member: DNamespaceMemberView) => void
24+
onAssignRole?: (member: DNamespaceMemberView, roles: DNamespaceRoleView[]) => void
2225
}
2326

2427
export const DNamespaceMemberContent: React.FC<DNamespaceMemberContentProps> = (props) => {
@@ -35,8 +38,92 @@ export const DNamespaceMemberContent: React.FC<DNamespaceMemberContentProps> = (
3538
const member = React.useMemo(() => memberService.getById(memberId), [memberStore, memberId])
3639
const user = React.useMemo(() => userService.getById(member?.user?.id), [userStore, member])
3740
const assignedRoles = React.useMemo(() => member?.roles?.nodes?.map(role => roleService.getById(role?.id, {namespaceId: member?.namespace?.id})) || [], [roleStore, member])
41+
const [localAssignedRoles, setLocalAssignedRoles] = React.useState(assignedRoles)
42+
const [openRemovedMemberDialog, setOpenRemovedMemberDialog] = React.useState(false)
43+
const [openAssignRolesDialog, setOpenAssignRolesDialog] = React.useState(false)
44+
const rolesToAssign = roleService
45+
.values({namespaceId: member?.namespace?.id})
46+
.filter(role => !localAssignedRoles.find(aRole => aRole?.id === role.id))
3847

3948
return <Flex align={"center"} style={{gap: "1.3rem"}} justify={"space-between"}>
49+
<Dialog open={openRemovedMemberDialog} onOpenChange={open => setOpenRemovedMemberDialog(open)}>
50+
<DialogPortal>
51+
<DialogContent showCloseButton title={"Remove member"}>
52+
<Text size={"md"} hierarchy={"secondary"}>
53+
Are you sure you want to remove {" "}
54+
<Badge color={"info"}>
55+
<Text size={"md"} style={{color: "inherit"}}>@{user?.username}</Text>
56+
</Badge> {" "}
57+
from the namespace members?
58+
</Text>
59+
<Flex justify={"space-between"} align={"center"}>
60+
<DialogClose asChild>
61+
<Button color={"secondary"}>No, go back!</Button>
62+
</DialogClose>
63+
<Button color={"error"} onClick={() => onRemove(member!!)}>Yes, remove!</Button>
64+
</Flex>
65+
</DialogContent>
66+
</DialogPortal>
67+
</Dialog>
68+
<Dialog open={openAssignRolesDialog} onOpenChange={open => setOpenAssignRolesDialog(open)}>
69+
<DialogPortal>
70+
<DialogContent autoFocus showCloseButton title={"Assign roles"}>
71+
<Text size={"md"} hierarchy={"tertiary"}>Assign, remove and manage the roles of a active
72+
member</Text>
73+
<Card paddingSize={"xs"} color={"secondary"}>
74+
{localAssignedRoles.map(role => {
75+
return <CardSection border key={role?.id}>
76+
<Flex style={{gap: "0.7rem"}} align={"center"} justify={"space-between"}>
77+
<Flex align={"center"} style={{gap: "1.3rem"}}>
78+
<Text hierarchy={"primary"}>{role?.name}</Text>
79+
<DNamespaceRolePermissions abilities={role?.abilities!!}/>
80+
</Flex>
81+
<Button color={"error"} paddingSize={"xxs"} onClick={() => {
82+
setLocalAssignedRoles(prevState => prevState.filter(aRole => aRole?.id != role?.id))
83+
}}>
84+
<IconTrash size={16}/>
85+
</Button>
86+
</Flex>
87+
88+
</CardSection>
89+
})}
90+
<Menu>
91+
<MenuTrigger asChild>
92+
<CardSection hover p={0.35} border display={"flex"} justify={"center"}>
93+
<Button paddingSize={"xxs"} variant={"none"}>Assign roles</Button>
94+
</CardSection>
95+
</MenuTrigger>
96+
<MenuPortal>
97+
<MenuContent side={"bottom"} sideOffset={8} align={"center"}>
98+
<MenuLabel>Roles to add</MenuLabel>
99+
{rolesToAssign.map((role, index) => {
100+
return <>
101+
<MenuItem onSelect={() => {
102+
setLocalAssignedRoles(prevState => [...prevState, role])
103+
}}>
104+
<Flex align={"center"} style={{gap: "1.3rem"}}>
105+
<Text hierarchy={"primary"}>{role?.name}</Text>
106+
<DNamespaceRolePermissions abilities={role?.abilities!!}/>
107+
</Flex>
108+
</MenuItem>
109+
{index < rolesToAssign.length - 1 && <MenuSeparator/>}
110+
</>
111+
})}
112+
</MenuContent>
113+
</MenuPortal>
114+
</Menu>
115+
</Card>
116+
<Flex justify={"space-between"} align={"center"}>
117+
<DialogClose asChild>
118+
<Button color={"secondary"}>No, go back!</Button>
119+
</DialogClose>
120+
121+
<Button onClick={() => onAssignRole(member!!, localAssignedRoles as DNamespaceRoleView[])}
122+
color={"success"}>Yes, save!</Button>
123+
</Flex>
124+
</DialogContent>
125+
</DialogPortal>
126+
</Dialog>
40127
<Flex style={{gap: "1.3rem"}} align={"center"}>
41128
<Flex align={"center"} style={{gap: ".7rem"}}>
42129
<Avatar identifier={user?.username!!} bg={"transparent"}/>
@@ -96,13 +183,13 @@ export const DNamespaceMemberContent: React.FC<DNamespaceMemberContentProps> = (
96183
<MenuContent align={"end"} side={"bottom"} sideOffset={8}>
97184
<MenuLabel>Actions</MenuLabel>
98185
{member?.userAbilities?.deleteMember && (
99-
<MenuItem onSelect={() => onRemove(member)}>
186+
<MenuItem onSelect={() => setOpenRemovedMemberDialog(true)}>
100187
<IconUserOff size={16}/>
101188
<Text>Remove member</Text>
102189
</MenuItem>
103190
)}
104191
{member?.userAbilities?.assignMemberRoles && (
105-
<MenuItem onSelect={() => onAssignRole(member)}>
192+
<MenuItem onSelect={() => setOpenAssignRolesDialog(true)}>
106193
<IconUserCog size={16}/>
107194
<Text>Assign role</Text>
108195
</MenuItem>

src/components/d-member/DNamespaceMemberList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import {Card} from "../card/Card";
88
import CardSection from "../card/CardSection";
99
import {DNamespaceMemberView} from "./DNamespaceMember.view";
1010
import {DNamespaceMemberContent} from "./DNamespaceMemberContent";
11+
import {DNamespaceRoleView} from "../d-role";
1112

1213
export interface DNamespaceMemberListProps extends Omit<Card, "children" | "onSelect"> {
1314
namespaceId: Namespace["id"]
1415
filter?: (runtime: DNamespaceMemberView, index: number) => boolean
1516
onRemove?: (member: DNamespaceMemberView) => void
16-
onAssignRole?: (member: DNamespaceMemberView) => void
17+
onAssignRole?: (member: DNamespaceMemberView, roles: DNamespaceRoleView[]) => void
1718
}
1819

1920
export const DNamespaceMemberList: React.FC<DNamespaceMemberListProps> = (props) => {

src/components/dialog/Dialog.style.scss

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
@use "sass:color";
66

77
.dialog {
8-
@include box.box(variables.$primary, variables.$primary, variables.$secondary);
9-
@include helpers.borderRadius();
10-
118
backdrop-filter: none;
129
-webkit-backdrop-filter: none;
1310

@@ -26,10 +23,13 @@
2623
bottom: 0;
2724
left: 0;
2825
z-index: 50;
29-
background: rgba(0, 0, 0, 0.5);
26+
background: rgba(variables.$primary, 0.75);
3027
}
3128

3229
&__content {
30+
@include box.box(variables.$secondary);
31+
@include helpers.borderRadius();
32+
3333
position: fixed;
3434
top: 50%;
3535
left: 50%;
@@ -39,15 +39,10 @@
3939
max-width: calc(100% - 2rem);
4040
gap: 1rem;
4141
transform: translate(-50%, -50%);
42-
border: 1px solid helpers.borderColor();
4342
padding: 1rem;
44-
background: variables.$primary;
45-
color: variables.$secondary;
46-
47-
@include helpers.borderRadius();
4843

4944
@include media.respond-above(md) {
50-
width: 420px;
45+
width: 490px;
5146
}
5247
}
5348

@@ -87,38 +82,4 @@
8782
justify-content: flex-end;
8883
}
8984
}
90-
91-
&__close {
92-
position: absolute;
93-
top: 1rem;
94-
right: 1rem;
95-
border-radius: 0.125rem;
96-
border: none;
97-
opacity: 0.7;
98-
transition: opacity 0.2s ease;
99-
background: none;
100-
color: variables.$secondary;
101-
102-
&:hover {
103-
opacity: 1;
104-
}
105-
106-
&:focus {
107-
outline: none;
108-
}
109-
110-
&:disabled {
111-
pointer-events: none;
112-
}
113-
114-
svg {
115-
pointer-events: none;
116-
flex-shrink: 0;
117-
}
118-
119-
svg:not([class*="size-"]) {
120-
width: 1rem;
121-
height: 1rem;
122-
}
123-
}
12485
}

src/components/dialog/Dialog.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import * as DialogPrimitive from "@radix-ui/react-dialog"
2-
import {Code0Component, Code0ComponentProps} from "../../utils/types";
3-
import React, {useEffect} from "react";
4-
import {mergeCode0Props} from "../../utils/utils";
1+
import * as DialogPrimitive from "@radix-ui/react-dialog"
2+
import {Code0Component, Code0ComponentProps} from "../../utils";
3+
import React from "react";
4+
import {mergeCode0Props} from "../../utils";
55
import {IconX} from "@tabler/icons-react"
66
import "./Dialog.style.scss"
7+
import {Button} from "../button/Button";
8+
import {Flex} from "../flex/Flex";
9+
import {Text} from "../text/Text";
710

811
export type DialogProps = Code0ComponentProps & React.ComponentProps<typeof DialogPrimitive.Root>
912
export type DialogTriggerProps = Code0ComponentProps & React.ComponentProps<typeof DialogPrimitive.Trigger>
@@ -16,6 +19,7 @@ export type DialogHeaderProps = Code0ComponentProps & React.ComponentProps<"div"
1619
export type DialogFooterProps = Code0ComponentProps & React.ComponentProps<"div">
1720
export type DialogContentProps = Code0ComponentProps & React.ComponentProps<typeof DialogPrimitive.Content> & {
1821
showCloseButton?: boolean
22+
title?: string
1923
}
2024

2125
export type DialogStickyContentProps = Code0Component<HTMLDivElement> & {
@@ -48,9 +52,14 @@ export const DialogContent: React.FC<DialogContentProps> = (props) => {
4852
<DialogOverlay/>
4953
<DialogPrimitive.Content {...mergeCode0Props("dialog__content", props) as DialogContentProps}>
5054
{props.showCloseButton && (
51-
<DialogClose>
52-
<IconX size={16}/>
53-
</DialogClose>
55+
<Flex align={"center"} justify={"space-between"}>
56+
<Text hierarchy={"primary"} size={"xl"}>{props.title}</Text>
57+
<DialogClose asChild>
58+
<Button>
59+
<IconX size={16}/>
60+
</Button>
61+
</DialogClose>
62+
</Flex>
5463
)}
5564
{props.children}
5665
</DialogPrimitive.Content>

0 commit comments

Comments
 (0)