mirror of
https://github.com/documenso/documenso.git
synced 2026-10-04 02:33:54 +10:00
refactor(inbox): move partially approved into extended document status
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import type { TInboxStatus } from '@documenso/trpc/server/document-router/find-inbox.types';
|
||||
import { SignatureIcon } from '@documenso/ui/icons/signature';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import type { MessageDescriptor } from '@lingui/core';
|
||||
@@ -16,7 +15,7 @@ type FriendlyStatus = {
|
||||
color: string;
|
||||
};
|
||||
|
||||
export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus | TInboxStatus, FriendlyStatus> = {
|
||||
export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus, FriendlyStatus> = {
|
||||
PENDING: {
|
||||
label: msg`Pending`,
|
||||
labelExtended: msg`Document pending`,
|
||||
@@ -73,7 +72,7 @@ export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus | TInboxStatus,
|
||||
};
|
||||
|
||||
export type DocumentStatusProps = HTMLAttributes<HTMLSpanElement> & {
|
||||
status: ExtendedDocumentStatus | TInboxStatus;
|
||||
status: ExtendedDocumentStatus;
|
||||
inheritColor?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
import { useOptionalCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { STATS_COUNT_CAP } from '@documenso/lib/constants/document';
|
||||
import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import type { TFindDocumentsInternalResponse } from '@documenso/trpc/server/document-router/find-documents-internal.types';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { OrganisationType } from '@prisma/client';
|
||||
import { ListFilterIcon } from 'lucide-react';
|
||||
import { parseAsString, useQueryStates } from 'nuqs';
|
||||
import { useQueryStates } from 'nuqs';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { DocumentStatusProps } from '~/components/general/document/document-status';
|
||||
import { DocumentStatus, FRIENDLY_STATUS_MAP } from '~/components/general/document/document-status';
|
||||
import { FilterPill } from '~/components/general/filter-pill';
|
||||
import { documentsSearchParams } from '~/utils/documents-search-params';
|
||||
|
||||
type DocumentsTableStatusFilterProps = {
|
||||
/**
|
||||
* Per-status document counts, shown next to each option. When omitted no
|
||||
* counts are rendered.
|
||||
* Per-status document counts, shown next to each option. When omitted, or
|
||||
* when a status has no count, no count is rendered.
|
||||
*/
|
||||
stats?: TFindDocumentsInternalResponse['stats'];
|
||||
stats?: Partial<Record<ExtendedDocumentStatus, number>>;
|
||||
|
||||
/**
|
||||
* The statuses available for selection. Defaults to every status that
|
||||
* makes sense for the documents page.
|
||||
*/
|
||||
statuses?: readonly DocumentStatusProps['status'][];
|
||||
statuses?: readonly ExtendedDocumentStatus[];
|
||||
};
|
||||
|
||||
export const DocumentsTableStatusFilter = ({
|
||||
@@ -37,11 +34,9 @@ export const DocumentsTableStatusFilter = ({
|
||||
|
||||
const organisation = useOptionalCurrentOrganisation();
|
||||
|
||||
// Any string is read here since each page offers its own statuses, which
|
||||
// are matched against the selectable statuses below.
|
||||
const [{ status }, setSearchParams] = useQueryStates(
|
||||
{
|
||||
status: parseAsString,
|
||||
status: documentsSearchParams.status,
|
||||
page: documentsSearchParams.page,
|
||||
},
|
||||
{ history: 'push' },
|
||||
@@ -82,7 +77,7 @@ export const DocumentsTableStatusFilter = ({
|
||||
options={selectableStatuses.map((value) => ({
|
||||
value,
|
||||
label: <DocumentStatus status={value} />,
|
||||
trailing: stats && isExtendedDocumentStatus(value) ? formatStatsCount(stats[value]) : undefined,
|
||||
trailing: formatStatsCount(stats?.[value]),
|
||||
}))}
|
||||
testId="documents-table-status-filter"
|
||||
/>
|
||||
@@ -90,7 +85,7 @@ export const DocumentsTableStatusFilter = ({
|
||||
{/* Visually hidden document counts, for screen readers and tests. */}
|
||||
{stats && (
|
||||
<span className="sr-only" data-testid="documents-status-counts">
|
||||
{[...selectableStatuses, ExtendedDocumentStatus.ALL].filter(isExtendedDocumentStatus).map((value) => (
|
||||
{[...selectableStatuses, ExtendedDocumentStatus.ALL].map((value) => (
|
||||
<span key={value}>
|
||||
{_(FRIENDLY_STATUS_MAP[value].label)}:{' '}
|
||||
<span data-testid={`documents-status-count-${value}`}>{stats[value]}</span>
|
||||
@@ -112,6 +107,10 @@ const SELECTABLE_STATUSES: ExtendedDocumentStatus[] = [
|
||||
ExtendedDocumentStatus.EXPIRED,
|
||||
];
|
||||
|
||||
const formatStatsCount = (count: number) => {
|
||||
const formatStatsCount = (count: number | undefined) => {
|
||||
if (count === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return count >= STATS_COUNT_CAP ? `${STATS_COUNT_CAP.toLocaleString()}+` : count.toString();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import type { TFindInboxResponse } from '@documenso/trpc/server/document-router/find-inbox.types';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@@ -131,7 +132,7 @@ export const InboxTable = () => {
|
||||
.with({ status: DocumentStatusEnum.COMPLETED }, () => (
|
||||
<Trans>Documents that you have completed will appear here</Trans>
|
||||
))
|
||||
.with({ status: 'PARTIALLY_APPROVED' }, () => (
|
||||
.with({ status: ExtendedDocumentStatus.PARTIALLY_APPROVED }, () => (
|
||||
<Trans>Documents that are waiting on other recipients will appear here</Trans>
|
||||
))
|
||||
.with({ status: DocumentStatusEnum.REJECTED }, () => (
|
||||
@@ -278,5 +279,5 @@ const getInboxStatus = (row: DocumentsTableRow, email: string) => {
|
||||
recipient.signingStatus !== SigningStatus.NOT_SIGNED,
|
||||
);
|
||||
|
||||
return isWaitingOnOthers ? 'PARTIALLY_APPROVED' : row.status;
|
||||
return isWaitingOnOthers ? ExtendedDocumentStatus.PARTIALLY_APPROVED : row.status;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import type { Page } from '@playwright/test';
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { DocumentStatus, RecipientRole, SigningStatus, TeamMemberRole } from '@prisma/client';
|
||||
@@ -37,7 +38,7 @@ const searchInbox = async (page: Page, query: string) => {
|
||||
// spelling "Canceled" for the `Cancelled` source string.
|
||||
const INBOX_STATUS_LABELS = {
|
||||
[DocumentStatus.PENDING]: 'Pending',
|
||||
PARTIALLY_APPROVED: 'Partially Approved',
|
||||
[ExtendedDocumentStatus.PARTIALLY_APPROVED]: 'Partially Approved',
|
||||
[DocumentStatus.COMPLETED]: 'Completed',
|
||||
[DocumentStatus.REJECTED]: 'Rejected',
|
||||
[DocumentStatus.CANCELLED]: 'Canceled',
|
||||
@@ -154,7 +155,7 @@ test.describe('Inbox - Search & Status Filter', () => {
|
||||
await expect(inboxRow(page, 'Inbox Unsigned Document')).toBeVisible();
|
||||
await expect(inboxRow(page, 'Inbox Signed Document')).not.toBeVisible();
|
||||
|
||||
await selectInboxStatus(page, 'PARTIALLY_APPROVED');
|
||||
await selectInboxStatus(page, ExtendedDocumentStatus.PARTIALLY_APPROVED);
|
||||
|
||||
await expect(page.getByTestId('documents-table-status-filter')).toContainText('Partially Approved');
|
||||
await expect(inboxRow(page, 'Inbox Signed Document')).toBeVisible();
|
||||
|
||||
@@ -13,7 +13,7 @@ export const getDocumentStats = async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const stats: Record<Exclude<ExtendedDocumentStatus, 'INBOX' | 'EXPIRED'>, number> = {
|
||||
const stats: Record<Exclude<ExtendedDocumentStatus, 'INBOX' | 'EXPIRED' | 'PARTIALLY_APPROVED'>, number> = {
|
||||
[ExtendedDocumentStatus.DRAFT]: 0,
|
||||
[ExtendedDocumentStatus.PENDING]: 0,
|
||||
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||
|
||||
@@ -326,6 +326,7 @@ export const findDocuments = async ({
|
||||
]),
|
||||
),
|
||||
)
|
||||
.with(ExtendedDocumentStatus.PARTIALLY_APPROVED, () => null) // Only the inbox offers this status.
|
||||
.exhaustive();
|
||||
};
|
||||
|
||||
@@ -488,6 +489,7 @@ export const findDocuments = async ({
|
||||
return eb.and([teamDeletedFilter(eb), visibilityFilter(eb), hasExpiredRecipient(eb), eb.or(accessBranches)]);
|
||||
}),
|
||||
)
|
||||
.with(ExtendedDocumentStatus.PARTIALLY_APPROVED, () => null) // Only the inbox offers this status.
|
||||
.exhaustive();
|
||||
};
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ export const getStats = async ({ userId, teamId, period, search = '', folderId,
|
||||
// `expired` is intentionally excluded from `all` — it overlaps PENDING.
|
||||
const all = Math.min(draft + pending + completed + rejected + cancelled + inbox, STATS_COUNT_CAP);
|
||||
|
||||
const stats: Record<ExtendedDocumentStatus, number> = {
|
||||
const stats: Record<Exclude<ExtendedDocumentStatus, 'PARTIALLY_APPROVED'>, number> = {
|
||||
[ExtendedDocumentStatus.DRAFT]: draft,
|
||||
[ExtendedDocumentStatus.PENDING]: pending,
|
||||
[ExtendedDocumentStatus.COMPLETED]: completed,
|
||||
|
||||
@@ -5,6 +5,7 @@ export const ExtendedDocumentStatus = {
|
||||
INBOX: 'INBOX',
|
||||
ALL: 'ALL',
|
||||
EXPIRED: 'EXPIRED',
|
||||
PARTIALLY_APPROVED: 'PARTIALLY_APPROVED',
|
||||
} as const;
|
||||
|
||||
export type ExtendedDocumentStatus = (typeof ExtendedDocumentStatus)[keyof typeof ExtendedDocumentStatus];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { FindResultResponse } from '@documenso/lib/types/search-params';
|
||||
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import type { Envelope, Prisma } from '@prisma/client';
|
||||
import { DocumentStatus, EnvelopeType, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
import { match, P } from 'ts-pattern';
|
||||
@@ -85,7 +86,7 @@ export const findInbox = async ({ userId, page = 1, perPage = 10, query = '', st
|
||||
status: DocumentStatus.PENDING,
|
||||
recipients: { some: unsignedUserRecipient },
|
||||
}))
|
||||
.with('PARTIALLY_APPROVED', () => ({
|
||||
.with(ExtendedDocumentStatus.PARTIALLY_APPROVED, () => ({
|
||||
status: DocumentStatus.PENDING,
|
||||
recipients: { some: userRecipient, none: unsignedUserRecipient },
|
||||
}))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { ZDocumentManySchema } from '@documenso/lib/types/document';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { DocumentStatus } from '@prisma/client';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
@@ -16,11 +16,11 @@ import { z } from 'zod';
|
||||
* that wait on other recipients.
|
||||
*/
|
||||
export const INBOX_STATUSES = [
|
||||
DocumentStatus.PENDING,
|
||||
'PARTIALLY_APPROVED',
|
||||
DocumentStatus.COMPLETED,
|
||||
DocumentStatus.REJECTED,
|
||||
DocumentStatus.CANCELLED,
|
||||
ExtendedDocumentStatus.PENDING,
|
||||
ExtendedDocumentStatus.PARTIALLY_APPROVED,
|
||||
ExtendedDocumentStatus.COMPLETED,
|
||||
ExtendedDocumentStatus.REJECTED,
|
||||
ExtendedDocumentStatus.CANCELLED,
|
||||
] as const;
|
||||
|
||||
export const ZInboxStatusSchema = z.enum(INBOX_STATUSES);
|
||||
|
||||
Reference in New Issue
Block a user