Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/d-member/DNamespaceMember.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export const MemberListExample = () => {
}]
},
userAbilities: {
deleteMember: true
deleteMember: true,
assignMemberRoles: true
}
})
])
Expand Down
3 changes: 2 additions & 1 deletion src/components/d-member/DNamespaceMemberCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {Card} from "../card/Card";
import React from "react";
import {DNamespaceMemberContent} from "./DNamespaceMemberContent";
import {DNamespaceMemberView} from "./DNamespaceMember.view";
import {DNamespaceRoleView} from "../d-role";

export interface DNamespaceMemberCardProps {
memberId: NamespaceMember["id"]
onRemove?: (member: DNamespaceMemberView) => void
onAssignRole?: (member: DNamespaceMemberView) => void
onAssignRole?: (member: DNamespaceMemberView, roles: DNamespaceRoleView[]) => void
}

export const DNamespaceMemberCard: React.FC<DNamespaceMemberCardProps> = (props) => {
Expand Down
125 changes: 106 additions & 19 deletions src/components/d-member/DNamespaceMemberContent.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import React from "react";
import {Flex} from "../flex/Flex";
import {NamespaceMember} from "@code0-tech/sagittarius-graphql-types";
import {useService, useStore} from "../../utils";
import {DNamespaceMemberReactiveService} from "./DNamespaceMember.service";
import {DUserReactiveService} from "../d-user";
import {DNamespaceRoleReactiveService} from "../d-role";
import {Avatar} from "../avatar/Avatar";
import {Text} from "../text/Text";
import {Badge} from "../badge/Badge";
import {IconDots, IconMailCheck, IconUserCog, IconUserOff} from "@tabler/icons-react";
import {Button} from "../button/Button";
import {Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger} from "../tooltip/Tooltip";
import {DNamespaceRolePermissions} from "../d-role/DNamespaceRolePermissions";
import {Menu, MenuContent, MenuItem, MenuLabel, MenuPortal, MenuTrigger} from "../menu/Menu";
import {DNamespaceMemberView} from "./DNamespaceMember.view";
import React from "react"
import {Flex} from "../flex/Flex"
import {NamespaceMember} from "@code0-tech/sagittarius-graphql-types"
import {useService, useStore} from "../../utils"
import {DNamespaceMemberReactiveService} from "./DNamespaceMember.service"
import {DUserReactiveService} from "../d-user"
import {DNamespaceRoleReactiveService, DNamespaceRoleView} from "../d-role"
import {Avatar} from "../avatar/Avatar"
import {Text} from "../text/Text"
import {Badge} from "../badge/Badge"
import {IconDots, IconMailCheck, IconTrash, IconUserCog, IconUserOff} from "@tabler/icons-react"
import {Button} from "../button/Button"
import {Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipTrigger} from "../tooltip/Tooltip"
import {DNamespaceRolePermissions} from "../d-role/DNamespaceRolePermissions"
import {Menu, MenuContent, MenuItem, MenuLabel, MenuPortal, MenuSeparator, MenuTrigger} from "../menu/Menu"
import {DNamespaceMemberView} from "./DNamespaceMember.view"
import {Dialog, DialogClose, DialogContent, DialogPortal} from "../dialog/Dialog"
import {Card} from "../card/Card"
import CardSection from "../card/CardSection"

export interface DNamespaceMemberContentProps {
memberId: NamespaceMember['id']
onRemove?: (member: DNamespaceMemberView) => void
onAssignRole?: (member: DNamespaceMemberView) => void
onAssignRole?: (member: DNamespaceMemberView, roles: DNamespaceRoleView[]) => void
}

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

return <Flex align={"center"} style={{gap: "1.3rem"}} justify={"space-between"}>
<Dialog open={openRemovedMemberDialog} onOpenChange={open => setOpenRemovedMemberDialog(open)}>
<DialogPortal>
<DialogContent showCloseButton title={"Remove member"}>
<Text size={"md"} hierarchy={"secondary"}>
Are you sure you want to remove {" "}
<Badge color={"info"}>
<Text size={"md"} style={{color: "inherit"}}>@{user?.username}</Text>
</Badge> {" "}
from the namespace members?
</Text>
<Flex justify={"space-between"} align={"center"}>
<DialogClose asChild>
<Button color={"secondary"}>No, go back!</Button>
</DialogClose>
<Button color={"error"} onClick={() => onRemove(member!!)}>Yes, remove!</Button>
</Flex>
</DialogContent>
</DialogPortal>
</Dialog>
<Dialog open={openAssignRolesDialog} onOpenChange={open => setOpenAssignRolesDialog(open)}>
<DialogPortal>
<DialogContent autoFocus showCloseButton title={"Assign roles"}>
<Text size={"md"} hierarchy={"tertiary"}>Assign, remove and manage the roles of a active
member</Text>
<Card paddingSize={"xs"} color={"secondary"}>
{localAssignedRoles.map(role => {
return <CardSection border key={role?.id}>
<Flex style={{gap: "0.7rem"}} align={"center"} justify={"space-between"}>
<Flex align={"center"} style={{gap: "1.3rem"}}>
<Text hierarchy={"primary"}>{role?.name}</Text>
<DNamespaceRolePermissions abilities={role?.abilities!!}/>
</Flex>
<Button color={"error"} paddingSize={"xxs"} onClick={() => {
setLocalAssignedRoles(prevState => prevState.filter(aRole => aRole?.id != role?.id))
}}>
<IconTrash size={16}/>
</Button>
</Flex>

</CardSection>
})}
<Menu>
<MenuTrigger asChild>
<CardSection hover p={0.35} border display={"flex"} justify={"center"}>
<Button paddingSize={"xxs"} variant={"none"}>Assign roles</Button>
</CardSection>
</MenuTrigger>
<MenuPortal>
<MenuContent side={"bottom"} sideOffset={8} align={"center"}>
<MenuLabel>Roles to add</MenuLabel>
{rolesToAssign.map((role, index) => {
return <>
<MenuItem onSelect={() => {
setLocalAssignedRoles(prevState => [...prevState, role])
}}>
<Flex align={"center"} style={{gap: "1.3rem"}}>
<Text hierarchy={"primary"}>{role?.name}</Text>
<DNamespaceRolePermissions abilities={role?.abilities!!}/>
</Flex>
</MenuItem>
{index < rolesToAssign.length - 1 && <MenuSeparator/>}
</>
})}
</MenuContent>
</MenuPortal>
</Menu>
</Card>
<Flex justify={"space-between"} align={"center"}>
<DialogClose asChild>
<Button color={"secondary"}>No, go back!</Button>
</DialogClose>

<Button onClick={() => onAssignRole(member!!, localAssignedRoles as DNamespaceRoleView[])}
color={"success"}>Yes, save!</Button>
</Flex>
</DialogContent>
</DialogPortal>
</Dialog>
<Flex style={{gap: "1.3rem"}} align={"center"}>
<Flex align={"center"} style={{gap: ".7rem"}}>
<Avatar identifier={user?.username!!} bg={"transparent"}/>
Expand Down Expand Up @@ -96,13 +183,13 @@ export const DNamespaceMemberContent: React.FC<DNamespaceMemberContentProps> = (
<MenuContent align={"end"} side={"bottom"} sideOffset={8}>
<MenuLabel>Actions</MenuLabel>
{member?.userAbilities?.deleteMember && (
<MenuItem onSelect={() => onRemove(member)}>
<MenuItem onSelect={() => setOpenRemovedMemberDialog(true)}>
<IconUserOff size={16}/>
<Text>Remove member</Text>
</MenuItem>
)}
{member?.userAbilities?.assignMemberRoles && (
<MenuItem onSelect={() => onAssignRole(member)}>
<MenuItem onSelect={() => setOpenAssignRolesDialog(true)}>
<IconUserCog size={16}/>
<Text>Assign role</Text>
</MenuItem>
Expand Down
3 changes: 2 additions & 1 deletion src/components/d-member/DNamespaceMemberList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {Card} from "../card/Card";
import CardSection from "../card/CardSection";
import {DNamespaceMemberView} from "./DNamespaceMember.view";
import {DNamespaceMemberContent} from "./DNamespaceMemberContent";
import {DNamespaceRoleView} from "../d-role";

export interface DNamespaceMemberListProps extends Omit<Card, "children" | "onSelect"> {
namespaceId: Namespace["id"]
filter?: (runtime: DNamespaceMemberView, index: number) => boolean
onRemove?: (member: DNamespaceMemberView) => void
onAssignRole?: (member: DNamespaceMemberView) => void
onAssignRole?: (member: DNamespaceMemberView, roles: DNamespaceRoleView[]) => void
}

export const DNamespaceMemberList: React.FC<DNamespaceMemberListProps> = (props) => {
Expand Down
49 changes: 5 additions & 44 deletions src/components/dialog/Dialog.style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
@use "sass:color";

.dialog {
@include box.box(variables.$primary, variables.$primary, variables.$secondary);
@include helpers.borderRadius();

backdrop-filter: none;
-webkit-backdrop-filter: none;

Expand All @@ -26,10 +23,13 @@
bottom: 0;
left: 0;
z-index: 50;
background: rgba(0, 0, 0, 0.5);
background: rgba(variables.$primary, 0.75);
}

&__content {
@include box.box(variables.$secondary);
@include helpers.borderRadius();

position: fixed;
top: 50%;
left: 50%;
Expand All @@ -39,15 +39,10 @@
max-width: calc(100% - 2rem);
gap: 1rem;
transform: translate(-50%, -50%);
border: 1px solid helpers.borderColor();
padding: 1rem;
background: variables.$primary;
color: variables.$secondary;

@include helpers.borderRadius();

@include media.respond-above(md) {
width: 420px;
width: 490px;
}
}

Expand Down Expand Up @@ -87,38 +82,4 @@
justify-content: flex-end;
}
}

&__close {
position: absolute;
top: 1rem;
right: 1rem;
border-radius: 0.125rem;
border: none;
opacity: 0.7;
transition: opacity 0.2s ease;
background: none;
color: variables.$secondary;

&:hover {
opacity: 1;
}

&:focus {
outline: none;
}

&:disabled {
pointer-events: none;
}

svg {
pointer-events: none;
flex-shrink: 0;
}

svg:not([class*="size-"]) {
width: 1rem;
height: 1rem;
}
}
}
23 changes: 16 additions & 7 deletions src/components/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import * as DialogPrimitive from "@radix-ui/react-dialog"
import {Code0Component, Code0ComponentProps} from "../../utils/types";
import React, {useEffect} from "react";
import {mergeCode0Props} from "../../utils/utils";
import * as DialogPrimitive from "@radix-ui/react-dialog"
import {Code0Component, Code0ComponentProps} from "../../utils";
import React from "react";
import {mergeCode0Props} from "../../utils";
import {IconX} from "@tabler/icons-react"
import "./Dialog.style.scss"
import {Button} from "../button/Button";
import {Flex} from "../flex/Flex";
import {Text} from "../text/Text";

export type DialogProps = Code0ComponentProps & React.ComponentProps<typeof DialogPrimitive.Root>
export type DialogTriggerProps = Code0ComponentProps & React.ComponentProps<typeof DialogPrimitive.Trigger>
Expand All @@ -16,6 +19,7 @@ export type DialogHeaderProps = Code0ComponentProps & React.ComponentProps<"div"
export type DialogFooterProps = Code0ComponentProps & React.ComponentProps<"div">
export type DialogContentProps = Code0ComponentProps & React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
title?: string
}

export type DialogStickyContentProps = Code0Component<HTMLDivElement> & {
Expand Down Expand Up @@ -48,9 +52,14 @@ export const DialogContent: React.FC<DialogContentProps> = (props) => {
<DialogOverlay/>
<DialogPrimitive.Content {...mergeCode0Props("dialog__content", props) as DialogContentProps}>
{props.showCloseButton && (
<DialogClose>
<IconX size={16}/>
</DialogClose>
<Flex align={"center"} justify={"space-between"}>
<Text hierarchy={"primary"} size={"xl"}>{props.title}</Text>
<DialogClose asChild>
<Button>
<IconX size={16}/>
</Button>
</DialogClose>
</Flex>
)}
{props.children}
</DialogPrimitive.Content>
Expand Down