Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ POSTGRESQL_PWD=dml2aWQK
POSTGRESQL_PWD_DECODED=vivid
POSTGRESQL_DB=main
POSTGRESQL_USE_SSL=false
POSTGRESQL_RLS_USER=rls_user
POSTGRESQL_RLS_PWD=dml2aWQK

# fileStorage
FILE_STORAGE_PATH="../uploads"
Expand Down
49 changes: 49 additions & 0 deletions demo-saas/admin/src/common/AssignDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useApolloClient } from "@apollo/client";
import { CancelButton, Dialog, SaveBoundary, SaveBoundarySaveButton } from "@comet/admin";
import { DialogActions, DialogContent } from "@mui/material";
import { type PropsWithChildren, type ReactNode } from "react";
import { FormattedMessage } from "react-intl";

type Props = {
onDialogClose: () => void;
open: boolean;
apolloCacheName?: string;
title?: ReactNode;
};

export const AssignDialog = ({ onDialogClose, open, apolloCacheName, children, title }: PropsWithChildren<Props>) => {
const client = useApolloClient();

return (
<SaveBoundary
onAfterSave={() => {
onDialogClose();
if (apolloCacheName) {
client.cache.evict({ fieldName: apolloCacheName });
}
}}
>
<Dialog
open={open}
onClose={() => {
onDialogClose();
}}
maxWidth="lg"
title={title}
>
<DialogContent>{children}</DialogContent>

<DialogActions>
<CancelButton
onClick={() => {
onDialogClose();
}}
/>
<SaveBoundarySaveButton disabled={false}>
<FormattedMessage id="common.assignSelection" defaultMessage="Assign selection" />
</SaveBoundarySaveButton>
</DialogActions>
</Dialog>
</SaveBoundary>
);
};
12 changes: 11 additions & 1 deletion demo-saas/admin/src/common/apollo/createApolloClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ApolloClient, ApolloLink, HttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { createErrorDialogApolloLink } from "@comet/admin";
import { includeInvisibleContentContext } from "@comet/cms-admin";
import fragmentTypes from "@src/fragmentTypes.json";
Expand All @@ -9,7 +10,16 @@ export const createApolloClient = (apiUrl: string) => {
credentials: "include",
});

const link = ApolloLink.from([createErrorDialogApolloLink(), includeInvisibleContentContext, httpLink]);
const tenantContext = setContext((_, { headers }) => {
return {
headers: {
...headers,
...{ "x-tenant-id": "f9c86c6c-0625-46c0-9be5-bee3a14cc7f4" },
},
};
});

const link = ApolloLink.from([createErrorDialogApolloLink(), includeInvisibleContentContext, tenantContext, httpLink]);

const cache = new InMemoryCache({
possibleTypes: fragmentTypes.possibleTypes,
Expand Down
27 changes: 27 additions & 0 deletions demo-saas/admin/src/tenants/TenantsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useApolloClient } from "@apollo/client";
import {
Button,
FieldSet,
FillSpace,
FullHeightContent,
Expand All @@ -17,13 +18,18 @@ import {
ToolbarBackButton,
useStackSwitch,
} from "@comet/admin";
import { Add } from "@comet/admin-icons";
import { ContentScopeIndicator, useContentScopeConfig } from "@comet/cms-admin";
import { AssignDialog } from "@src/common/AssignDialog";
import { DepartmentForm } from "@src/departments/generated/DepartmentForm";
import { DepartmentsGrid } from "@src/departments/generated/DepartmentsGrid";
import { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";

import { TenantForm } from "./generated/TenantForm";
import { TenantsGrid } from "./generated/TenantsGrid";
import { AssignTenantUser } from "./users/AssignTenantUser";
import { TenantUsersGrid } from "./users/generated/TenantUsersGrid";

const FormToolbar = () => (
<StackToolbar scopeIndicator={<ContentScopeIndicator global />}>
Expand All @@ -42,6 +48,7 @@ export function TenantsPage() {
const [TenantsStackSwitch, tenantsStackSwitchApi] = useStackSwitch();
const [DepartmentsStackSwitch, departmentsStackSwitchApi] = useStackSwitch();
const client = useApolloClient();
const [dialogOpen, setDialogOpen] = useState(false);

return (
<Stack topLevelTitle={<FormattedMessage id="tenants.tenants" defaultMessage="Tenants" />}>
Expand Down Expand Up @@ -133,6 +140,26 @@ export function TenantsPage() {
</StackPage>
</DepartmentsStackSwitch>
</RouterTab>
<RouterTab path="/users" label={<FormattedMessage id="tenants.users" defaultMessage="Users" />}>
<FullHeightContent>
<TenantUsersGrid
tenant={selectedTenantId}
toolbarAction={
<Button responsive startIcon={<Add />} onClick={() => setDialogOpen(true)}>
<FormattedMessage id="tenants.user.assign" defaultMessage="Assign user" />
</Button>
}
/>
</FullHeightContent>
<AssignDialog
title={<FormattedMessage id="tenants.assignUsers.dialog.title" defaultMessage="Assign Users" />}
apolloCacheName="tenantUsers"
onDialogClose={() => setDialogOpen(false)}
open={dialogOpen}
>
<AssignTenantUser tenantId={selectedTenantId} onDialogClose={() => setDialogOpen(false)} />
</AssignDialog>
</RouterTab>
</RouterTabs>
</StackMainContent>
</>
Expand Down
52 changes: 52 additions & 0 deletions demo-saas/admin/src/tenants/users/AssignTenantUser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { gql, useApolloClient } from "@apollo/client";
import { MainContent, Savable } from "@comet/admin";
import { useState } from "react";

import { type GQLAssignTenantUserMutation, type GQLAssignTenantUserMutationVariables } from "./AssignTenantUser.generated";
import { UserPermissionsUsersGrid } from "./generated/AssignUsersGrid";

const assignTenantUserQuery = gql`
mutation AssignTenantUser($tenant: ID!, $userId: String!) {
assignTenantUser(tenant: $tenant, userId: $userId) {
id
}
}
`;

type AssignTenantUserProps = {
tenantId: string;
onDialogClose: () => void;
};

export const AssignTenantUser = ({ tenantId, onDialogClose }: AssignTenantUserProps) => {
const client = useApolloClient();
const [values, setValues] = useState<string[]>([]);

return (
<>
<Savable
doSave={async () => {
for (const userId of values) {
await client.mutate<GQLAssignTenantUserMutation, GQLAssignTenantUserMutationVariables>({
mutation: assignTenantUserQuery,
variables: { tenant: tenantId, userId },
});
}
return true;
}}
hasChanges={values.length > 0}
doReset={() => {
setValues([]);
}}
/>
<MainContent disablePadding sx={{ height: 500 }}>
<UserPermissionsUsersGrid
rowSelectionModel={values}
onRowSelectionModelChange={(newUserSelection) => {
setValues(newUserSelection.map((rowId) => String(rowId)));
}}
/>
</MainContent>
</>
);
};
14 changes: 14 additions & 0 deletions demo-saas/admin/src/tenants/users/AssignUsersGrid.cometGen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from "@comet/admin-generator";
import { type GQLUserPermissionsUser } from "@src/graphql.generated";

export default defineConfig<GQLUserPermissionsUser>({
type: "grid",
gqlType: "UserPermissionsUser",
fragmentName: "AssignTenantUsersGrid",
queryParamsPrefix: "assignTenantUsers",
selectionProps: "multiSelect",
add: false,
edit: false,
delete: false,
columns: [{ type: "text", name: "name", headerName: "Name" }],
});
16 changes: 16 additions & 0 deletions demo-saas/admin/src/tenants/users/TenantUsersGrid.cometGen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from "@comet/admin-generator";
import { type GQLTenantUser } from "@src/graphql.generated";

export default defineConfig<GQLTenantUser>({
type: "grid",
gqlType: "TenantUser",
fragmentName: "TenantUsersGrid",
queryParamsPrefix: "tenantUsers",
newEntryText: "Assign User",
edit: false,
toolbarActionProp: true,
columns: [
{ type: "text", name: "userName", headerName: "User" },
{ type: "text", name: "tenant.name", headerName: "Tenant" },
],
});
70 changes: 70 additions & 0 deletions demo-saas/admin/src/tenants/users/generated/AssignUsersGrid.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading