Files
homarr/apps/nextjs/src/components/access/group-access-form.tsx
Meier Lukas 408cdeb5c3 feat: add integration access settings (#725)
* feat: add integration access settings

* fix: typecheck and test issues

* fix: test timeout

* chore: address pull request feedback

* chore: add throw if action forbidden for integration permissions

* fix: unable to create new migrations because of duplicate prevId in sqlite snapshots

* chore: add sqlite migration for integration permissions

* test: add unit tests for integration access

* test: add permission checks to integration router tests

* test: add unit test for integration permissions

* chore: add mysql migration

* fix: format issues
2024-07-08 00:00:37 +02:00

102 lines
3.5 KiB
TypeScript

import { useState } from "react";
import Link from "next/link";
import { Anchor, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
import { IconPlus } from "@tabler/icons-react";
import { useModalAction } from "@homarr/modals";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { AccessQueryData } from "./access-settings";
import { AccessSelectRow } from "./access-table-rows";
import { useAccessContext } from "./context";
import type { AccessFormType } from "./form";
import { FormProvider, useForm } from "./form";
import { GroupSelectModal } from "./group-select-modal";
import type { FormProps } from "./user-access-form";
export const GroupAccessForm = <TPermission extends string>({
accessQueryData,
handleCountChange,
handleSubmit,
isPending,
}: Omit<FormProps<TPermission>, "entity">) => {
const { defaultPermission } = useAccessContext();
const [groups, setGroups] = useState<Map<string, AccessQueryData<string>["groups"][number]["group"]>>(
new Map(accessQueryData.groups.map(({ group }) => [group.id, group])),
);
const { openModal } = useModalAction(GroupSelectModal);
const t = useI18n();
const tPermissions = useScopedI18n("permission");
const form = useForm({
initialValues: {
items: accessQueryData.groups.map(({ group, permission }) => ({
principalId: group.id,
permission,
})),
},
});
const handleAddUser = () => {
openModal({
presentGroupIds: form.values.items.map(({ principalId: id }) => id),
onSelect: (group) => {
setGroups((prev) => new Map(prev).set(group.id, group));
form.setFieldValue("items", [
{
principalId: group.id,
permission: defaultPermission,
},
...form.values.items,
]);
handleCountChange((prev) => prev + 1);
},
});
};
return (
<form onSubmit={form.onSubmit((values) => handleSubmit(values as AccessFormType<TPermission>))}>
<FormProvider form={form}>
<Stack pt="sm">
<Table>
<TableThead>
<TableTr>
<TableTh style={{ whiteSpace: "nowrap" }}>{tPermissions("field.group.label")}</TableTh>
<TableTh>{tPermissions("field.permission.label")}</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{form.values.items.map((row, index) => (
<AccessSelectRow
key={row.principalId}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
itemContent={<GroupItemContent group={groups.get(row.principalId)!} />}
permission={row.permission}
index={index}
handleCountChange={handleCountChange}
/>
))}
</TableTbody>
</Table>
<Group justify="space-between">
<Button rightSection={<IconPlus size="1rem" />} variant="light" onClick={handleAddUser}>
{t("common.action.add")}
</Button>
<Button type="submit" loading={isPending} color="teal">
{t("permission.action.saveGroup")}
</Button>
</Group>
</Stack>
</FormProvider>
</form>
);
};
export const GroupItemContent = ({ group }: { group: AccessQueryData<string>["groups"][number]["group"] }) => {
return (
<Anchor component={Link} href={`/manage/users/groups/${group.id}`} size="sm" style={{ whiteSpace: "nowrap" }}>
{group.name}
</Anchor>
);
};