Files
sign/apps/remix/app/routes/_authenticated+/documents.$id._index.tsx

261 lines
9.7 KiB
TypeScript
Raw Normal View History

2024-08-27 20:34:39 +09:00
import { useLingui } from '@lingui/react';
2025-01-02 15:33:37 +11:00
import { Plural, Trans } from '@lingui/react/macro';
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
2024-02-15 18:20:10 +11:00
import { ChevronLeft, Clock9, Users2 } from 'lucide-react';
2025-01-02 15:33:37 +11:00
import { Link, redirect } from 'react-router';
2024-02-12 17:30:23 +11:00
import { match } from 'ts-pattern';
2025-02-17 22:46:36 +11:00
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
2025-01-02 15:33:37 +11:00
import { useSession } from '@documenso/lib/client-only/providers/session';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
fix: show field on pending document (#1158) ## Description This pull request introduces the functionality to display pending fields on the document page view. This enhancement allows users to see which fields are pending and need to be completed. ![CleanShot 2024-05-14 at 23 31 29@2x](https://github.com/documenso/documenso/assets/55143799/ffea0b29-d251-4dd5-9742-5416ac8262ad) ## Changes Made - Added `getPendingFieldsForDocument` function in `packages/lib/server-only/field/get-pending-fields-for-document.ts` to fetch pending fields for a document. - Created a new component `DocumentPendingFields` in `document-pending-fields.tsx` to display the pending fields with options to hide individual fields. ## Testing Performed - Tested the new feature by creating documents with pending fields and verifying their display on the document page view. - Verified that the pending fields are correctly hidden when the "Hide field" button is clicked. - Ran unit tests for the new functionality and existing components to ensure no regressions. ## Checklist - [x] I have tested these changes locally and they work as expected. - [x] I have added/updated tests that prove the effectiveness of these changes. - [x] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## Additional Notes No additional notes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced logic for handling pending and completed document fields based on signing status. - **Refactor** - Replaced `getCompletedFieldsForDocument` with `getFieldsForDocument`. - Updated `DocumentReadOnlyFields` component to `DocumentPendingFields`. - **Bug Fixes** - Improved field retrieval accuracy and display based on recipient signing status. - **Style** - Enhanced UI elements with new icons and button adjustments for better user interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 06:08:06 +00:00
import { getFieldsForDocument } from '@documenso/lib/server-only/field/get-fields-for-document';
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
2025-02-17 22:46:36 +11:00
import { type TGetTeamByUrlResponse, getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { Badge } from '@documenso/ui/primitives/badge';
2024-02-15 18:20:10 +11:00
import { Button } from '@documenso/ui/primitives/button';
2024-02-12 17:30:23 +11:00
import { Card, CardContent } from '@documenso/ui/primitives/card';
2025-02-26 21:48:06 +11:00
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
2025-01-02 15:33:37 +11:00
import { DocumentHistorySheet } from '~/components/general/document/document-history-sheet';
import { DocumentPageViewButton } from '~/components/general/document/document-page-view-button';
import { DocumentPageViewDropdown } from '~/components/general/document/document-page-view-dropdown';
import { DocumentPageViewInformation } from '~/components/general/document/document-page-view-information';
import { DocumentPageViewRecentActivity } from '~/components/general/document/document-page-view-recent-activity';
import { DocumentPageViewRecipients } from '~/components/general/document/document-page-view-recipients';
import { DocumentReadOnlyFields } from '~/components/general/document/document-read-only-fields';
import { DocumentRecipientLinkCopyDialog } from '~/components/general/document/document-recipient-link-copy-dialog';
2024-02-12 17:30:23 +11:00
import {
DocumentStatus as DocumentStatusComponent,
FRIENDLY_STATUS_MAP,
2025-01-02 15:33:37 +11:00
} from '~/components/general/document/document-status';
import { StackAvatarsWithTooltip } from '~/components/general/stack-avatars-with-tooltip';
import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
2024-02-12 17:30:23 +11:00
2025-02-21 15:53:23 +11:00
import type { Route } from './+types/documents.$id._index';
2025-02-17 22:46:36 +11:00
export async function loader({ params, request }: Route.LoaderArgs) {
const { user } = await getSession(request);
let team: TGetTeamByUrlResponse | null = null;
if (params.teamUrl) {
team = await getTeamByUrl({ userId: user.id, teamUrl: params.teamUrl });
}
const { id } = params;
const documentId = Number(id);
const documentRootPath = formatDocumentsPath(team?.url);
if (!documentId || Number.isNaN(documentId)) {
2025-01-02 15:33:37 +11:00
throw redirect(documentRootPath);
}
const document = await getDocumentById({
2024-12-10 16:11:20 +09:00
documentId,
userId: user.id,
teamId: team?.id,
}).catch(() => null);
if (document?.teamId && !team?.url) {
2025-01-02 15:33:37 +11:00
throw redirect(documentRootPath);
}
const documentVisibility = document?.visibility;
const currentTeamMemberRole = team?.currentTeamMember?.role;
2025-01-13 13:41:53 +11:00
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
let canAccessDocument = true;
feat: add global settings for teams (#1391) ## Description This PR introduces global settings for teams. At the moment, it allows team admins to configure the following: * The default visibility of the documents uploaded to the team account * Whether to include the document owner (sender) details when sending emails to the recipients. ### Include Sender Details If the Sender Details setting is enabled, the emails sent by the team will include the sender's name: > "Example User" on behalf of "Example Team" has invited you to sign "document.pdf" Otherwise, the email will say: > "Example Team" has invited you to sign "document.pdf" ### Default Document Visibility This new option allows users to set the default visibility for the documents uploaded to the team account. It can have the following values: * Everyone * Manager and above * Admins only If the default document visibility isn't set, the document will be set to the role of the user who created the document: * If a user with the "User" role creates a document, the document's visibility is set to "Everyone". * Manager role -> "Manager and above" * Admin role -> "Admins only" Otherwise, if there is a default document visibility value, it uses that value. #### Gotcha To avoid issues, the `document owner` and the `recipient` can access the document irrespective of their role. For example: * If a team member with the role "Member" uploads a document and the default document visibility is "Admins", only the document owner and admins can access the document. * Similar to the other scenarios. * If an admin uploads a document and the default document visibility is "Admins", the recipient can access the document. * The admins have access to all the documents. * Managers have access to documents with the visibility set to "Everyone" and "Manager and above" * Members have access only to the documents with the visibility set to "Everyone". ## Testing Performed Tested it locally.
2024-11-08 13:50:49 +02:00
if (team && !isRecipient && document?.userId !== user.id) {
canAccessDocument = match([documentVisibility, currentTeamMemberRole])
.with([DocumentVisibility.EVERYONE, TeamMemberRole.ADMIN], () => true)
.with([DocumentVisibility.EVERYONE, TeamMemberRole.MANAGER], () => true)
.with([DocumentVisibility.EVERYONE, TeamMemberRole.MEMBER], () => true)
.with([DocumentVisibility.MANAGER_AND_ABOVE, TeamMemberRole.ADMIN], () => true)
.with([DocumentVisibility.MANAGER_AND_ABOVE, TeamMemberRole.MANAGER], () => true)
.with([DocumentVisibility.ADMIN, TeamMemberRole.ADMIN], () => true)
.otherwise(() => false);
}
if (!document || !document.documentData || (team && !canAccessDocument)) {
2025-01-02 15:33:37 +11:00
throw redirect(documentRootPath);
}
if (team && !canAccessDocument) {
2025-01-02 15:33:37 +11:00
throw redirect(documentRootPath);
}
2025-02-13 20:21:23 +11:00
// Todo: Get full document instead?
fix: show field on pending document (#1158) ## Description This pull request introduces the functionality to display pending fields on the document page view. This enhancement allows users to see which fields are pending and need to be completed. ![CleanShot 2024-05-14 at 23 31 29@2x](https://github.com/documenso/documenso/assets/55143799/ffea0b29-d251-4dd5-9742-5416ac8262ad) ## Changes Made - Added `getPendingFieldsForDocument` function in `packages/lib/server-only/field/get-pending-fields-for-document.ts` to fetch pending fields for a document. - Created a new component `DocumentPendingFields` in `document-pending-fields.tsx` to display the pending fields with options to hide individual fields. ## Testing Performed - Tested the new feature by creating documents with pending fields and verifying their display on the document page view. - Verified that the pending fields are correctly hidden when the "Hide field" button is clicked. - Ran unit tests for the new functionality and existing components to ensure no regressions. ## Checklist - [x] I have tested these changes locally and they work as expected. - [x] I have added/updated tests that prove the effectiveness of these changes. - [x] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## Additional Notes No additional notes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced logic for handling pending and completed document fields based on signing status. - **Refactor** - Replaced `getCompletedFieldsForDocument` with `getFieldsForDocument`. - Updated `DocumentReadOnlyFields` component to `DocumentPendingFields`. - **Bug Fixes** - Improved field retrieval accuracy and display based on recipient signing status. - **Style** - Enhanced UI elements with new icons and button adjustments for better user interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 06:08:06 +00:00
const [recipients, fields] = await Promise.all([
2024-04-18 21:56:31 +07:00
getRecipientsForDocument({
documentId,
teamId: team?.id,
userId: user.id,
}),
fix: show field on pending document (#1158) ## Description This pull request introduces the functionality to display pending fields on the document page view. This enhancement allows users to see which fields are pending and need to be completed. ![CleanShot 2024-05-14 at 23 31 29@2x](https://github.com/documenso/documenso/assets/55143799/ffea0b29-d251-4dd5-9742-5416ac8262ad) ## Changes Made - Added `getPendingFieldsForDocument` function in `packages/lib/server-only/field/get-pending-fields-for-document.ts` to fetch pending fields for a document. - Created a new component `DocumentPendingFields` in `document-pending-fields.tsx` to display the pending fields with options to hide individual fields. ## Testing Performed - Tested the new feature by creating documents with pending fields and verifying their display on the document page view. - Verified that the pending fields are correctly hidden when the "Hide field" button is clicked. - Ran unit tests for the new functionality and existing components to ensure no regressions. ## Checklist - [x] I have tested these changes locally and they work as expected. - [x] I have added/updated tests that prove the effectiveness of these changes. - [x] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## Additional Notes No additional notes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced logic for handling pending and completed document fields based on signing status. - **Refactor** - Replaced `getCompletedFieldsForDocument` with `getFieldsForDocument`. - Updated `DocumentReadOnlyFields` component to `DocumentPendingFields`. - **Bug Fixes** - Improved field retrieval accuracy and display based on recipient signing status. - **Style** - Enhanced UI elements with new icons and button adjustments for better user interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 06:08:06 +00:00
getFieldsForDocument({
2024-04-18 21:56:31 +07:00
documentId,
fix: show field on pending document (#1158) ## Description This pull request introduces the functionality to display pending fields on the document page view. This enhancement allows users to see which fields are pending and need to be completed. ![CleanShot 2024-05-14 at 23 31 29@2x](https://github.com/documenso/documenso/assets/55143799/ffea0b29-d251-4dd5-9742-5416ac8262ad) ## Changes Made - Added `getPendingFieldsForDocument` function in `packages/lib/server-only/field/get-pending-fields-for-document.ts` to fetch pending fields for a document. - Created a new component `DocumentPendingFields` in `document-pending-fields.tsx` to display the pending fields with options to hide individual fields. ## Testing Performed - Tested the new feature by creating documents with pending fields and verifying their display on the document page view. - Verified that the pending fields are correctly hidden when the "Hide field" button is clicked. - Ran unit tests for the new functionality and existing components to ensure no regressions. ## Checklist - [x] I have tested these changes locally and they work as expected. - [x] I have added/updated tests that prove the effectiveness of these changes. - [x] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## Additional Notes No additional notes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced logic for handling pending and completed document fields based on signing status. - **Refactor** - Replaced `getCompletedFieldsForDocument` with `getFieldsForDocument`. - Updated `DocumentReadOnlyFields` component to `DocumentPendingFields`. - **Bug Fixes** - Improved field retrieval accuracy and display based on recipient signing status. - **Style** - Enhanced UI elements with new icons and button adjustments for better user interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 06:08:06 +00:00
userId: user.id,
teamId: team?.id,
2024-04-18 21:56:31 +07:00
}),
]);
2024-02-12 17:30:23 +11:00
const documentWithRecipients = {
...document,
2025-01-13 13:41:53 +11:00
recipients,
2024-02-12 17:30:23 +11:00
};
2025-01-02 15:33:37 +11:00
return superLoaderJson({
document: documentWithRecipients,
documentRootPath,
fields,
});
}
export default function DocumentPage() {
const loaderData = useSuperLoaderData<typeof loader>();
const { _ } = useLingui();
const { user } = useSession();
const { document, documentRootPath, fields } = loaderData;
const { recipients, documentData, documentMeta } = document;
// This was a feature flag. Leave to false since it's not ready.
const isDocumentHistoryEnabled = false;
return (
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
2024-11-06 21:34:06 +09:00
{document.status === DocumentStatus.PENDING && (
<DocumentRecipientLinkCopyDialog recipients={recipients} />
)}
2025-01-02 15:33:37 +11:00
<Link to={documentRootPath} className="flex items-center text-[#7AC455] hover:opacity-80">
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
2024-08-27 20:34:39 +09:00
<Trans>Documents</Trans>
</Link>
<div className="flex flex-row justify-between truncate">
2024-02-15 18:20:10 +11:00
<div>
<h1
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
title={document.title}
>
2024-02-15 18:20:10 +11:00
{document.title}
</h1>
<div className="mt-2.5 flex items-center gap-x-6">
<DocumentStatusComponent
inheritColor
status={document.status}
className="text-muted-foreground"
/>
{recipients.length > 0 && (
<div className="text-muted-foreground flex items-center">
<Users2 className="mr-2 h-5 w-5" />
<StackAvatarsWithTooltip
recipients={recipients}
documentStatus={document.status}
position="bottom"
>
2024-08-27 20:34:39 +09:00
<span>
<Trans>{recipients.length} Recipient(s)</Trans>
</span>
2024-02-15 18:20:10 +11:00
</StackAvatarsWithTooltip>
</div>
)}
2024-08-27 20:34:39 +09:00
{document.deletedAt && (
<Badge variant="destructive">
<Trans>Document deleted</Trans>
</Badge>
)}
2024-02-15 18:20:10 +11:00
</div>
</div>
2024-02-19 14:31:26 +11:00
{isDocumentHistoryEnabled && (
<div className="self-end">
<DocumentHistorySheet documentId={document.id} userId={user.id}>
<Button variant="outline">
<Clock9 className="mr-1.5 h-4 w-4" />
2024-08-27 20:34:39 +09:00
<Trans>Document history</Trans>
2024-02-19 14:31:26 +11:00
</Button>
</DocumentHistorySheet>
</div>
)}
</div>
2024-02-12 17:30:23 +11:00
<div className="mt-6 grid w-full grid-cols-12 gap-8">
<Card
className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7"
gradient
>
<CardContent className="p-2">
2025-02-26 21:48:06 +11:00
<PDFViewer document={document} key={documentData.id} documentData={documentData} />
2024-02-12 17:30:23 +11:00
</CardContent>
</Card>
2024-04-18 21:56:31 +07:00
{document.status === DocumentStatus.PENDING && (
fix: show field on pending document (#1158) ## Description This pull request introduces the functionality to display pending fields on the document page view. This enhancement allows users to see which fields are pending and need to be completed. ![CleanShot 2024-05-14 at 23 31 29@2x](https://github.com/documenso/documenso/assets/55143799/ffea0b29-d251-4dd5-9742-5416ac8262ad) ## Changes Made - Added `getPendingFieldsForDocument` function in `packages/lib/server-only/field/get-pending-fields-for-document.ts` to fetch pending fields for a document. - Created a new component `DocumentPendingFields` in `document-pending-fields.tsx` to display the pending fields with options to hide individual fields. ## Testing Performed - Tested the new feature by creating documents with pending fields and verifying their display on the document page view. - Verified that the pending fields are correctly hidden when the "Hide field" button is clicked. - Ran unit tests for the new functionality and existing components to ensure no regressions. ## Checklist - [x] I have tested these changes locally and they work as expected. - [x] I have added/updated tests that prove the effectiveness of these changes. - [x] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## Additional Notes No additional notes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced logic for handling pending and completed document fields based on signing status. - **Refactor** - Replaced `getCompletedFieldsForDocument` with `getFieldsForDocument`. - Updated `DocumentReadOnlyFields` component to `DocumentPendingFields`. - **Bug Fixes** - Improved field retrieval accuracy and display based on recipient signing status. - **Style** - Enhanced UI elements with new icons and button adjustments for better user interaction. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: David Nguyen <davidngu28@gmail.com>
2024-06-24 06:08:06 +00:00
<DocumentReadOnlyFields fields={fields} documentMeta={documentMeta || undefined} />
2024-04-18 21:56:31 +07:00
)}
2024-02-12 17:30:23 +11:00
<div className="col-span-12 lg:col-span-6 xl:col-span-5">
2024-02-15 18:20:10 +11:00
<div className="space-y-6">
<section className="border-border bg-widget flex flex-col rounded-xl border pb-4 pt-6">
2024-02-12 17:30:23 +11:00
<div className="flex flex-row items-center justify-between px-4">
<h3 className="text-foreground text-2xl font-semibold">
2024-08-27 20:34:39 +09:00
{_(FRIENDLY_STATUS_MAP[document.status].labelExtended)}
2024-02-12 17:30:23 +11:00
</h3>
2025-01-02 15:33:37 +11:00
<DocumentPageViewDropdown document={document} />
2024-02-12 17:30:23 +11:00
</div>
<p className="text-muted-foreground mt-2 px-4 text-sm">
2024-02-12 17:30:23 +11:00
{match(document.status)
2024-08-27 20:34:39 +09:00
.with(DocumentStatus.COMPLETED, () => (
<Trans>This document has been signed by all recipients</Trans>
))
.with(DocumentStatus.DRAFT, () => (
<Trans>This document is currently a draft and has not been sent</Trans>
))
2024-02-12 17:30:23 +11:00
.with(DocumentStatus.PENDING, () => {
const pendingRecipients = recipients.filter(
(recipient) => recipient.signingStatus === 'NOT_SIGNED',
);
2024-08-27 20:34:39 +09:00
return (
<Plural
value={pendingRecipients.length}
one="Waiting on 1 recipient"
other="Waiting on # recipients"
/>
);
2024-02-12 17:30:23 +11:00
})
.exhaustive()}
</p>
<div className="mt-4 border-t px-4 pt-4">
2025-01-02 15:33:37 +11:00
<DocumentPageViewButton document={document} />
2024-02-12 17:30:23 +11:00
</div>
</section>
{/* Document information section. */}
2025-01-02 15:33:37 +11:00
<DocumentPageViewInformation document={document} userId={user.id} />
2024-02-12 17:30:23 +11:00
{/* Recipients section. */}
2025-01-02 15:33:37 +11:00
<DocumentPageViewRecipients document={document} documentRootPath={documentRootPath} />
2024-02-12 17:30:23 +11:00
2024-02-15 18:20:10 +11:00
{/* Recent activity section. */}
<DocumentPageViewRecentActivity documentId={document.id} userId={user.id} />
2024-02-12 17:30:23 +11:00
</div>
</div>
2024-02-12 17:30:23 +11:00
</div>
</div>
);
2025-01-02 15:33:37 +11:00
}