diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 73a8c1fd..bc5146af 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -403,6 +403,7 @@ "Replace (Enter)": "Replace (Enter)", "Replace all (Ctrl+Alt+Enter)": "Replace all (Ctrl+Alt+Enter)", "Replace all": "Replace all", + "View all spaces": "View all spaces" "Error": "Error", "Failed to disable MFA": "Failed to disable MFA", "Disable two-factor authentication": "Disable two-factor authentication", diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index 29b4bb0e..f2772bb8 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -31,6 +31,7 @@ import Shares from "@/pages/settings/shares/shares.tsx"; import ShareLayout from "@/features/share/components/share-layout.tsx"; import ShareRedirect from "@/pages/share/share-redirect.tsx"; import { useTrackOrigin } from "@/hooks/use-track-origin"; +import SpacesPage from "@/pages/spaces/spaces.tsx"; import { MfaChallengePage } from "@/ee/mfa/pages/mfa-challenge-page"; import { MfaSetupRequiredPage } from "@/ee/mfa/pages/mfa-setup-required-page"; @@ -77,6 +78,7 @@ export default function App() { }> } /> + } /> } /> ( @@ -38,7 +40,7 @@ export function AppHeader() { <> - {!isHomeRoute && ( + {!hideSidebar && ( <> - {!isHomeRoute && ( + {!hideSidebar && ( ( - - {t("Spaces you belong to")} - + + + {t("Spaces you belong to")} + + {cards} + + + + ); } diff --git a/apps/client/src/features/space/components/spaces-page/all-spaces-list.module.css b/apps/client/src/features/space/components/spaces-page/all-spaces-list.module.css new file mode 100644 index 00000000..9baea232 --- /dev/null +++ b/apps/client/src/features/space/components/spaces-page/all-spaces-list.module.css @@ -0,0 +1,10 @@ +.spaceLink { + text-decoration: none; + color: inherit; + display: flex; + width: 100%; + + &:hover { + text-decoration: none; + } +} \ No newline at end of file diff --git a/apps/client/src/features/space/components/spaces-page/all-spaces-list.tsx b/apps/client/src/features/space/components/spaces-page/all-spaces-list.tsx new file mode 100644 index 00000000..deb7a0df --- /dev/null +++ b/apps/client/src/features/space/components/spaces-page/all-spaces-list.tsx @@ -0,0 +1,160 @@ +import { + Table, + Text, + Group, + ActionIcon, + Box, + Space, + Menu, + Avatar, + Anchor, +} from "@mantine/core"; +import { IconDots, IconSettings } from "@tabler/icons-react"; +import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { useState } from "react"; +import { useDisclosure } from "@mantine/hooks"; +import { formatMemberCount } from "@/lib"; +import { getSpaceUrl } from "@/lib/config"; +import { prefetchSpace } from "@/features/space/queries/space-query"; +import { SearchInput } from "@/components/common/search-input"; +import Paginate from "@/components/common/paginate"; +import NoTableResults from "@/components/common/no-table-results"; +import SpaceSettingsModal from "@/features/space/components/settings-modal"; +import classes from "./all-spaces-list.module.css"; + +interface AllSpacesListProps { + spaces: any[]; + onSearch: (query: string) => void; + page: number; + hasPrevPage?: boolean; + hasNextPage?: boolean; + onPageChange: (page: number) => void; +} + +export default function AllSpacesList({ + spaces, + onSearch, + page, + hasPrevPage, + hasNextPage, + onPageChange, +}: AllSpacesListProps) { + const { t } = useTranslation(); + const [settingsOpened, { open: openSettings, close: closeSettings }] = + useDisclosure(false); + const [selectedSpaceId, setSelectedSpaceId] = useState(null); + + const handleOpenSettings = (spaceId: string) => { + setSelectedSpaceId(spaceId); + openSettings(); + }; + + return ( + + + + + + + + + + {t("Space")} + {t("Members")} + + + + + + {spaces.length > 0 ? ( + spaces.map((space) => ( + + + + prefetchSpace(space.slug, space.id)} + > + +
+ + {space.name} + + {space.description && ( + + {space.description} + + )} +
+
+
+
+ + + {formatMemberCount(space.memberCount, t)} + + + + + + + + + + + + } + onClick={() => handleOpenSettings(space.id)} + > + {t("Space settings")} + + + + + +
+ )) + ) : ( + + )} +
+
+
+ + {spaces.length > 0 && ( + + )} + + {selectedSpaceId && ( + + )} +
+ ); +} diff --git a/apps/client/src/features/space/components/spaces-page/index.ts b/apps/client/src/features/space/components/spaces-page/index.ts new file mode 100644 index 00000000..748b581e --- /dev/null +++ b/apps/client/src/features/space/components/spaces-page/index.ts @@ -0,0 +1 @@ +export { default as AllSpacesList } from "./all-spaces-list"; \ No newline at end of file diff --git a/apps/client/src/lib/app-route.ts b/apps/client/src/lib/app-route.ts index 7b95d5c4..dc42dad5 100644 --- a/apps/client/src/lib/app-route.ts +++ b/apps/client/src/lib/app-route.ts @@ -1,5 +1,6 @@ const APP_ROUTE = { HOME: "/home", + SPACES: "/spaces", AUTH: { LOGIN: "/login", SIGNUP: "/signup", diff --git a/apps/client/src/pages/spaces/spaces.tsx b/apps/client/src/pages/spaces/spaces.tsx new file mode 100644 index 00000000..30df05d4 --- /dev/null +++ b/apps/client/src/pages/spaces/spaces.tsx @@ -0,0 +1,53 @@ +import { Container, Title, Text, Group, Box } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { Helmet } from "react-helmet-async"; +import { getAppName } from "@/lib/config"; +import { useGetSpacesQuery } from "@/features/space/queries/space-query"; +import CreateSpaceModal from "@/features/space/components/create-space-modal"; +import { AllSpacesList } from "@/features/space/components/spaces-page"; +import { usePaginateAndSearch } from "@/hooks/use-paginate-and-search"; +import useUserRole from "@/hooks/use-user-role"; + +export default function Spaces() { + const { t } = useTranslation(); + const { isAdmin } = useUserRole(); + const { search, page, setPage, handleSearch } = usePaginateAndSearch(); + + const { data, isLoading } = useGetSpacesQuery({ + page, + limit: 30, + query: search, + }); + + return ( + <> + + + {t("Spaces")} - {getAppName()} + + + + + + {t("Spaces")} + {isAdmin && } + + + + + {t("Spaces you belong to")} + + + + + + + ); +}