feat: add template page (#1395)

Add a template page view to allow users to see more details about a
template at a glance.
This commit is contained in:
David Nguyen
2024-11-05 17:36:30 +09:00
committed by GitHub
parent 32b65c4d49
commit 4fa6dc1e21
24 changed files with 1096 additions and 96 deletions

View File

@ -2,8 +2,9 @@
import { useState } from 'react';
import { Trans } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { EyeOffIcon } from 'lucide-react';
import { Clock, EyeOffIcon } from 'lucide-react';
import { P, match } from 'ts-pattern';
import {
@ -18,8 +19,10 @@ import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
import type { DocumentMeta } from '@documenso/prisma/client';
import { FieldType, SigningStatus } from '@documenso/prisma/client';
import { FieldRootContainer } from '@documenso/ui/components/field/field';
import { SignatureIcon } from '@documenso/ui/icons/signature';
import { cn } from '@documenso/ui/lib/utils';
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
import { Badge } from '@documenso/ui/primitives/badge';
import { FRIENDLY_FIELD_TYPE } from '@documenso/ui/primitives/document-flow/types';
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { PopoverHover } from '@documenso/ui/primitives/popover';
@ -27,9 +30,14 @@ import { PopoverHover } from '@documenso/ui/primitives/popover';
export type DocumentReadOnlyFieldsProps = {
fields: DocumentField[];
documentMeta?: DocumentMeta;
showFieldStatus?: boolean;
};
export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnlyFieldsProps) => {
export const DocumentReadOnlyFields = ({
documentMeta,
fields,
showFieldStatus = true,
}: DocumentReadOnlyFieldsProps) => {
const { _ } = useLingui();
const [hiddenFieldIds, setHiddenFieldIds] = useState<Record<string, boolean>>({});
@ -58,15 +66,37 @@ export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnl
</Avatar>
}
contentProps={{
className: 'relative flex w-fit flex-col p-2.5 text-sm',
className: 'relative flex w-fit flex-col p-4 text-sm',
}}
>
<p className="font-semibold">
{field.Recipient.signingStatus === SigningStatus.SIGNED ? 'Signed' : 'Pending'}{' '}
{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type]).toLowerCase()} field
{showFieldStatus && (
<Badge
className="mx-auto mb-1 py-0.5"
variant={
field.Recipient.signingStatus === SigningStatus.SIGNED
? 'default'
: 'secondary'
}
>
{field.Recipient.signingStatus === SigningStatus.SIGNED ? (
<>
<SignatureIcon className="mr-1 h-3 w-3" />
<Trans>Signed</Trans>
</>
) : (
<>
<Clock className="mr-1 h-3 w-3" />
<Trans>Pending</Trans>
</>
)}
</Badge>
)}
<p className="text-center font-semibold">
<span>{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])} field</span>
</p>
<p className="text-muted-foreground text-xs">
<p className="text-muted-foreground mt-1 text-center text-xs">
{field.Recipient.name
? `${field.Recipient.name} (${field.Recipient.email})`
: field.Recipient.email}{' '}

View File

@ -0,0 +1,50 @@
import React, { useMemo } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { Select, SelectContent, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
export type SearchParamSelector = {
paramKey: string;
isValueValid: (value: unknown) => boolean;
children: React.ReactNode;
};
export const SearchParamSelector = ({ children, paramKey, isValueValid }: SearchParamSelector) => {
const pathname = usePathname();
const searchParams = useSearchParams();
const router = useRouter();
const value = useMemo(() => {
const p = searchParams?.get(paramKey) ?? 'all';
return isValueValid(p) ? p : 'all';
}, [searchParams]);
const onValueChange = (newValue: string) => {
if (!pathname) {
return;
}
const params = new URLSearchParams(searchParams?.toString());
params.set(paramKey, newValue);
if (newValue === '' || newValue === 'all') {
params.delete(paramKey);
}
router.push(`${pathname}?${params.toString()}`, { scroll: false });
};
return (
<Select defaultValue={value} onValueChange={onValueChange}>
<SelectTrigger className="text-muted-foreground max-w-[200px]">
<SelectValue />
</SelectTrigger>
<SelectContent position="popper">{children}</SelectContent>
</Select>
);
};