-
{option.label}
+
{option.label}
{option?.["email"]}
diff --git a/apps/client/src/features/group/queries/group-query.ts b/apps/client/src/features/group/queries/group-query.ts
index ac113f87..6fa8f6d5 100644
--- a/apps/client/src/features/group/queries/group-query.ts
+++ b/apps/client/src/features/group/queries/group-query.ts
@@ -3,8 +3,9 @@ import {
useQuery,
useQueryClient,
UseQueryResult,
-} from '@tanstack/react-query';
-import { IGroup } from '@/features/group/types/group.types';
+ keepPreviousData,
+} from "@tanstack/react-query";
+import { IGroup } from "@/features/group/types/group.types";
import {
addGroupMember,
createGroup,
@@ -14,22 +15,24 @@ import {
getGroups,
removeGroupMember,
updateGroup,
-} from '@/features/group/services/group-service';
-import { notifications } from '@mantine/notifications';
-import { QueryParams } from '@/lib/types.ts';
+} from "@/features/group/services/group-service";
+import { notifications } from "@mantine/notifications";
+import { IPagination, QueryParams } from "@/lib/types.ts";
+import { IUser } from "@/features/user/types/user.types.ts";
export function useGetGroupsQuery(
- params?: QueryParams
-): UseQueryResult
{
+ params?: QueryParams,
+): UseQueryResult, Error> {
return useQuery({
- queryKey: ['groups', params],
+ queryKey: ["groups", params],
queryFn: () => getGroups(params),
+ placeholderData: keepPreviousData,
});
}
export function useGroupQuery(groupId: string): UseQueryResult {
return useQuery({
- queryKey: ['group', groupId],
+ queryKey: ["group", groupId],
queryFn: () => getGroupById(groupId),
enabled: !!groupId,
});
@@ -42,13 +45,13 @@ export function useCreateGroupMutation() {
mutationFn: (data) => createGroup(data),
onSuccess: () => {
queryClient.invalidateQueries({
- queryKey: ['groups'],
+ queryKey: ["groups"],
});
- notifications.show({ message: 'Group created successfully' });
+ notifications.show({ message: "Group created successfully" });
},
onError: () => {
- notifications.show({ message: 'Failed to create group', color: 'red' });
+ notifications.show({ message: "Failed to create group", color: "red" });
},
});
}
@@ -59,14 +62,14 @@ export function useUpdateGroupMutation() {
return useMutation>({
mutationFn: (data) => updateGroup(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Group updated successfully' });
+ notifications.show({ message: "Group updated successfully" });
queryClient.invalidateQueries({
- queryKey: ['group', variables.groupId],
+ queryKey: ["group", variables.groupId],
});
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
@@ -77,28 +80,25 @@ export function useDeleteGroupMutation() {
return useMutation({
mutationFn: (groupId: string) => deleteGroup({ groupId }),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Group deleted successfully' });
-
- const groups = queryClient.getQueryData(['groups']) as any;
- if (groups) {
- groups.items = groups.items?.filter(
- (group: IGroup) => group.id !== variables
- );
- queryClient.setQueryData(['groups'], groups);
- }
+ notifications.show({ message: "Group deleted successfully" });
+ queryClient.refetchQueries({ queryKey: ["groups"] });
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
-export function useGroupMembersQuery(groupId: string) {
+export function useGroupMembersQuery(
+ groupId: string,
+ params?: QueryParams,
+): UseQueryResult, Error> {
return useQuery({
- queryKey: ['groupMembers', groupId],
- queryFn: () => getGroupMembers(groupId),
+ queryKey: ["groupMembers", groupId, params],
+ queryFn: () => getGroupMembers(groupId, params),
enabled: !!groupId,
+ placeholderData: keepPreviousData,
});
}
@@ -108,15 +108,15 @@ export function useAddGroupMemberMutation() {
return useMutation({
mutationFn: (data) => addGroupMember(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Added successfully' });
+ notifications.show({ message: "Added successfully" });
queryClient.invalidateQueries({
- queryKey: ['groupMembers', variables.groupId],
+ queryKey: ["groupMembers", variables.groupId],
});
},
onError: () => {
notifications.show({
- message: 'Failed to add group members',
- color: 'red',
+ message: "Failed to add group members",
+ color: "red",
});
},
});
@@ -135,14 +135,14 @@ export function useRemoveGroupMemberMutation() {
>({
mutationFn: (data) => removeGroupMember(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Removed successfully' });
+ notifications.show({ message: "Removed successfully" });
queryClient.invalidateQueries({
- queryKey: ['groupMembers', variables.groupId],
+ queryKey: ["groupMembers", variables.groupId],
});
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
diff --git a/apps/client/src/features/group/services/group-service.ts b/apps/client/src/features/group/services/group-service.ts
index a5e97605..dfd9e4f4 100644
--- a/apps/client/src/features/group/services/group-service.ts
+++ b/apps/client/src/features/group/services/group-service.ts
@@ -1,10 +1,12 @@
import api from "@/lib/api-client";
import { IGroup } from "@/features/group/types/group.types";
-import { QueryParams } from "@/lib/types.ts";
+import { IPagination, QueryParams } from "@/lib/types.ts";
+import { IUser } from "@/features/user/types/user.types.ts";
-export async function getGroups(params?: QueryParams): Promise {
- // TODO: returns paginated. Fix type
- const req = await api.post("/groups", params);
+export async function getGroups(
+ params?: QueryParams,
+): Promise> {
+ const req = await api.post("/groups", params);
return req.data;
}
@@ -27,8 +29,11 @@ export async function deleteGroup(data: { groupId: string }): Promise {
await api.post("/groups/delete", data);
}
-export async function getGroupMembers(groupId: string) {
- const req = await api.post("/groups/members", { groupId });
+export async function getGroupMembers(
+ groupId: string,
+ params?: QueryParams,
+): Promise> {
+ const req = await api.post("/groups/members", { groupId, params });
return req.data;
}
diff --git a/apps/client/src/features/space/components/multi-member-select.tsx b/apps/client/src/features/space/components/multi-member-select.tsx
index 86dd2f2a..efa2142f 100644
--- a/apps/client/src/features/space/components/multi-member-select.tsx
+++ b/apps/client/src/features/space/components/multi-member-select.tsx
@@ -15,7 +15,7 @@ interface MultiMemberSelectProps {
const renderMultiSelectOption: MultiSelectProps["renderOption"] = ({
option,
}) => (
-
+
{option["type"] === "user" && (
}
- {option.label}
+ {option.label}
);
diff --git a/apps/client/src/features/space/components/sidebar/space-select.tsx b/apps/client/src/features/space/components/sidebar/space-select.tsx
index 20c66013..961b0cf0 100644
--- a/apps/client/src/features/space/components/sidebar/space-select.tsx
+++ b/apps/client/src/features/space/components/sidebar/space-select.tsx
@@ -12,10 +12,10 @@ interface SpaceSelectProps {
}
const renderSelectOption: SelectProps["renderOption"] = ({ option }) => (
-
+
- {option.label}
+ {option.label}
);
diff --git a/apps/client/src/features/space/components/space-list.tsx b/apps/client/src/features/space/components/space-list.tsx
index 1528869d..6bb29128 100644
--- a/apps/client/src/features/space/components/space-list.tsx
+++ b/apps/client/src/features/space/components/space-list.tsx
@@ -5,10 +5,12 @@ import SpaceSettingsModal from "@/features/space/components/settings-modal.tsx";
import { useDisclosure } from "@mantine/hooks";
import { formatMemberCount } from "@/lib";
import { useTranslation } from "react-i18next";
+import Paginate from "@/components/common/paginate.tsx";
export default function SpaceList() {
const { t } = useTranslation();
- const { data, isLoading } = useGetSpacesQuery();
+ const [page, setPage] = useState(1);
+ const { data, isLoading } = useGetSpacesQuery({ page });
const [opened, { open, close }] = useDisclosure(false);
const [selectedSpaceId, setSelectedSpaceId] = useState(null);
@@ -19,50 +21,57 @@ export default function SpaceList() {
return (
<>
- {data && (
-
-
-
-
- {t("Space")}
- {t("Members")}
-
-
+
+
+
+
+ {t("Space")}
+ {t("Members")}
+
+
-
- {data?.items.map((space, index) => (
- handleClick(space.id)}
- >
-
-
-
-
-
- {space.name}
-
-
- {space.description}
-
-
-
-
-
-
- {formatMemberCount(space.memberCount, t)}
-
-
-
- ))}
-
-
-
+
+ {data?.items.map((space, index) => (
+ handleClick(space.id)}
+ >
+
+
+
+
+
+ {space.name}
+
+
+ {space.description}
+
+
+
+
+
+
+ {formatMemberCount(space.memberCount, t)}
+
+
+
+ ))}
+
+
+
+
+ {data?.items.length > 0 && (
+
)}
{selectedSpaceId && (
diff --git a/apps/client/src/features/space/components/space-members.tsx b/apps/client/src/features/space/components/space-members.tsx
index dc0bc1f7..b1c0e026 100644
--- a/apps/client/src/features/space/components/space-members.tsx
+++ b/apps/client/src/features/space/components/space-members.tsx
@@ -1,5 +1,5 @@
import { Group, Table, Text, Menu, ActionIcon } from "@mantine/core";
-import React from "react";
+import React, { useState } from "react";
import { IconDots } from "@tabler/icons-react";
import { modals } from "@mantine/modals";
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
@@ -17,6 +17,7 @@ import {
} from "@/features/space/types/space-role-data.ts";
import { formatMemberCount } from "@/lib";
import { useTranslation } from "react-i18next";
+import Paginate from "@/components/common/paginate.tsx";
type MemberType = "user" | "group";
@@ -30,8 +31,11 @@ export default function SpaceMembersList({
readOnly,
}: SpaceMembersProps) {
const { t } = useTranslation();
- const { data, isLoading } = useSpaceMembersQuery(spaceId);
-
+ const [page, setPage] = useState(1);
+ const { data, isLoading } = useSpaceMembersQuery(spaceId, {
+ page,
+ limit: 100,
+ });
const removeSpaceMember = useRemoveSpaceMemberMutation();
const changeSpaceMemberRoleMutation = useChangeSpaceMemberRoleMutation();
@@ -98,94 +102,101 @@ export default function SpaceMembersList({
return (
<>
- {data && (
-
-
-
-
- {t("Member")}
- {t("Role")}
-
-
-
+
+
+
+
+ {t("Member")}
+ {t("Role")}
+
+
+
-
- {data?.items.map((member, index) => (
-
-
-
- {member.type === "user" && (
-
- )}
-
- {member.type === "group" && }
-
-
-
- {member?.name}
-
-
- {member.type == "user" && member?.email}
-
- {member.type == "group" &&
- `${t("Group")} - ${formatMemberCount(member?.memberCount, t)}`}
-
-
-
-
-
-
-
- handleRoleChange(
- member.id,
- member.type,
- newRole,
- member.role,
- )
- }
- disabled={readOnly}
- />
-
-
-
- {!readOnly && (
-
+
+ {data?.items.map((member, index) => (
+
+
+
+ {member.type === "user" && (
+
)}
-
-
- ))}
-
-
-
+
+ {member.type === "group" && }
+
+
+
+ {member?.name}
+
+
+ {member.type == "user" && member?.email}
+
+ {member.type == "group" &&
+ `${t("Group")} - ${formatMemberCount(member?.memberCount, t)}`}
+
+
+
+
+
+
+
+ handleRoleChange(
+ member.id,
+ member.type,
+ newRole,
+ member.role,
+ )
+ }
+ disabled={readOnly}
+ />
+
+
+
+ {!readOnly && (
+
+ )}
+
+
+ ))}
+
+
+
+
+ {data?.items.length > 0 && (
+
)}
>
);
diff --git a/apps/client/src/features/space/queries/space-query.ts b/apps/client/src/features/space/queries/space-query.ts
index e1fa7a12..f0e5f5b5 100644
--- a/apps/client/src/features/space/queries/space-query.ts
+++ b/apps/client/src/features/space/queries/space-query.ts
@@ -1,16 +1,17 @@
import {
+ keepPreviousData,
useMutation,
useQuery,
useQueryClient,
UseQueryResult,
-} from '@tanstack/react-query';
+} from "@tanstack/react-query";
import {
IAddSpaceMember,
IChangeSpaceMemberRole,
IRemoveSpaceMember,
ISpace,
ISpaceMember,
-} from '@/features/space/types/space.types';
+} from "@/features/space/types/space.types";
import {
addSpaceMember,
changeMemberRole,
@@ -21,22 +22,23 @@ import {
createSpace,
updateSpace,
deleteSpace,
-} from '@/features/space/services/space-service.ts';
-import { notifications } from '@mantine/notifications';
-import { IPagination, QueryParams } from '@/lib/types.ts';
+} from "@/features/space/services/space-service.ts";
+import { notifications } from "@mantine/notifications";
+import { IPagination, QueryParams } from "@/lib/types.ts";
export function useGetSpacesQuery(
- params?: QueryParams
+ params?: QueryParams,
): UseQueryResult, Error> {
return useQuery({
- queryKey: ['spaces', params],
+ queryKey: ["spaces", params],
queryFn: () => getSpaces(params),
+ placeholderData: keepPreviousData,
});
}
export function useSpaceQuery(spaceId: string): UseQueryResult {
return useQuery({
- queryKey: ['space', spaceId],
+ queryKey: ["space", spaceId],
queryFn: () => getSpaceById(spaceId),
enabled: !!spaceId,
staleTime: 5 * 60 * 1000,
@@ -50,22 +52,22 @@ export function useCreateSpaceMutation() {
mutationFn: (data) => createSpace(data),
onSuccess: () => {
queryClient.invalidateQueries({
- queryKey: ['spaces'],
+ queryKey: ["spaces"],
});
- notifications.show({ message: 'Space created successfully' });
+ notifications.show({ message: "Space created successfully" });
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
export function useGetSpaceBySlugQuery(
- spaceId: string
+ spaceId: string,
): UseQueryResult {
return useQuery({
- queryKey: ['space', spaceId],
+ queryKey: ["space", spaceId],
queryFn: () => getSpaceById(spaceId),
enabled: !!spaceId,
staleTime: 5 * 60 * 1000,
@@ -78,25 +80,25 @@ export function useUpdateSpaceMutation() {
return useMutation>({
mutationFn: (data) => updateSpace(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Space updated successfully' });
+ notifications.show({ message: "Space updated successfully" });
const space = queryClient.getQueryData([
- 'space',
+ "space",
variables.spaceId,
]) as ISpace;
if (space) {
const updatedSpace = { ...space, ...data };
- queryClient.setQueryData(['space', variables.spaceId], updatedSpace);
- queryClient.setQueryData(['space', data.slug], updatedSpace);
+ queryClient.setQueryData(["space", variables.spaceId], updatedSpace);
+ queryClient.setQueryData(["space", data.slug], updatedSpace);
}
queryClient.invalidateQueries({
- queryKey: ['spaces'],
+ queryKey: ["spaces"],
});
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
@@ -107,37 +109,39 @@ export function useDeleteSpaceMutation() {
return useMutation({
mutationFn: (data: Partial) => deleteSpace(data.id),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Space deleted successfully' });
+ notifications.show({ message: "Space deleted successfully" });
if (variables.slug) {
queryClient.removeQueries({
- queryKey: ['space', variables.slug],
+ queryKey: ["space", variables.slug],
exact: true,
});
}
- const spaces = queryClient.getQueryData(['spaces']) as any;
+ const spaces = queryClient.getQueryData(["spaces"]) as any;
if (spaces) {
spaces.items = spaces.items?.filter(
- (space: ISpace) => space.id !== variables.id
+ (space: ISpace) => space.id !== variables.id,
);
- queryClient.setQueryData(['spaces'], spaces);
+ queryClient.setQueryData(["spaces"], spaces);
}
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
export function useSpaceMembersQuery(
- spaceId: string
+ spaceId: string,
+ params?: QueryParams,
): UseQueryResult, Error> {
return useQuery({
- queryKey: ['spaceMembers', spaceId],
- queryFn: () => getSpaceMembers(spaceId),
+ queryKey: ["spaceMembers", spaceId, params],
+ queryFn: () => getSpaceMembers(spaceId, params),
enabled: !!spaceId,
+ placeholderData: keepPreviousData,
});
}
@@ -147,14 +151,14 @@ export function useAddSpaceMemberMutation() {
return useMutation({
mutationFn: (data) => addSpaceMember(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Members added successfully' });
+ notifications.show({ message: "Members added successfully" });
queryClient.invalidateQueries({
- queryKey: ['spaceMembers', variables.spaceId],
+ queryKey: ["spaceMembers", variables.spaceId],
});
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
@@ -165,14 +169,14 @@ export function useRemoveSpaceMemberMutation() {
return useMutation({
mutationFn: (data) => removeSpaceMember(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Removed successfully' });
- queryClient.refetchQueries({
- queryKey: ['spaceMembers', variables.spaceId],
+ notifications.show({ message: "Removed successfully" });
+ queryClient.invalidateQueries({
+ queryKey: ["spaceMembers", variables.spaceId],
});
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
@@ -183,15 +187,15 @@ export function useChangeSpaceMemberRoleMutation() {
return useMutation({
mutationFn: (data) => changeMemberRole(data),
onSuccess: (data, variables) => {
- notifications.show({ message: 'Member role updated successfully' });
+ notifications.show({ message: "Member role updated successfully" });
// due to pagination levels, change in cache instead
queryClient.refetchQueries({
- queryKey: ['spaceMembers', variables.spaceId],
+ queryKey: ["spaceMembers", variables.spaceId],
});
},
onError: (error) => {
- const errorMessage = error['response']?.data?.message;
- notifications.show({ message: errorMessage, color: 'red' });
+ const errorMessage = error["response"]?.data?.message;
+ notifications.show({ message: errorMessage, color: "red" });
},
});
}
diff --git a/apps/client/src/features/space/services/space-service.ts b/apps/client/src/features/space/services/space-service.ts
index cd0c247e..74dc57f9 100644
--- a/apps/client/src/features/space/services/space-service.ts
+++ b/apps/client/src/features/space/services/space-service.ts
@@ -5,13 +5,14 @@ import {
IExportSpaceParams,
IRemoveSpaceMember,
ISpace,
+ ISpaceMember,
} from "@/features/space/types/space.types";
import { IPagination, QueryParams } from "@/lib/types.ts";
import { IUser } from "@/features/user/types/user.types.ts";
import { saveAs } from "file-saver";
export async function getSpaces(
- params?: QueryParams
+ params?: QueryParams,
): Promise> {
const req = await api.post("/spaces", params);
return req.data;
@@ -37,9 +38,10 @@ export async function deleteSpace(spaceId: string): Promise {
}
export async function getSpaceMembers(
- spaceId: string
-): Promise> {
- const req = await api.post("/spaces/members", { spaceId });
+ spaceId: string,
+ params?: QueryParams,
+): Promise> {
+ const req = await api.post("/spaces/members", { spaceId, params });
return req.data;
}
@@ -48,13 +50,13 @@ export async function addSpaceMember(data: IAddSpaceMember): Promise {
}
export async function removeSpaceMember(
- data: IRemoveSpaceMember
+ data: IRemoveSpaceMember,
): Promise {
await api.post("/spaces/members/remove", data);
}
export async function changeMemberRole(
- data: IChangeSpaceMemberRole
+ data: IChangeSpaceMemberRole,
): Promise {
await api.post("/spaces/members/change-role", data);
}
diff --git a/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx b/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx
index 7b3464bf..bc7b9db5 100644
--- a/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx
+++ b/apps/client/src/features/workspace/components/members/components/workspace-invites-table.tsx
@@ -1,19 +1,22 @@
-import {Group, Table, Avatar, Text, Alert} from "@mantine/core";
-import {useWorkspaceInvitationsQuery} from "@/features/workspace/queries/workspace-query.ts";
-import React from "react";
-import {getUserRoleLabel} from "@/features/workspace/types/user-role-data.ts";
+import { Group, Table, Avatar, Text, Alert } from "@mantine/core";
+import { useWorkspaceInvitationsQuery } from "@/features/workspace/queries/workspace-query.ts";
+import React, { useState } from "react";
+import { getUserRoleLabel } from "@/features/workspace/types/user-role-data.ts";
import InviteActionMenu from "@/features/workspace/components/members/components/invite-action-menu.tsx";
-import {IconInfoCircle} from "@tabler/icons-react";
-import {formattedDate, timeAgo} from "@/lib/time.ts";
+import { IconInfoCircle } from "@tabler/icons-react";
+import { timeAgo } from "@/lib/time.ts";
import useUserRole from "@/hooks/use-user-role.tsx";
import { useTranslation } from "react-i18next";
+import Paginate from "@/components/common/paginate.tsx";
export default function WorkspaceInvitesTable() {
const { t } = useTranslation();
+ const [page, setPage] = useState(1);
const { data, isLoading } = useWorkspaceInvitationsQuery({
+ page,
limit: 100,
});
- const {isAdmin} = useUserRole();
+ const { isAdmin } = useUserRole();
return (
<>
@@ -23,47 +26,50 @@ export default function WorkspaceInvitesTable() {
)}
- {data && (
- <>
-
-
-
-
- {t("Email")}
- {t("Role")}
- {t("Date")}
-
-
+
+
+
+
+ {t("Email")}
+ {t("Role")}
+ {t("Date")}
+
+
-
- {data?.items.map((invitation, index) => (
-
-
-
-
-
-
- {invitation.email}
-
-
-
-
+
+ {data?.items.map((invitation, index) => (
+
+
+
+
+
+
+ {invitation.email}
+
+
+
+
- {t(getUserRoleLabel(invitation.role))}
+ {t(getUserRoleLabel(invitation.role))}
- {timeAgo(invitation.createdAt)}
+ {timeAgo(invitation.createdAt)}
-
- {isAdmin && (
-
- )}
-
-
- ))}
-
-
-
- >
+
+ {isAdmin && }
+
+
+ ))}
+
+
+
+
+ {data?.items.length > 0 && (
+
)}
>
);
diff --git a/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx b/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx
index 0c4bd679..ed5f8172 100644
--- a/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx
+++ b/apps/client/src/features/workspace/components/members/components/workspace-members-table.tsx
@@ -1,10 +1,10 @@
-import {Group, Table, Text, Badge} from "@mantine/core";
+import { Group, Table, Text, Badge } from "@mantine/core";
import {
useChangeMemberRoleMutation,
useWorkspaceMembersQuery,
} from "@/features/workspace/queries/workspace-query.ts";
-import {CustomAvatar} from "@/components/ui/custom-avatar.tsx";
-import React from "react";
+import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
+import React, { useState } from "react";
import RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
import {
getUserRoleLabel,
@@ -13,12 +13,21 @@ import {
import useUserRole from "@/hooks/use-user-role.tsx";
import { UserRole } from "@/lib/types.ts";
import { useTranslation } from "react-i18next";
+import Paginate from "@/components/common/paginate.tsx";
+import { SearchInput } from "@/components/common/search-input.tsx";
+import NoTableResults from "@/components/common/no-table-results.tsx";
export default function WorkspaceMembersTable() {
const { t } = useTranslation();
- const { data, isLoading } = useWorkspaceMembersQuery({ limit: 100 });
+ const [page, setPage] = useState(1);
+ const [search, setSearch] = useState(undefined);
+ const { data, isLoading } = useWorkspaceMembersQuery({
+ page,
+ limit: 100,
+ query: search,
+ });
const changeMemberRoleMutation = useChangeMemberRoleMutation();
- const {isAdmin, isOwner} = useUserRole();
+ const { isAdmin, isOwner } = useUserRole();
const assignableUserRoles = isOwner
? userRoleData
@@ -43,25 +52,34 @@ export default function WorkspaceMembersTable() {
return (
<>
- {data && (
-
-
-
-
- {t("User")}
- {t("Status")}
- {t("Role")}
-
-
+ {
+ setSearch(debouncedSearch);
+ setPage(1);
+ }}
+ />
+
+
+
+
+ {t("User")}
+ {t("Status")}
+ {t("Role")}
+
+
-
- {data?.items.map((user, index) => (
+
+ {data?.items.length > 0 ? (
+ data?.items.map((user, index) => (
-
-
+
+
-
+
{user.name}
@@ -84,10 +102,21 @@ export default function WorkspaceMembersTable() {
/>
- ))}
-
-
-
+ ))
+ ) : (
+
+ )}
+
+
+
+
+ {data?.items.length > 0 && (
+
)}
>
);
diff --git a/apps/client/src/features/workspace/queries/workspace-query.ts b/apps/client/src/features/workspace/queries/workspace-query.ts
index 204b83b4..c9f288de 100644
--- a/apps/client/src/features/workspace/queries/workspace-query.ts
+++ b/apps/client/src/features/workspace/queries/workspace-query.ts
@@ -1,4 +1,5 @@
import {
+ keepPreviousData,
useMutation,
useQuery,
useQueryClient,
@@ -22,6 +23,7 @@ import {
IInvitation,
IWorkspace,
} from "@/features/workspace/types/workspace.types.ts";
+import { IUser } from "@/features/user/types/user.types.ts";
export function useWorkspaceQuery(): UseQueryResult {
return useQuery({
@@ -40,10 +42,13 @@ export function useWorkspacePublicDataQuery(): UseQueryResult<
});
}
-export function useWorkspaceMembersQuery(params?: QueryParams) {
+export function useWorkspaceMembersQuery(
+ params?: QueryParams,
+): UseQueryResult, Error> {
return useQuery({
queryKey: ["workspaceMembers", params],
queryFn: () => getWorkspaceMembers(params),
+ placeholderData: keepPreviousData,
});
}
@@ -53,7 +58,6 @@ export function useChangeMemberRoleMutation() {
return useMutation({
mutationFn: (data) => changeMemberRole(data),
onSuccess: (data, variables) => {
- // TODO: change in cache instead
notifications.show({ message: "Member role updated successfully" });
queryClient.refetchQueries({
queryKey: ["workspaceMembers"],
@@ -72,6 +76,7 @@ export function useWorkspaceInvitationsQuery(
return useQuery({
queryKey: ["invitations", params],
queryFn: () => getPendingInvitations(params),
+ placeholderData: keepPreviousData,
});
}
@@ -82,7 +87,6 @@ export function useCreateInvitationMutation() {
mutationFn: (data) => createInvitation(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Invitation sent" });
- // TODO: mutate cache
queryClient.refetchQueries({
queryKey: ["invitations"],
});
diff --git a/apps/client/src/features/workspace/services/workspace-service.ts b/apps/client/src/features/workspace/services/workspace-service.ts
index c8e9c893..e1bc2071 100644
--- a/apps/client/src/features/workspace/services/workspace-service.ts
+++ b/apps/client/src/features/workspace/services/workspace-service.ts
@@ -18,7 +18,6 @@ export async function getWorkspacePublicData(): Promise {
return req.data;
}
-// Todo: fix all paginated types
export async function getWorkspaceMembers(
params?: QueryParams,
): Promise> {