Skip to content

Commit 6e55782

Browse files
committed
Add user assignment to tenant
1 parent 5ee770d commit 6e55782

21 files changed

+643
-9
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { useApolloClient } from "@apollo/client";
2+
import { CancelButton, Dialog, SaveBoundary, SaveBoundarySaveButton } from "@comet/admin";
3+
import { DialogActions, DialogContent } from "@mui/material";
4+
import { type PropsWithChildren, type ReactNode } from "react";
5+
import { FormattedMessage } from "react-intl";
6+
7+
type Props = {
8+
onDialogClose: () => void;
9+
open: boolean;
10+
apolloCacheName?: string;
11+
title?: ReactNode;
12+
};
13+
14+
export const AssignDialog = ({ onDialogClose, open, apolloCacheName, children, title }: PropsWithChildren<Props>) => {
15+
const client = useApolloClient();
16+
17+
return (
18+
<SaveBoundary
19+
onAfterSave={() => {
20+
onDialogClose();
21+
if (apolloCacheName) {
22+
client.cache.evict({ fieldName: apolloCacheName });
23+
}
24+
}}
25+
>
26+
<Dialog
27+
open={open}
28+
onClose={() => {
29+
onDialogClose();
30+
}}
31+
maxWidth="lg"
32+
title={title}
33+
>
34+
<DialogContent>{children}</DialogContent>
35+
36+
<DialogActions>
37+
<CancelButton
38+
onClick={() => {
39+
onDialogClose();
40+
}}
41+
/>
42+
<SaveBoundarySaveButton disabled={false}>
43+
<FormattedMessage id="common.assignSelection" defaultMessage="Assign selection" />
44+
</SaveBoundarySaveButton>
45+
</DialogActions>
46+
</Dialog>
47+
</SaveBoundary>
48+
);
49+
};

demo-saas/admin/src/tenants/TenantsPage.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useApolloClient } from "@apollo/client";
22
import {
3+
Button,
34
FieldSet,
45
FillSpace,
56
FullHeightContent,
@@ -17,13 +18,18 @@ import {
1718
ToolbarBackButton,
1819
useStackSwitch,
1920
} from "@comet/admin";
21+
import { Add } from "@comet/admin-icons";
2022
import { ContentScopeIndicator, useContentScopeConfig } from "@comet/cms-admin";
23+
import { AssignDialog } from "@src/common/AssignDialog";
2124
import { DepartmentForm } from "@src/departments/generated/DepartmentForm";
2225
import { DepartmentsGrid } from "@src/departments/generated/DepartmentsGrid";
26+
import { useState } from "react";
2327
import { FormattedMessage, useIntl } from "react-intl";
2428

2529
import { TenantForm } from "./generated/TenantForm";
2630
import { TenantsGrid } from "./generated/TenantsGrid";
31+
import { AssignTenantUser } from "./users/AssignTenantUser";
32+
import { TenantUsersGrid } from "./users/generated/TenantUsersGrid";
2733

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

4653
return (
4754
<Stack topLevelTitle={<FormattedMessage id="tenants.tenants" defaultMessage="Tenants" />}>
@@ -133,6 +140,26 @@ export function TenantsPage() {
133140
</StackPage>
134141
</DepartmentsStackSwitch>
135142
</RouterTab>
143+
<RouterTab path="/users" label={<FormattedMessage id="tenants.users" defaultMessage="Users" />}>
144+
<FullHeightContent>
145+
<TenantUsersGrid
146+
tenant={selectedTenantId}
147+
toolbarAction={
148+
<Button responsive startIcon={<Add />} onClick={() => setDialogOpen(true)}>
149+
<FormattedMessage id="tenants.user.assign" defaultMessage="Assign user" />
150+
</Button>
151+
}
152+
/>
153+
</FullHeightContent>
154+
<AssignDialog
155+
title={<FormattedMessage id="tenants.assignUsers.dialog.title" defaultMessage="Assign Users" />}
156+
apolloCacheName="tenantUsers"
157+
onDialogClose={() => setDialogOpen(false)}
158+
open={dialogOpen}
159+
>
160+
<AssignTenantUser tenantId={selectedTenantId} onDialogClose={() => setDialogOpen(false)} />
161+
</AssignDialog>
162+
</RouterTab>
136163
</RouterTabs>
137164
</StackMainContent>
138165
</>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { gql, useApolloClient } from "@apollo/client";
2+
import { MainContent, Savable } from "@comet/admin";
3+
import { useState } from "react";
4+
5+
import { type GQLAssignTenantUserMutation, type GQLAssignTenantUserMutationVariables } from "./AssignTenantUser.generated";
6+
import { UserPermissionsUsersGrid } from "./generated/AssignUsersGrid";
7+
8+
const assignTenantUserQuery = gql`
9+
mutation AssignTenantUser($tenant: ID!, $userId: String!) {
10+
assignTenantUser(tenant: $tenant, userId: $userId) {
11+
id
12+
}
13+
}
14+
`;
15+
16+
type AssignTenantUserProps = {
17+
tenantId: string;
18+
onDialogClose: () => void;
19+
};
20+
21+
export const AssignTenantUser = ({ tenantId, onDialogClose }: AssignTenantUserProps) => {
22+
const client = useApolloClient();
23+
const [values, setValues] = useState<string[]>([]);
24+
25+
return (
26+
<>
27+
<Savable
28+
doSave={async () => {
29+
for (const userId of values) {
30+
await client.mutate<GQLAssignTenantUserMutation, GQLAssignTenantUserMutationVariables>({
31+
mutation: assignTenantUserQuery,
32+
variables: { tenant: tenantId, userId },
33+
});
34+
}
35+
return true;
36+
}}
37+
hasChanges={values.length > 0}
38+
doReset={() => {
39+
setValues([]);
40+
}}
41+
/>
42+
<MainContent disablePadding sx={{ height: 500 }}>
43+
<UserPermissionsUsersGrid
44+
rowSelectionModel={values}
45+
onRowSelectionModelChange={(newUserSelection) => {
46+
setValues(newUserSelection.map((rowId) => String(rowId)));
47+
}}
48+
/>
49+
</MainContent>
50+
</>
51+
);
52+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { defineConfig } from "@comet/admin-generator";
2+
import { type GQLUserPermissionsUser } from "@src/graphql.generated";
3+
4+
export default defineConfig<GQLUserPermissionsUser>({
5+
type: "grid",
6+
gqlType: "UserPermissionsUser",
7+
fragmentName: "AssignTenantUsersGrid",
8+
queryParamsPrefix: "assignTenantUsers",
9+
selectionProps: "multiSelect",
10+
add: false,
11+
edit: false,
12+
delete: false,
13+
columns: [{ type: "text", name: "name", headerName: "Name" }],
14+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { defineConfig } from "@comet/admin-generator";
2+
import { type GQLTenantUser } from "@src/graphql.generated";
3+
4+
export default defineConfig<GQLTenantUser>({
5+
type: "grid",
6+
gqlType: "TenantUser",
7+
fragmentName: "TenantUsersGrid",
8+
queryParamsPrefix: "tenantUsers",
9+
newEntryText: "Assign User",
10+
edit: false,
11+
toolbarActionProp: true,
12+
columns: [
13+
{ type: "text", name: "userName", headerName: "User" },
14+
{ type: "text", name: "tenant.name", headerName: "Tenant" },
15+
],
16+
});

demo-saas/admin/src/tenants/users/generated/AssignUsersGrid.tsx

Lines changed: 70 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo-saas/admin/src/tenants/users/generated/TenantUsersGrid.tsx

Lines changed: 113 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)