fix: bug fixes (#397)

* Add more html page titles

* Make tables responsive

* fix react query keys

* Add tooltip to sidebar toggle

* fix: trim inputs

* fix inputs
This commit is contained in:
Philip Okugbe
2024-10-13 17:09:45 +01:00
committed by GitHub
parent fa3c8a03e1
commit 36e720920b
38 changed files with 681 additions and 612 deletions

View File

@ -8,9 +8,10 @@ import { computeSpaceSlug } from "@/lib";
import { getSpaceUrl } from "@/lib/config.ts";
const formSchema = z.object({
name: z.string().min(2).max(50),
name: z.string().trim().min(2).max(50),
slug: z
.string()
.trim()
.min(2)
.max(50)
.regex(

View File

@ -1,10 +1,10 @@
import { Modal, Tabs, rem, Group, ScrollArea } from "@mantine/core";
import {Modal, Tabs, rem, Group, ScrollArea, Text} from "@mantine/core";
import SpaceMembersList from "@/features/space/components/space-members.tsx";
import AddSpaceMembersModal from "@/features/space/components/add-space-members-modal.tsx";
import React, { useMemo } from "react";
import React, {useMemo} from "react";
import SpaceDetails from "@/features/space/components/space-details.tsx";
import { useSpaceQuery } from "@/features/space/queries/space-query.ts";
import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts";
import {useSpaceQuery} from "@/features/space/queries/space-query.ts";
import {useSpaceAbility} from "@/features/space/permissions/use-space-ability.ts";
import {
SpaceCaslAction,
SpaceCaslSubject,
@ -17,11 +17,11 @@ interface SpaceSettingsModalProps {
}
export default function SpaceSettingsModal({
spaceId,
opened,
onClose,
}: SpaceSettingsModalProps) {
const { data: space, isLoading } = useSpaceQuery(spaceId);
spaceId,
opened,
onClose,
}: SpaceSettingsModalProps) {
const {data: space, isLoading} = useSpaceQuery(spaceId);
const spaceRules = space?.membership?.permissions;
const spaceAbility = useMemo(() => useSpaceAbility(spaceRules), [spaceRules]);
@ -37,14 +37,16 @@ export default function SpaceSettingsModal({
xOffset={0}
mah={400}
>
<Modal.Overlay />
<Modal.Content style={{ overflow: "hidden" }}>
<Modal.Overlay/>
<Modal.Content style={{overflow: "hidden"}}>
<Modal.Header py={0}>
<Modal.Title fw={500}>{space?.name}</Modal.Title>
<Modal.CloseButton />
<Modal.Title>
<Text fw={500} lineClamp={1}>{space?.name}</Text>
</Modal.Title>
<Modal.CloseButton/>
</Modal.Header>
<Modal.Body>
<div style={{ height: rem("600px") }}>
<div style={{height: rem(600)}}>
<Tabs defaultValue="members">
<Tabs.List>
<Tabs.Tab fw={500} value="general">
@ -55,34 +57,32 @@ export default function SpaceSettingsModal({
</Tabs.Tab>
</Tabs.List>
<ScrollArea h="600" w="100%" scrollbarSize={5}>
<Tabs.Panel value="general">
<SpaceDetails
spaceId={space?.id}
readOnly={spaceAbility.cannot(
SpaceCaslAction.Manage,
SpaceCaslSubject.Settings,
)}
/>
</Tabs.Panel>
<Tabs.Panel value="general">
<SpaceDetails
spaceId={space?.id}
readOnly={spaceAbility.cannot(
SpaceCaslAction.Manage,
SpaceCaslSubject.Settings,
)}
/>
</Tabs.Panel>
<Tabs.Panel value="members">
<Group my="md" justify="flex-end">
{spaceAbility.can(
SpaceCaslAction.Manage,
SpaceCaslSubject.Member,
) && <AddSpaceMembersModal spaceId={space?.id} />}
</Group>
<Tabs.Panel value="members">
<Group my="md" justify="flex-end">
{spaceAbility.can(
SpaceCaslAction.Manage,
SpaceCaslSubject.Member,
) && <AddSpaceMembersModal spaceId={space?.id}/>}
</Group>
<SpaceMembersList
spaceId={space?.id}
readOnly={spaceAbility.cannot(
SpaceCaslAction.Manage,
SpaceCaslSubject.Member,
)}
/>
</Tabs.Panel>
</ScrollArea>
<SpaceMembersList
spaceId={space?.id}
readOnly={spaceAbility.cannot(
SpaceCaslAction.Manage,
SpaceCaslSubject.Member,
)}
/>
</Tabs.Panel>
</Tabs>
</div>
</Modal.Body>

View File

@ -1,13 +1,13 @@
import { Table, Group, Text, Avatar } from "@mantine/core";
import React, { useState } from "react";
import { useGetSpacesQuery } from "@/features/space/queries/space-query.ts";
import {Table, Group, Text, Avatar} from "@mantine/core";
import React, {useState} from "react";
import {useGetSpacesQuery} from "@/features/space/queries/space-query.ts";
import SpaceSettingsModal from "@/features/space/components/settings-modal.tsx";
import { useDisclosure } from "@mantine/hooks";
import { formatMemberCount } from "@/lib";
import {useDisclosure} from "@mantine/hooks";
import {formatMemberCount} from "@/lib";
export default function SpaceList() {
const { data, isLoading } = useGetSpacesQuery();
const [opened, { open, close }] = useDisclosure(false);
const {data, isLoading} = useGetSpacesQuery();
const [opened, {open, close}] = useDisclosure(false);
const [selectedSpaceId, setSelectedSpaceId] = useState<string>(null);
const handleClick = (spaceId: string) => {
@ -18,44 +18,48 @@ export default function SpaceList() {
return (
<>
{data && (
<Table highlightOnHover verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Th>Space</Table.Th>
<Table.Th>Members</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data?.items.map((space, index) => (
<Table.Tr
key={index}
style={{ cursor: "pointer" }}
onClick={() => handleClick(space.id)}
>
<Table.Td>
<Group gap="sm">
<Avatar
color="initials"
variant="filled"
name={space.name}
/>
<div>
<Text fz="sm" fw={500}>
{space.name}
</Text>
<Text fz="xs" c="dimmed">
{space.description}
</Text>
</div>
</Group>
</Table.Td>
<Table.Td>{formatMemberCount(space.memberCount)}</Table.Td>
<Table.ScrollContainer minWidth={400}>
<Table highlightOnHover verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Th>Space</Table.Th>
<Table.Th>Members</Table.Th>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.Thead>
<Table.Tbody>
{data?.items.map((space, index) => (
<Table.Tr
key={index}
style={{cursor: "pointer"}}
onClick={() => handleClick(space.id)}
>
<Table.Td>
<Group gap="sm" wrap="nowrap">
<Avatar
color="initials"
variant="filled"
name={space.name}
/>
<div>
<Text fz="sm" fw={500} lineClamp={1}>
{space.name}
</Text>
<Text fz="xs" c="dimmed" lineClamp={2}>
{space.description}
</Text>
</div>
</Group>
</Table.Td>
<Table.Td>
<Text size="sm" style={{whiteSpace: 'nowrap'}}>{formatMemberCount(space.memberCount)}</Text>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
)}
{selectedSpaceId && (

View File

@ -1,32 +1,34 @@
import { Group, Table, Text, Menu, ActionIcon } from "@mantine/core";
import {Group, Table, Text, Menu, ActionIcon} from "@mantine/core";
import React from "react";
import { IconDots } from "@tabler/icons-react";
import { modals } from "@mantine/modals";
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
import {IconDots} from "@tabler/icons-react";
import {modals} from "@mantine/modals";
import {CustomAvatar} from "@/components/ui/custom-avatar.tsx";
import {
useChangeSpaceMemberRoleMutation,
useRemoveSpaceMemberMutation,
useSpaceMembersQuery,
} from "@/features/space/queries/space-query.ts";
import { IconGroupCircle } from "@/components/icons/icon-people-circle.tsx";
import { IRemoveSpaceMember } from "@/features/space/types/space.types.ts";
import {IconGroupCircle} from "@/components/icons/icon-people-circle.tsx";
import {IRemoveSpaceMember} from "@/features/space/types/space.types.ts";
import RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
import {
getSpaceRoleLabel,
spaceRoleData,
} from "@/features/space/types/space-role-data.ts";
import { formatMemberCount } from "@/lib";
import {formatMemberCount} from "@/lib";
type MemberType = "user" | "group";
interface SpaceMembersProps {
spaceId: string;
readOnly?: boolean;
}
export default function SpaceMembersList({
spaceId,
readOnly,
}: SpaceMembersProps) {
const { data, isLoading } = useSpaceMembersQuery(spaceId);
spaceId,
readOnly,
}: SpaceMembersProps) {
const {data, isLoading} = useSpaceMembersQuery(spaceId);
const removeSpaceMember = useRemoveSpaceMemberMutation();
const changeSpaceMemberRoleMutation = useChangeSpaceMemberRoleMutation();
@ -85,99 +87,101 @@ export default function SpaceMembersList({
</Text>
),
centered: true,
labels: { confirm: "Remove", cancel: "Cancel" },
confirmProps: { color: "red" },
labels: {confirm: "Remove", cancel: "Cancel"},
confirmProps: {color: "red"},
onConfirm: () => onRemove(memberId, type),
});
return (
<>
{data && (
<Table verticalSpacing={8}>
<Table.Thead>
<Table.Tr>
<Table.Th>Member</Table.Th>
<Table.Th>Role</Table.Th>
<Table.Th></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data?.items.map((member, index) => (
<Table.Tr key={index}>
<Table.Td>
<Group gap="sm">
{member.type === "user" && (
<CustomAvatar
avatarUrl={member?.avatarUrl}
name={member.name}
/>
)}
{member.type === "group" && <IconGroupCircle />}
<div>
<Text fz="sm" fw={500}>
{member?.name}
</Text>
<Text fz="xs" c="dimmed">
{member.type == "user" && member?.email}
{member.type == "group" &&
`Group - ${formatMemberCount(member?.memberCount)}`}
</Text>
</div>
</Group>
</Table.Td>
<Table.Td>
<RoleSelectMenu
roles={spaceRoleData}
roleName={getSpaceRoleLabel(member.role)}
onChange={(newRole) =>
handleRoleChange(
member.id,
member.type,
newRole,
member.role,
)
}
disabled={readOnly}
/>
</Table.Td>
<Table.Td>
{!readOnly && (
<Menu
shadow="xl"
position="bottom-end"
offset={20}
width={200}
withArrow
arrowPosition="center"
>
<Menu.Target>
<ActionIcon variant="subtle" c="gray">
<IconDots size={20} stroke={2} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
onClick={() =>
openRemoveModal(member.id, member.type)
}
>
Remove space member
</Menu.Item>
</Menu.Dropdown>
</Menu>
)}
</Table.Td>
<Table.ScrollContainer minWidth={500}>
<Table verticalSpacing={8}>
<Table.Thead>
<Table.Tr>
<Table.Th>Member</Table.Th>
<Table.Th>Role</Table.Th>
<Table.Th></Table.Th>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.Thead>
<Table.Tbody>
{data?.items.map((member, index) => (
<Table.Tr key={index}>
<Table.Td>
<Group gap="sm">
{member.type === "user" && (
<CustomAvatar
avatarUrl={member?.avatarUrl}
name={member.name}
/>
)}
{member.type === "group" && <IconGroupCircle/>}
<div>
<Text fz="sm" fw={500}>
{member?.name}
</Text>
<Text fz="xs" c="dimmed">
{member.type == "user" && member?.email}
{member.type == "group" &&
`Group - ${formatMemberCount(member?.memberCount)}`}
</Text>
</div>
</Group>
</Table.Td>
<Table.Td>
<RoleSelectMenu
roles={spaceRoleData}
roleName={getSpaceRoleLabel(member.role)}
onChange={(newRole) =>
handleRoleChange(
member.id,
member.type,
newRole,
member.role,
)
}
disabled={readOnly}
/>
</Table.Td>
<Table.Td>
{!readOnly && (
<Menu
shadow="xl"
position="bottom-end"
offset={20}
width={200}
withArrow
arrowPosition="center"
>
<Menu.Target>
<ActionIcon variant="subtle" c="gray">
<IconDots size={20} stroke={2}/>
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
onClick={() =>
openRemoveModal(member.id, member.type)
}
>
Remove space member
</Menu.Item>
</Menu.Dropdown>
</Menu>
)}
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
)}
</>
);