import { useMemo } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro'; import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client'; import { Loader } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { Link } from 'react-router'; import { z } from 'zod'; import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; import { ORGANISATION_MEMBER_ROLE_HIERARCHY } from '@documenso/lib/constants/organisations'; import { EXTENDED_ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations'; import { TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams-translations'; import { AppError } from '@documenso/lib/errors/app-error'; import { trpc } from '@documenso/trpc/react'; import type { TFindOrganisationGroupsResponse } from '@documenso/trpc/server/organisation-router/find-organisation-groups.types'; import type { TFindOrganisationMembersResponse } from '@documenso/trpc/server/organisation-router/find-organisation-members.types'; import { Button } from '@documenso/ui/primitives/button'; import { DataTable, type DataTableColumnDef } from '@documenso/ui/primitives/data-table'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { MultiSelectCombobox } from '@documenso/ui/primitives/multi-select-combobox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@documenso/ui/primitives/select'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { OrganisationGroupDeleteDialog } from '~/components/dialogs/organisation-group-delete-dialog'; import { GenericErrorLayout } from '~/components/general/generic-error-layout'; import { SettingsHeader } from '~/components/general/settings-header'; import type { Route } from './+types/o.$orgUrl.settings.groups.$id'; export default function OrganisationGroupSettingsPage({ params }: Route.ComponentProps) { const { t } = useLingui(); const organisation = useCurrentOrganisation(); const groupId = params.id; const { data: members, isLoading: isLoadingMembers } = trpc.organisation.member.find.useQuery({ organisationId: organisation.id, }); const { data: groupData, isLoading: isLoadingGroup } = trpc.organisation.group.find.useQuery( { organisationId: organisation.id, organisationGroupId: groupId, page: 1, perPage: 1, types: [OrganisationGroupType.CUSTOM], }, { enabled: !!organisation.id && !!groupId, }, ); const group = groupData?.data.find((g) => g.id === groupId); if (isLoadingGroup || isLoadingMembers) { return (
); } // Todo: Update UI, currently out of place. if (!group) { return ( Go back } secondaryButton={null} /> ); } return (
Delete } />
); } const ZUpdateOrganisationGroupFormSchema = z.object({ name: z.string().min(1, msg`Name is required`.id), organisationRole: z.nativeEnum(OrganisationMemberRole), memberIds: z.array(z.string()), }); type TUpdateOrganisationGroupFormSchema = z.infer; type OrganisationGroupFormOptions = { group: TFindOrganisationGroupsResponse['data'][number]; organisationMembers: TFindOrganisationMembersResponse['data']; }; const OrganisationGroupForm = ({ group, organisationMembers }: OrganisationGroupFormOptions) => { const { toast } = useToast(); const { t } = useLingui(); const organisation = useCurrentOrganisation(); const { mutateAsync: updateOrganisationGroup } = trpc.organisation.group.update.useMutation(); const form = useForm({ resolver: zodResolver(ZUpdateOrganisationGroupFormSchema), defaultValues: { name: group.name || '', organisationRole: group.organisationRole, memberIds: group.members.map((member) => member.id), }, }); const onSubmit = async (values: TUpdateOrganisationGroupFormSchema) => { try { await updateOrganisationGroup({ id: group.id, name: values.name, organisationRole: values.organisationRole, memberIds: values.memberIds, }); toast({ title: t`Success`, description: t`Group has been updated successfully`, duration: 5000, }); } catch (err) { const error = AppError.parseError(err); console.error(error); toast({ title: t`An error occurred`, description: t`We couldn't update the group. Please try again.`, variant: 'destructive', }); } }; const teamGroupsColumns = useMemo(() => { return [ { header: t`Team`, accessorKey: 'name', }, { header: t`Team Role`, cell: ({ row }) => t(TEAM_MEMBER_ROLE_MAP[row.original.teamRole]), }, ] satisfies DataTableColumnDef[]; }, []); return (
( Group Name )} /> ( Organisation Role The organisation role that will be applied to all members in this group. )} /> ( Members ({ label: member.name || member.email, value: member.id, }))} selectedValues={field.value} onChange={field.onChange} className="w-full" emptySelectionPlaceholder={t`Select members`} /> Select the members to include in this group )} />
Team Assignments
Teams that this organisation group is currently assigned to
); };