From d7d0fca501ac75b84bf847ed627be76e055dea63 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Fri, 31 Jan 2025 14:09:02 +1100 Subject: [PATCH] fix: wip --- apps/remix/app/_[id]/edit/index.tsx | 21 --- apps/remix/app/_[id]/index.tsx | 21 --- apps/remix/app/_[id]/logs/index.tsx | 21 --- .../document-search/document-search.tsx | 3 +- .../tables/current-user-teams-data-table.tsx | 4 +- .../tables/pending-user-teams-data-table.tsx | 5 +- .../tables/team-members-data-table.tsx | 4 +- .../dialogs/account-delete-dialog.tsx | 155 ++++++++++++++++ .../dialogs/document-duplicate-dialog.tsx | 6 +- .../dialogs/document-resend-dialog.tsx | 5 +- .../team-checkout-create-dialog.tsx} | 6 +- .../team-create-dialog.tsx} | 4 +- .../team-delete-dialog.tsx} | 4 +- .../team-email-add-dialog.tsx} | 4 +- .../team-email-delete-dialog.tsx} | 4 +- .../team-email-update-dialog.tsx} | 6 +- .../team-leave-dialog.tsx} | 6 +- .../team-member-delete-dialog.tsx} | 6 +- .../team-member-invite-dialog.tsx} | 6 +- .../team-member-update-dialog.tsx} | 6 +- .../team-transfer-dialog.tsx} | 6 +- .../forms/2fa/view-recovery-codes-dialog.tsx | 3 +- .../app/components/forms/avatar-image.tsx | 69 +++---- apps/remix/app/components/forms/password.tsx | 6 +- .../forms/public-profile-claim-dialog.tsx | 4 +- .../components/forms/public-profile-form.tsx | 6 +- apps/remix/app/components/forms/signin.tsx | 22 +-- apps/remix/app/components/forms/signup.tsx | 20 +-- .../leaderboard+/data-table-leaderboard.tsx | 169 ------------------ .../leaderboard+/fetch-leaderboard.actions.ts | 25 --- .../components/hidden/leaderboard+/page.tsx | 48 ----- .../document-audit-log-download-button.tsx} | 8 +- .../document-certificate-download-button.tsx} | 10 +- .../pages/document/document-edit-form.tsx} | 25 +-- .../document}/document-page-view-button.tsx | 18 +- .../document}/document-page-view-dropdown.tsx | 43 ++--- .../document-page-view-information.tsx | 0 .../document-page-view-recent-activity.tsx | 0 .../document-page-view-recipients.tsx | 4 + .../tables/document-logs-table.tsx} | 8 +- .../app/documents+/_documents-page-view.tsx | 159 ---------------- apps/remix/app/documents+/index.tsx | 16 -- apps/remix/app/root.tsx | 17 ++ .../routes/_authenticated+/admin+/stats.tsx | 7 +- .../documents+/$id._index.tsx} | 89 +++++---- .../_authenticated+/documents+/$id.edit.tsx} | 60 +++++-- .../_authenticated+/documents+/$id.logs.tsx} | 64 ++++--- .../_authenticated+/documents+/_index.tsx | 165 +++++++++++++++++ .../_authenticated+/documents+/index.tsx | 19 -- .../documents+/todo-loading.tsx} | 0 .../_authenticated+/settings+/profile.tsx | 9 +- .../settings+/teams+/index.tsx | 4 +- .../_authenticated+/t.$teamUrl+/_layout.tsx | 65 +++++++ .../t.$teamUrl+/documents.$id._index.tsx | 5 + .../t.$teamUrl+/documents.$id.edit.tsx | 5 + .../t.$teamUrl+/documents.$id.logs.tsx | 5 + .../t.$teamUrl+/documents._index.tsx | 5 + .../t.$teamUrl+/settings._index.tsx | 5 + .../app/routes/_unauthenticated+/signin.tsx | 9 +- .../app/routes/_unauthenticated+/signup.tsx | 9 +- apps/remix/package.json | 5 +- apps/remix/public/pdf.worker.min.js | 22 +++ apps/remix/vite-env.d.ts | 10 ++ apps/remix/vite.config.ts | 12 +- .../forms/2fa/view-recovery-codes-dialog.tsx | 3 +- apps/web/src/components/forms/v2/signup.tsx | 4 +- packages/auth/client/index.ts | 60 ++++--- packages/auth/server/lib/utils/debugger.ts | 5 +- packages/auth/server/routes/email-password.ts | 103 ++++++++--- packages/auth/server/routes/google.ts | 10 +- packages/auth/server/types/email-password.ts | 15 +- .../ee/server-only/stripe/webhook/handler.ts | 3 +- packages/email/mailer.ts | 39 ++-- .../template-document-self-signed.tsx | 3 +- .../template-reset-password.tsx | 3 +- packages/email/transports/mailchannels.ts | 10 +- .../lib/client-only/providers/i18n-server.tsx | 3 +- packages/lib/constants/app.ts | 10 +- packages/lib/constants/auth.ts | 15 +- packages/lib/constants/email.ts | 6 +- packages/lib/constants/feature-flags.ts | 2 +- packages/lib/errors/app-error.ts | 4 - packages/lib/jobs/client/client.ts | 3 +- packages/lib/jobs/client/inngest.ts | 5 +- packages/lib/jobs/client/trigger.ts | 5 +- packages/lib/next-auth/auth-options.ts | 2 +- packages/lib/next-auth/error-codes.ts | 48 ++--- packages/lib/server-only/2fa/setup-2fa.ts | 3 +- packages/lib/server-only/2fa/validate-2fa.ts | 8 +- .../auth/send-confirmation-email.ts | 5 +- .../server-only/auth/send-forgot-password.ts | 5 +- .../server-only/auth/send-reset-password.ts | 5 +- .../document/send-completed-email.ts | 9 +- .../server-only/document/send-delete-email.ts | 5 +- .../document/send-pending-email.ts | 5 +- .../htmltopdf/get-certificate-pdf.ts | 5 +- .../server-only/pdf/insert-field-in-pdf.ts | 9 +- packages/lib/server-only/redis/index.ts | 6 +- packages/lib/server-only/stripe/index.ts | 4 +- .../team/create-team-email-verification.ts | 3 +- .../lib/server-only/team/delete-team-email.ts | 3 +- .../create-document-from-direct-template.ts | 5 +- packages/lib/server-only/user/create-user.ts | 35 ++-- packages/lib/server-only/user/verify-email.ts | 20 ++- packages/lib/tsconfig.json | 2 +- packages/lib/universal/get-base-url.ts | 7 +- packages/lib/universal/upload/put-file.ts | 3 +- .../lib/universal/upload/server-actions.ts | 52 +++--- packages/lib/utils/env.ts | 18 ++ packages/lib/utils/i18n.ts | 3 +- packages/lib/utils/logger.ts | 8 +- packages/prisma/schema.prisma | 35 ++-- packages/signing/index.ts | 4 +- .../signing/transports/google-cloud-hsm.ts | 25 ++- packages/signing/transports/local-cert.ts | 13 +- packages/trpc/react/index.tsx | 8 +- packages/trpc/server/auth-router/router.ts | 35 ---- packages/trpc/server/profile-router/router.ts | 58 +----- packages/trpc/server/profile-router/schema.ts | 28 --- .../components/document/document-dialog.tsx | 3 +- .../document/document-download-button.tsx | 2 +- .../ui/components/field/field-tooltip.tsx | 2 +- packages/ui/components/field/field.tsx | 2 +- .../recipient/recipient-role-select.tsx | 2 +- packages/ui/components/signing-card.tsx | 3 +- .../primitives/document-flow/add-fields.tsx | 26 ++- .../document-flow/add-fields.types.ts | 2 +- .../primitives/document-flow/add-settings.tsx | 4 +- .../document-flow/add-settings.types.ts | 2 +- .../primitives/document-flow/add-signers.tsx | 4 +- .../document-flow/add-signers.types.ts | 2 +- .../primitives/document-flow/add-subject.tsx | 8 +- .../document-flow/add-subject.types.ts | 3 +- .../primitives/document-flow/field-icon.tsx | 2 +- .../field-item-advanced-settings.tsx | 2 +- .../primitives/document-flow/field-item.tsx | 20 +-- .../document-flow/show-field-item.tsx | 20 +-- packages/ui/primitives/document-flow/types.ts | 2 +- packages/ui/primitives/pdf-viewer.tsx | 2 +- .../ui/primitives/recipient-role-icons.tsx | 3 +- .../template-flow/add-template-fields.tsx | 24 ++- .../add-template-fields.types.ts | 2 +- .../add-template-placeholder-recipients.tsx | 9 +- ...d-template-placeholder-recipients.types.ts | 2 +- .../template-flow/add-template-settings.tsx | 4 +- .../add-template-settings.types.tsx | 4 +- 146 files changed, 1250 insertions(+), 1263 deletions(-) delete mode 100644 apps/remix/app/_[id]/edit/index.tsx delete mode 100644 apps/remix/app/_[id]/index.tsx delete mode 100644 apps/remix/app/_[id]/logs/index.tsx create mode 100644 apps/remix/app/components/dialogs/account-delete-dialog.tsx rename apps/remix/app/components/{(teams)/dialogs/create-team-checkout-dialog.tsx => dialogs/team-checkout-create-dialog.tsx} (97%) rename apps/remix/app/components/{(teams)/dialogs/create-team-dialog.tsx => dialogs/team-create-dialog.tsx} (98%) rename apps/remix/app/components/{(teams)/dialogs/delete-team-dialog.tsx => dialogs/team-delete-dialog.tsx} (97%) rename apps/remix/app/components/{(teams)/dialogs/add-team-email-dialog.tsx => dialogs/team-email-add-dialog.tsx} (97%) rename apps/remix/app/components/{(teams)/dialogs/remove-team-email-dialog.tsx => dialogs/team-email-delete-dialog.tsx} (97%) rename apps/remix/app/components/{(teams)/dialogs/update-team-email-dialog.tsx => dialogs/team-email-update-dialog.tsx} (97%) rename apps/remix/app/components/{(teams)/dialogs/leave-team-dialog.tsx => dialogs/team-leave-dialog.tsx} (96%) rename apps/remix/app/components/{(teams)/dialogs/delete-team-member-dialog.tsx => dialogs/team-member-delete-dialog.tsx} (96%) rename apps/remix/app/components/{(teams)/dialogs/invite-team-member-dialog.tsx => dialogs/team-member-invite-dialog.tsx} (99%) rename apps/remix/app/components/{(teams)/dialogs/update-team-member-dialog.tsx => dialogs/team-member-update-dialog.tsx} (97%) rename apps/remix/app/components/{(teams)/dialogs/transfer-team-dialog.tsx => dialogs/team-transfer-dialog.tsx} (98%) delete mode 100644 apps/remix/app/components/hidden/leaderboard+/data-table-leaderboard.tsx delete mode 100644 apps/remix/app/components/hidden/leaderboard+/fetch-leaderboard.actions.ts delete mode 100644 apps/remix/app/components/hidden/leaderboard+/page.tsx rename apps/remix/app/{_[id]/logs/download-audit-log-button.tsx => components/pages/document/document-audit-log-download-button.tsx} (92%) rename apps/remix/app/{_[id]/logs/download-certificate-button.tsx => components/pages/document/document-certificate-download-button.tsx} (92%) rename apps/remix/app/{_[id]/edit-document.tsx => components/pages/document/document-edit-form.tsx} (95%) rename apps/remix/app/{_[id] => components/pages/document}/document-page-view-button.tsx (91%) rename apps/remix/app/{_[id] => components/pages/document}/document-page-view-dropdown.tsx (84%) rename apps/remix/app/{_[id] => components/pages/document}/document-page-view-information.tsx (100%) rename apps/remix/app/{_[id] => components/pages/document}/document-page-view-recent-activity.tsx (100%) rename apps/remix/app/{_[id] => components/pages/document}/document-page-view-recipients.tsx (99%) rename apps/remix/app/{_[id]/logs/document-logs-data-table.tsx => components/tables/document-logs-table.tsx} (96%) delete mode 100644 apps/remix/app/documents+/_documents-page-view.tsx delete mode 100644 apps/remix/app/documents+/index.tsx rename apps/remix/app/{_[id]/document-page-view.tsx => routes/_authenticated+/documents+/$id._index.tsx} (78%) rename apps/remix/app/{_[id]/edit/document-edit-page-view.tsx => routes/_authenticated+/documents+/$id.edit.tsx} (75%) rename apps/remix/app/{_[id]/logs/document-logs-page-view.tsx => routes/_authenticated+/documents+/$id.logs.tsx} (74%) create mode 100644 apps/remix/app/routes/_authenticated+/documents+/_index.tsx delete mode 100644 apps/remix/app/routes/_authenticated+/documents+/index.tsx rename apps/remix/app/{_[id]/loading.tsx => routes/_authenticated+/documents+/todo-loading.tsx} (100%) create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.logs.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents._index.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._index.tsx create mode 100644 apps/remix/public/pdf.worker.min.js create mode 100644 apps/remix/vite-env.d.ts create mode 100644 packages/lib/utils/env.ts diff --git a/apps/remix/app/_[id]/edit/index.tsx b/apps/remix/app/_[id]/edit/index.tsx deleted file mode 100644 index 38531fbb9..000000000 --- a/apps/remix/app/_[id]/edit/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { redirect, useParams } from 'react-router'; - -import { formatDocumentsPath } from '@documenso/lib/utils/teams'; - -import { useOptionalCurrentTeam } from '~/providers/team'; - -import { DocumentEditPageView } from './document-edit-page-view'; - -export default function DocumentEditPage() { - const { id: documentId } = useParams(); - - const team = useOptionalCurrentTeam(); - - const documentRootPath = formatDocumentsPath(team?.url); - - if (!documentId || Number.isNaN(documentId)) { - redirect(documentRootPath); - } - - return ; -} diff --git a/apps/remix/app/_[id]/index.tsx b/apps/remix/app/_[id]/index.tsx deleted file mode 100644 index ac60a5d91..000000000 --- a/apps/remix/app/_[id]/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { redirect, useParams } from 'react-router'; - -import { formatDocumentsPath } from '@documenso/lib/utils/teams'; - -import { useOptionalCurrentTeam } from '~/providers/team'; - -import { DocumentPageView } from './document-page-view'; - -export default function DocumentPage() { - const { id: documentId } = useParams(); - - const team = useOptionalCurrentTeam(); - - const documentRootPath = formatDocumentsPath(team?.url); - - if (!documentId || Number.isNaN(documentId)) { - redirect(documentRootPath); - } - - return ; -} diff --git a/apps/remix/app/_[id]/logs/index.tsx b/apps/remix/app/_[id]/logs/index.tsx deleted file mode 100644 index bf26f35f5..000000000 --- a/apps/remix/app/_[id]/logs/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { redirect, useParams } from 'react-router'; - -import { formatDocumentsPath } from '@documenso/lib/utils/teams'; - -import { useOptionalCurrentTeam } from '~/providers/team'; - -import { DocumentLogsPageView } from './document-logs-page-view'; - -export default function DocumentsLogsPage() { - const { id: documentId } = useParams(); - - const team = useOptionalCurrentTeam(); - - const documentRootPath = formatDocumentsPath(team?.url); - - if (!documentId || Number.isNaN(documentId)) { - redirect(documentRootPath); - } - - return ; -} diff --git a/apps/remix/app/components/(dashboard)/document-search/document-search.tsx b/apps/remix/app/components/(dashboard)/document-search/document-search.tsx index 4d1344c75..b086d87a7 100644 --- a/apps/remix/app/components/(dashboard)/document-search/document-search.tsx +++ b/apps/remix/app/components/(dashboard)/document-search/document-search.tsx @@ -25,8 +25,7 @@ export const DocumentSearch = ({ initialValue = '' }: { initialValue?: string }) params.delete('search'); } - // Todo: Test - void navigate(`/documents?${params.toString()}`); + void navigate(`?${params.toString()}`); }, [searchParams], ); diff --git a/apps/remix/app/components/(teams)/tables/current-user-teams-data-table.tsx b/apps/remix/app/components/(teams)/tables/current-user-teams-data-table.tsx index 643883939..cd4c16ffb 100644 --- a/apps/remix/app/components/(teams)/tables/current-user-teams-data-table.tsx +++ b/apps/remix/app/components/(teams)/tables/current-user-teams-data-table.tsx @@ -20,7 +20,7 @@ import { DataTablePagination } from '@documenso/ui/primitives/data-table-paginat import { Skeleton } from '@documenso/ui/primitives/skeleton'; import { TableCell } from '@documenso/ui/primitives/table'; -import { LeaveTeamDialog } from '../dialogs/leave-team-dialog'; +import { TeamLeaveDialog } from '~/components/dialogs/team-leave-dialog'; export const CurrentUserTeamsDataTable = () => { const { _, i18n } = useLingui(); @@ -99,7 +99,7 @@ export const CurrentUserTeamsDataTable = () => { )} - { @@ -139,7 +140,7 @@ export const PendingUserTeamsDataTable = () => { {(table) => } - setCheckoutPendingTeamId(null)} /> diff --git a/apps/remix/app/components/(teams)/tables/team-members-data-table.tsx b/apps/remix/app/components/(teams)/tables/team-members-data-table.tsx index 1af4caca0..3b31d0a2c 100644 --- a/apps/remix/app/components/(teams)/tables/team-members-data-table.tsx +++ b/apps/remix/app/components/(teams)/tables/team-members-data-table.tsx @@ -26,8 +26,8 @@ import { import { Skeleton } from '@documenso/ui/primitives/skeleton'; import { TableCell } from '@documenso/ui/primitives/table'; -import { DeleteTeamMemberDialog } from '../dialogs/delete-team-member-dialog'; -import { UpdateTeamMemberDialog } from '../dialogs/update-team-member-dialog'; +import { DeleteTeamMemberDialog } from '../../dialogs/team-member-delete-dialog'; +import { UpdateTeamMemberDialog } from '../../dialogs/team-member-update-dialog'; export type TeamMembersDataTableProps = { currentUserTeamRole: TeamMemberRole; diff --git a/apps/remix/app/components/dialogs/account-delete-dialog.tsx b/apps/remix/app/components/dialogs/account-delete-dialog.tsx new file mode 100644 index 000000000..5295af56c --- /dev/null +++ b/apps/remix/app/components/dialogs/account-delete-dialog.tsx @@ -0,0 +1,155 @@ +import { useState } from 'react'; + +import { Trans, msg } from '@lingui/macro'; +import { useLingui } from '@lingui/react'; + +import { authClient } from '@documenso/auth/client'; +import { trpc } from '@documenso/trpc/react'; +import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert'; +import { Button } from '@documenso/ui/primitives/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@documenso/ui/primitives/dialog'; +import { Input } from '@documenso/ui/primitives/input'; +import { Label } from '@documenso/ui/primitives/label'; +import { useToast } from '@documenso/ui/primitives/use-toast'; + +import { useAuth } from '~/providers/auth'; + +export type AccountDeleteDialogProps = { + className?: string; +}; + +export const AccountDeleteDialog = ({ className }: AccountDeleteDialogProps) => { + const { user } = useAuth(); + + const { _ } = useLingui(); + const { toast } = useToast(); + + const hasTwoFactorAuthentication = user.twoFactorEnabled; + + const [enteredEmail, setEnteredEmail] = useState(''); + + const { mutateAsync: deleteAccount, isPending: isDeletingAccount } = + trpc.profile.deleteAccount.useMutation(); + + const onDeleteAccount = async () => { + try { + await deleteAccount(); + + toast({ + title: _(msg`Account deleted`), + description: _(msg`Your account has been deleted successfully.`), + duration: 5000, + }); + + return await authClient.signOut(); + } catch (err) { + toast({ + title: _(msg`An unknown error occurred`), + variant: 'destructive', + description: _( + msg`We encountered an unknown error while attempting to delete your account. Please try again later.`, + ), + }); + } + }; + + return ( +
+ +
+ + Delete Account + + + + Delete your account and all its contents, including completed documents. This action + is irreversible and will cancel your subscription, so proceed with caution. + + +
+ +
+ setEnteredEmail('')}> + + + + + + + + Delete Account + + + + + This action is not reversible. Please be certain. + + + + {hasTwoFactorAuthentication && ( + + + Disable Two Factor Authentication before deleting your account. + + + )} + + + + Documenso will delete{' '} + all of your documents, along with all of + your completed documents, signatures, and all other resources belonging to your + Account. + + + + + {!hasTwoFactorAuthentication && ( +
+ + + setEnteredEmail(e.target.value)} + /> +
+ )} + + + +
+
+
+
+
+ ); +}; diff --git a/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx b/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx index d52959957..1530f1fd0 100644 --- a/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx +++ b/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx @@ -16,24 +16,26 @@ import { import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer'; import { useToast } from '@documenso/ui/primitives/use-toast'; +import { useOptionalCurrentTeam } from '~/providers/team'; + type DocumentDuplicateDialogProps = { id: number; open: boolean; onOpenChange: (_open: boolean) => void; - team?: Pick; }; export const DocumentDuplicateDialog = ({ id, open, onOpenChange, - team, }: DocumentDuplicateDialogProps) => { const navigate = useNavigate(); const { toast } = useToast(); const { _ } = useLingui(); + const team = useOptionalCurrentTeam(); + const { data: document, isLoading } = trpcReact.document.getDocumentById.useQuery({ documentId: id, }); diff --git a/apps/remix/app/components/dialogs/document-resend-dialog.tsx b/apps/remix/app/components/dialogs/document-resend-dialog.tsx index dacb52f2e..bb6b93bbe 100644 --- a/apps/remix/app/components/dialogs/document-resend-dialog.tsx +++ b/apps/remix/app/components/dialogs/document-resend-dialog.tsx @@ -36,6 +36,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast'; import { StackAvatar } from '~/components/(dashboard)/avatar/stack-avatar'; import { useAuth } from '~/providers/auth'; +import { useOptionalCurrentTeam } from '~/providers/team'; const FORM_ID = 'resend-email'; @@ -44,7 +45,6 @@ export type DocumentResendDialogProps = { team: Pick | null; }; recipients: Recipient[]; - team?: Pick; }; export const ZResendDocumentFormSchema = z.object({ @@ -55,8 +55,9 @@ export const ZResendDocumentFormSchema = z.object({ export type TResendDocumentFormSchema = z.infer; -export const DocumentResendDialog = ({ document, recipients, team }: DocumentResendDialogProps) => { +export const DocumentResendDialog = ({ document, recipients }: DocumentResendDialogProps) => { const { user } = useAuth(); + const team = useOptionalCurrentTeam(); const { toast } = useToast(); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/create-team-checkout-dialog.tsx b/apps/remix/app/components/dialogs/team-checkout-create-dialog.tsx similarity index 97% rename from apps/remix/app/components/(teams)/dialogs/create-team-checkout-dialog.tsx rename to apps/remix/app/components/dialogs/team-checkout-create-dialog.tsx index 9a66c7073..fb49cca7b 100644 --- a/apps/remix/app/components/(teams)/dialogs/create-team-checkout-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-checkout-create-dialog.tsx @@ -20,18 +20,18 @@ import { import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type CreateTeamCheckoutDialogProps = { +export type TeamCheckoutCreateDialogProps = { pendingTeamId: number | null; onClose: () => void; } & Omit; const MotionCard = motion(Card); -export const CreateTeamCheckoutDialog = ({ +export const TeamCheckoutCreateDialog = ({ pendingTeamId, onClose, ...props -}: CreateTeamCheckoutDialogProps) => { +}: TeamCheckoutCreateDialogProps) => { const { _ } = useLingui(); const { toast } = useToast(); diff --git a/apps/remix/app/components/(teams)/dialogs/create-team-dialog.tsx b/apps/remix/app/components/dialogs/team-create-dialog.tsx similarity index 98% rename from apps/remix/app/components/(teams)/dialogs/create-team-dialog.tsx rename to apps/remix/app/components/dialogs/team-create-dialog.tsx index 1898c583a..1f9b28845 100644 --- a/apps/remix/app/components/(teams)/dialogs/create-team-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-create-dialog.tsx @@ -35,7 +35,7 @@ import { import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type CreateTeamDialogProps = { +export type TeamCreateDialogProps = { trigger?: React.ReactNode; } & Omit; @@ -46,7 +46,7 @@ const ZCreateTeamFormSchema = ZCreateTeamMutationSchema.pick({ type TCreateTeamFormSchema = z.infer; -export const CreateTeamDialog = ({ trigger, ...props }: CreateTeamDialogProps) => { +export const TeamCreateDialog = ({ trigger, ...props }: TeamCreateDialogProps) => { const { _ } = useLingui(); const { toast } = useToast(); diff --git a/apps/remix/app/components/(teams)/dialogs/delete-team-dialog.tsx b/apps/remix/app/components/dialogs/team-delete-dialog.tsx similarity index 97% rename from apps/remix/app/components/(teams)/dialogs/delete-team-dialog.tsx rename to apps/remix/app/components/dialogs/team-delete-dialog.tsx index 24dcd2de6..c0c665823 100644 --- a/apps/remix/app/components/(teams)/dialogs/delete-team-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-delete-dialog.tsx @@ -30,13 +30,13 @@ import { Input } from '@documenso/ui/primitives/input'; import type { Toast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type DeleteTeamDialogProps = { +export type TeamDeleteDialogProps = { teamId: number; teamName: string; trigger?: React.ReactNode; }; -export const DeleteTeamDialog = ({ trigger, teamId, teamName }: DeleteTeamDialogProps) => { +export const TeamDeleteDialog = ({ trigger, teamId, teamName }: TeamDeleteDialogProps) => { const navigate = useNavigate(); const [open, setOpen] = useState(false); diff --git a/apps/remix/app/components/(teams)/dialogs/add-team-email-dialog.tsx b/apps/remix/app/components/dialogs/team-email-add-dialog.tsx similarity index 97% rename from apps/remix/app/components/(teams)/dialogs/add-team-email-dialog.tsx rename to apps/remix/app/components/dialogs/team-email-add-dialog.tsx index 43033732c..ae80cff51 100644 --- a/apps/remix/app/components/(teams)/dialogs/add-team-email-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-email-add-dialog.tsx @@ -32,7 +32,7 @@ import { import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type AddTeamEmailDialogProps = { +export type TeamEmailAddDialogProps = { teamId: number; trigger?: React.ReactNode; } & Omit; @@ -44,7 +44,7 @@ const ZCreateTeamEmailFormSchema = ZCreateTeamEmailVerificationMutationSchema.pi type TCreateTeamEmailFormSchema = z.infer; -export const AddTeamEmailDialog = ({ teamId, trigger, ...props }: AddTeamEmailDialogProps) => { +export const TeamEmailAddDialog = ({ teamId, trigger, ...props }: TeamEmailAddDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/remove-team-email-dialog.tsx b/apps/remix/app/components/dialogs/team-email-delete-dialog.tsx similarity index 97% rename from apps/remix/app/components/(teams)/dialogs/remove-team-email-dialog.tsx rename to apps/remix/app/components/dialogs/team-email-delete-dialog.tsx index 86ef5ce2b..21f0c0503 100644 --- a/apps/remix/app/components/(teams)/dialogs/remove-team-email-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-email-delete-dialog.tsx @@ -21,7 +21,7 @@ import { } from '@documenso/ui/primitives/dialog'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type RemoveTeamEmailDialogProps = { +export type TeamEmailDeleteDialogProps = { trigger?: React.ReactNode; teamName: string; team: Prisma.TeamGetPayload<{ @@ -38,7 +38,7 @@ export type RemoveTeamEmailDialogProps = { }>; }; -export const RemoveTeamEmailDialog = ({ trigger, teamName, team }: RemoveTeamEmailDialogProps) => { +export const TeamEmailDeleteDialog = ({ trigger, teamName, team }: TeamEmailDeleteDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/update-team-email-dialog.tsx b/apps/remix/app/components/dialogs/team-email-update-dialog.tsx similarity index 97% rename from apps/remix/app/components/(teams)/dialogs/update-team-email-dialog.tsx rename to apps/remix/app/components/dialogs/team-email-update-dialog.tsx index 31fd2082b..ccab54733 100644 --- a/apps/remix/app/components/(teams)/dialogs/update-team-email-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-email-update-dialog.tsx @@ -30,7 +30,7 @@ import { import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type UpdateTeamEmailDialogProps = { +export type TeamEmailUpdateDialogProps = { teamEmail: TeamEmail; trigger?: React.ReactNode; } & Omit; @@ -41,11 +41,11 @@ const ZUpdateTeamEmailFormSchema = z.object({ type TUpdateTeamEmailFormSchema = z.infer; -export const UpdateTeamEmailDialog = ({ +export const TeamEmailUpdateDialog = ({ teamEmail, trigger, ...props -}: UpdateTeamEmailDialogProps) => { +}: TeamEmailUpdateDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/leave-team-dialog.tsx b/apps/remix/app/components/dialogs/team-leave-dialog.tsx similarity index 96% rename from apps/remix/app/components/(teams)/dialogs/leave-team-dialog.tsx rename to apps/remix/app/components/dialogs/team-leave-dialog.tsx index d5c07863b..883d42a6e 100644 --- a/apps/remix/app/components/(teams)/dialogs/leave-team-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-leave-dialog.tsx @@ -21,7 +21,7 @@ import { } from '@documenso/ui/primitives/dialog'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type LeaveTeamDialogProps = { +export type TeamLeaveDialogProps = { teamId: number; teamName: string; teamAvatarImageId?: string | null; @@ -29,13 +29,13 @@ export type LeaveTeamDialogProps = { trigger?: React.ReactNode; }; -export const LeaveTeamDialog = ({ +export const TeamLeaveDialog = ({ trigger, teamId, teamName, teamAvatarImageId, role, -}: LeaveTeamDialogProps) => { +}: TeamLeaveDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/delete-team-member-dialog.tsx b/apps/remix/app/components/dialogs/team-member-delete-dialog.tsx similarity index 96% rename from apps/remix/app/components/(teams)/dialogs/delete-team-member-dialog.tsx rename to apps/remix/app/components/dialogs/team-member-delete-dialog.tsx index d375c520d..3493cfa77 100644 --- a/apps/remix/app/components/(teams)/dialogs/delete-team-member-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-member-delete-dialog.tsx @@ -18,7 +18,7 @@ import { } from '@documenso/ui/primitives/dialog'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type DeleteTeamMemberDialogProps = { +export type TeamMemberDeleteDialogProps = { teamId: number; teamName: string; teamMemberId: number; @@ -27,14 +27,14 @@ export type DeleteTeamMemberDialogProps = { trigger?: React.ReactNode; }; -export const DeleteTeamMemberDialog = ({ +export const TeamMemberDeleteDialog = ({ trigger, teamId, teamName, teamMemberId, teamMemberName, teamMemberEmail, -}: DeleteTeamMemberDialogProps) => { +}: TeamMemberDeleteDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/invite-team-member-dialog.tsx b/apps/remix/app/components/dialogs/team-member-invite-dialog.tsx similarity index 99% rename from apps/remix/app/components/(teams)/dialogs/invite-team-member-dialog.tsx rename to apps/remix/app/components/dialogs/team-member-invite-dialog.tsx index dbd2ed9d2..91b74bf2c 100644 --- a/apps/remix/app/components/(teams)/dialogs/invite-team-member-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-member-invite-dialog.tsx @@ -45,7 +45,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type InviteTeamMembersDialogProps = { +export type TeamMemberInviteDialogProps = { currentUserTeamRole: TeamMemberRole; teamId: number; trigger?: React.ReactNode; @@ -94,12 +94,12 @@ const ZImportTeamMemberSchema = z.array( }), ); -export const InviteTeamMembersDialog = ({ +export const TeamMemberInviteDialog = ({ currentUserTeamRole, teamId, trigger, ...props -}: InviteTeamMembersDialogProps) => { +}: TeamMemberInviteDialogProps) => { const [open, setOpen] = useState(false); const fileInputRef = useRef(null); const [invitationType, setInvitationType] = useState('INDIVIDUAL'); diff --git a/apps/remix/app/components/(teams)/dialogs/update-team-member-dialog.tsx b/apps/remix/app/components/dialogs/team-member-update-dialog.tsx similarity index 97% rename from apps/remix/app/components/(teams)/dialogs/update-team-member-dialog.tsx rename to apps/remix/app/components/dialogs/team-member-update-dialog.tsx index 5f46cccb8..d8cd4c703 100644 --- a/apps/remix/app/components/(teams)/dialogs/update-team-member-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-member-update-dialog.tsx @@ -38,7 +38,7 @@ import { } from '@documenso/ui/primitives/select'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type UpdateTeamMemberDialogProps = { +export type TeamMemberUpdateDialogProps = { currentUserTeamRole: TeamMemberRole; trigger?: React.ReactNode; teamId: number; @@ -53,7 +53,7 @@ const ZUpdateTeamMemberFormSchema = z.object({ type ZUpdateTeamMemberSchema = z.infer; -export const UpdateTeamMemberDialog = ({ +export const TeamMemberUpdateDialog = ({ currentUserTeamRole, trigger, teamId, @@ -61,7 +61,7 @@ export const UpdateTeamMemberDialog = ({ teamMemberName, teamMemberRole, ...props -}: UpdateTeamMemberDialogProps) => { +}: TeamMemberUpdateDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/(teams)/dialogs/transfer-team-dialog.tsx b/apps/remix/app/components/dialogs/team-transfer-dialog.tsx similarity index 98% rename from apps/remix/app/components/(teams)/dialogs/transfer-team-dialog.tsx rename to apps/remix/app/components/dialogs/team-transfer-dialog.tsx index c3a3ef3d2..b990829b8 100644 --- a/apps/remix/app/components/(teams)/dialogs/transfer-team-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-transfer-dialog.tsx @@ -38,19 +38,19 @@ import { } from '@documenso/ui/primitives/select'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type TransferTeamDialogProps = { +export type TeamTransferDialogProps = { teamId: number; teamName: string; ownerUserId: number; trigger?: React.ReactNode; }; -export const TransferTeamDialog = ({ +export const TeamTransferDialog = ({ trigger, teamId, teamName, ownerUserId, -}: TransferTeamDialogProps) => { +}: TeamTransferDialogProps) => { const [open, setOpen] = useState(false); const { _ } = useLingui(); diff --git a/apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx b/apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx index 8f933951b..fe62c2265 100644 --- a/apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx +++ b/apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx @@ -8,7 +8,6 @@ import { z } from 'zod'; import { downloadFile } from '@documenso/lib/client-only/download-file'; import { AppError } from '@documenso/lib/errors/app-error'; -import { ErrorCode } from '@documenso/lib/next-auth/error-codes'; import { trpc } from '@documenso/trpc/react'; import { Alert, AlertDescription } from '@documenso/ui/primitives/alert'; import { Button } from '@documenso/ui/primitives/button'; @@ -145,7 +144,7 @@ export const ViewRecoveryCodesDialog = () => { {match(AppError.parseError(error).message) - .with(ErrorCode.INCORRECT_TWO_FACTOR_CODE, () => ( + .with('INCORRECT_TWO_FACTOR_CODE', () => ( Invalid code. Please try again. )) .otherwise(() => ( diff --git a/apps/remix/app/components/forms/avatar-image.tsx b/apps/remix/app/components/forms/avatar-image.tsx index e7a9180ae..556ae7042 100644 --- a/apps/remix/app/components/forms/avatar-image.tsx +++ b/apps/remix/app/components/forms/avatar-image.tsx @@ -3,14 +3,14 @@ import { useMemo } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; -// Todo -// import { ErrorCode, useDropzone } from 'react-dropzone'; +import { ErrorCode, useDropzone } from 'react-dropzone'; import { useForm } from 'react-hook-form'; import { match } from 'ts-pattern'; import { z } from 'zod'; +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { AppError } from '@documenso/lib/errors/app-error'; -import { formatAvatarUrl } from '@documenso/lib/utils/avatars'; +import { base64 } from '@documenso/lib/universal/base64'; import { extractInitials } from '@documenso/lib/utils/recipient-formatter'; import { trpc } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; @@ -40,9 +40,9 @@ export type AvatarImageFormProps = { }; export const AvatarImageForm = ({ className }: AvatarImageFormProps) => { + const { user } = useAuth(); const { _ } = useLingui(); const { toast } = useToast(); - const { user } = useAuth(); const team = useOptionalCurrentTeam(); @@ -67,31 +67,31 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => { resolver: zodResolver(ZAvatarImageFormSchema), }); - // const { getRootProps, getInputProps } = useDropzone({ - // maxSize: 1024 * 1024, - // accept: { - // 'image/*': ['.png', '.jpg', '.jpeg'], - // }, - // multiple: false, - // onDropAccepted: ([file]) => { - // void file.arrayBuffer().then((buffer) => { - // const contents = base64.encode(new Uint8Array(buffer)); + const { getRootProps, getInputProps } = useDropzone({ + maxSize: 1024 * 1024, + accept: { + 'image/*': ['.png', '.jpg', '.jpeg'], + }, + multiple: false, + onDropAccepted: ([file]) => { + void file.arrayBuffer().then((buffer) => { + const contents = base64.encode(new Uint8Array(buffer)); - // form.setValue('bytes', contents); - // void form.handleSubmit(onFormSubmit)(); - // }); - // }, - // onDropRejected: ([file]) => { - // form.setError('bytes', { - // type: 'onChange', - // message: match(file.errors[0].code) - // .with(ErrorCode.FileTooLarge, () => _(msg`Uploaded file is too large`)) - // .with(ErrorCode.FileTooSmall, () => _(msg`Uploaded file is too small`)) - // .with(ErrorCode.FileInvalidType, () => _(msg`Uploaded file not an allowed file type`)) - // .otherwise(() => _(msg`An unknown error occurred`)), - // }); - // }, - // }); + form.setValue('bytes', contents); + void form.handleSubmit(onFormSubmit)(); + }); + }, + onDropRejected: ([file]) => { + form.setError('bytes', { + type: 'onChange', + message: match(file.errors[0].code) + .with(ErrorCode.FileTooLarge, () => _(msg`Uploaded file is too large`)) + .with(ErrorCode.FileTooSmall, () => _(msg`Uploaded file is too small`)) + .with(ErrorCode.FileInvalidType, () => _(msg`Uploaded file not an allowed file type`)) + .otherwise(() => _(msg`An unknown error occurred`)), + }); + }, + }); const onFormSubmit = async (data: TAvatarImageFormSchema) => { try { @@ -106,7 +106,8 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => { duration: 5000, }); - // router.refresh(); // Todo + // Todo + // router.refresh(); } catch (err) { const error = AppError.parseError(err); @@ -143,7 +144,11 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => {
- {avatarImageId && } + {avatarImageId && ( + + )} {initials} @@ -165,12 +170,12 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => { type="button" variant="secondary" size="sm" - // {...getRootProps()} + {...getRootProps()} loading={form.formState.isSubmitting} disabled={form.formState.isSubmitting} > Upload Avatar - {/* */} +
diff --git a/apps/remix/app/components/forms/password.tsx b/apps/remix/app/components/forms/password.tsx index eb02e2a20..86dc3cd4b 100644 --- a/apps/remix/app/components/forms/password.tsx +++ b/apps/remix/app/components/forms/password.tsx @@ -6,8 +6,8 @@ import { useForm } from 'react-hook-form'; import { match } from 'ts-pattern'; import { z } from 'zod'; +import { authClient } from '@documenso/auth/client'; import { AppError } from '@documenso/lib/errors/app-error'; -import { trpc } from '@documenso/trpc/react'; import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -55,11 +55,9 @@ export const PasswordForm = ({ className }: PasswordFormProps) => { const isSubmitting = form.formState.isSubmitting; - const { mutateAsync: updatePassword } = trpc.profile.updatePassword.useMutation(); - const onFormSubmit = async ({ currentPassword, password }: TPasswordFormSchema) => { try { - await updatePassword({ + await authClient.updatePassword({ currentPassword, password, }); diff --git a/apps/remix/app/components/forms/public-profile-claim-dialog.tsx b/apps/remix/app/components/forms/public-profile-claim-dialog.tsx index 57002c78b..308e8a866 100644 --- a/apps/remix/app/components/forms/public-profile-claim-dialog.tsx +++ b/apps/remix/app/components/forms/public-profile-claim-dialog.tsx @@ -88,12 +88,12 @@ export const ClaimPublicProfileDialogForm = ({ } catch (err) { const error = AppError.parseError(err); - if (error.code === AppErrorCode.PROFILE_URL_TAKEN) { + if (error.code === 'PROFILE_URL_TAKEN') { form.setError('url', { type: 'manual', message: _(msg`This username is already taken`), }); - } else if (error.code === AppErrorCode.PREMIUM_PROFILE_URL) { + } else if (error.code === 'PREMIUM_PROFILE_URL') { form.setError('url', { type: 'manual', message: error.message, diff --git a/apps/remix/app/components/forms/public-profile-form.tsx b/apps/remix/app/components/forms/public-profile-form.tsx index 9bb6e7521..1cec987cf 100644 --- a/apps/remix/app/components/forms/public-profile-form.tsx +++ b/apps/remix/app/components/forms/public-profile-form.tsx @@ -11,7 +11,7 @@ import { useForm } from 'react-hook-form'; import type { z } from 'zod'; import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard'; -import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; +import { AppError } from '@documenso/lib/errors/app-error'; import { formatUserProfilePath } from '@documenso/lib/utils/public-profiles'; import { MAX_PROFILE_BIO_LENGTH, @@ -88,8 +88,8 @@ export const PublicProfileForm = ({ const error = AppError.parseError(err); switch (error.code) { - case AppErrorCode.PREMIUM_PROFILE_URL: - case AppErrorCode.PROFILE_URL_TAKEN: + case 'PREMIUM_PROFILE_URL': + case 'PROFILE_URL_TAKEN': form.setError('url', { type: 'manual', message: error.message, diff --git a/apps/remix/app/components/forms/signin.tsx b/apps/remix/app/components/forms/signin.tsx index 0762c57d4..adff07e02 100644 --- a/apps/remix/app/components/forms/signin.tsx +++ b/apps/remix/app/components/forms/signin.tsx @@ -15,7 +15,6 @@ import { z } from 'zod'; import { authClient } from '@documenso/auth/client'; import { AuthenticationErrorCode } from '@documenso/auth/server/lib/errors/error-codes'; import { AppError } from '@documenso/lib/errors/app-error'; -import { ErrorCode } from '@documenso/lib/next-auth/error-codes'; import { trpc } from '@documenso/trpc/react'; import { ZCurrentPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; @@ -46,8 +45,6 @@ const CommonErrorMessages = { [AuthenticationErrorCode.AccountDisabled]: msg`This account has been disabled. Please contact support.`, }; -const TwoFactorEnabledErrorCode = ErrorCode.TWO_FACTOR_MISSING_CREDENTIALS; - const LOGIN_REDIRECT_PATH = '/documents'; export const ZSignInFormSchema = z.object({ @@ -90,7 +87,7 @@ export const SignInForm = ({ const [isPasskeyLoading, setIsPasskeyLoading] = useState(false); - const callbackUrl = useMemo(() => { + const redirectUrl = useMemo(() => { // Handle SSR if (typeof window === 'undefined') { return LOGIN_REDIRECT_PATH; @@ -161,15 +158,16 @@ export const SignInForm = ({ const credential = await startAuthentication(options); - const result = await authClient.passkey.signIn({ + await authClient.passkey.signIn({ credential: JSON.stringify(credential), csrfToken: sessionId, + redirectUrl, // callbackUrl, // redirect: false, }); // Todo: Can't use navigate because of embed? - window.location.href = callbackUrl; + // window.location.href = callbackUrl; } catch (err) { setIsPasskeyLoading(false); @@ -208,17 +206,18 @@ export const SignInForm = ({ password, totpCode, backupCode, + redirectUrl, // callbackUrl, // redirect: false, }); - window.location.href = callbackUrl; + // window.location.href = callbackUrl; // Todo: Handle redirect. } catch (err) { console.log(err); const error = AppError.parseError(err); - if (error.code === TwoFactorEnabledErrorCode) { + if (error.code === 'TWO_FACTOR_MISSING_CREDENTIALS') { setIsTwoFactorAuthenticationDialogOpen(true); return; } @@ -257,12 +256,7 @@ export const SignInForm = ({ const onSignInWithGoogleClick = async () => { try { - // await signIn('google', { - // callbackUrl, - // }); - - const result = await authClient.google.signIn(); - console.log(result); + await authClient.google.signIn(); // Todo: Handle redirect. } catch (err) { toast({ title: _(msg`An unknown error occurred`), diff --git a/apps/remix/app/components/forms/signup.tsx b/apps/remix/app/components/forms/signup.tsx index de822c39b..17b63047b 100644 --- a/apps/remix/app/components/forms/signup.tsx +++ b/apps/remix/app/components/forms/signup.tsx @@ -12,9 +12,9 @@ import { Link, useNavigate, useSearchParams } from 'react-router'; import { z } from 'zod'; import communityCardsImage from '@documenso/assets/images/community-cards.png'; +import { authClient } from '@documenso/auth/client'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; -import { trpc } from '@documenso/trpc/react'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -71,8 +71,8 @@ export const signupErrorMessages: Record = { SIGNUP_DISABLED: msg`Signups are disabled.`, [AppErrorCode.ALREADY_EXISTS]: msg`User with this email already exists. Please use a different email address.`, [AppErrorCode.INVALID_REQUEST]: msg`We were unable to create your account. Please review the information you provided and try again.`, - [AppErrorCode.PROFILE_URL_TAKEN]: msg`This username has already been taken`, - [AppErrorCode.PREMIUM_PROFILE_URL]: msg`Only subscribers can have a username shorter than 6 characters`, + PROFILE_URL_TAKEN: msg`This username has already been taken`, + PREMIUM_PROFILE_URL: msg`Only subscribers can have a username shorter than 6 characters`, }; export type TSignUpFormSchema = z.infer; @@ -93,7 +93,7 @@ export const SignUpForm = ({ const { _ } = useLingui(); const { toast } = useToast(); - // const analytics = useAnalytics(); + // const analytics = useAnalytics(); // Todo const navigate = useNavigate(); const [searchParams] = useSearchParams(); @@ -120,11 +120,9 @@ export const SignUpForm = ({ const name = form.watch('name'); const url = form.watch('url'); - const { mutateAsync: signup } = trpc.auth.signup.useMutation(); - const onFormSubmit = async ({ name, email, password, signature, url }: TSignUpFormSchema) => { try { - await signup({ name, email, password, signature, url }); + await authClient.emailPassword.signUp({ name, email, password, signature, url }); void navigate(`/unverified-account`); @@ -146,10 +144,7 @@ export const SignUpForm = ({ const errorMessage = signupErrorMessages[error.code] ?? signupErrorMessages.INVALID_REQUEST; - if ( - error.code === AppErrorCode.PROFILE_URL_TAKEN || - error.code === AppErrorCode.PREMIUM_PROFILE_URL - ) { + if (error.code === 'PROFILE_URL_TAKEN' || error.code === 'PREMIUM_PROFILE_URL') { form.setError('url', { type: 'manual', message: _(errorMessage), @@ -175,8 +170,7 @@ export const SignUpForm = ({ const onSignUpWithGoogleClick = async () => { try { - await new Promise((resolve) => setTimeout(resolve, 2000)); - // await signIn('google', { callbackUrl: SIGN_UP_REDIRECT_PATH }); + await authClient.google.signIn(); } catch (err) { toast({ title: _(msg`An unknown error occurred`), diff --git a/apps/remix/app/components/hidden/leaderboard+/data-table-leaderboard.tsx b/apps/remix/app/components/hidden/leaderboard+/data-table-leaderboard.tsx deleted file mode 100644 index 596f0051d..000000000 --- a/apps/remix/app/components/hidden/leaderboard+/data-table-leaderboard.tsx +++ /dev/null @@ -1,169 +0,0 @@ -'use client'; - -import { useEffect, useMemo, useState, useTransition } from 'react'; - -import { msg } from '@lingui/macro'; -import { useLingui } from '@lingui/react'; -import { ChevronDownIcon as CaretSortIcon, Loader } from 'lucide-react'; - -import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value'; -import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; -import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table'; -import { DataTable } from '@documenso/ui/primitives/data-table'; -import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; -import { Input } from '@documenso/ui/primitives/input'; - -export type SigningVolume = { - id: number; - name: string; - signingVolume: number; - createdAt: Date; - planId: string; -}; - -type LeaderboardTableProps = { - signingVolume: SigningVolume[]; - totalPages: number; - perPage: number; - page: number; - sortBy: 'name' | 'createdAt' | 'signingVolume'; - sortOrder: 'asc' | 'desc'; -}; - -export const LeaderboardTable = ({ - signingVolume, - totalPages, - perPage, - page, - sortBy, - sortOrder, -}: LeaderboardTableProps) => { - const { _, i18n } = useLingui(); - - const [isPending, startTransition] = useTransition(); - const updateSearchParams = useUpdateSearchParams(); - const [searchString, setSearchString] = useState(''); - const debouncedSearchString = useDebouncedValue(searchString, 1000); - - const columns = useMemo(() => { - return [ - { - header: () => ( -
handleColumnSort('name')} - > - {_(msg`Name`)} - -
- ), - accessorKey: 'name', - cell: ({ row }) => { - return ( - - ); - }, - size: 250, - }, - { - header: () => ( -
handleColumnSort('signingVolume')} - > - {_(msg`Signing Volume`)} - -
- ), - accessorKey: 'signingVolume', - cell: ({ row }) =>
{Number(row.getValue('signingVolume'))}
, - }, - { - header: () => { - return ( -
handleColumnSort('createdAt')} - > - {_(msg`Created`)} - -
- ); - }, - accessorKey: 'createdAt', - cell: ({ row }) => i18n.date(row.original.createdAt), - }, - ] satisfies DataTableColumnDef[]; - }, [sortOrder]); - - useEffect(() => { - startTransition(() => { - updateSearchParams({ - search: debouncedSearchString, - page: 1, - perPage, - sortBy, - sortOrder, - }); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [debouncedSearchString]); - - const onPaginationChange = (page: number, perPage: number) => { - startTransition(() => { - updateSearchParams({ - page, - perPage, - }); - }); - }; - - const handleChange = (e: React.ChangeEvent) => { - setSearchString(e.target.value); - }; - - const handleColumnSort = (column: 'name' | 'createdAt' | 'signingVolume') => { - startTransition(() => { - updateSearchParams({ - sortBy: column, - sortOrder: sortBy === column && sortOrder === 'asc' ? 'desc' : 'asc', - }); - }); - }; - - return ( -
- - - {(table) => } - - - {isPending && ( -
- -
- )} -
- ); -}; diff --git a/apps/remix/app/components/hidden/leaderboard+/fetch-leaderboard.actions.ts b/apps/remix/app/components/hidden/leaderboard+/fetch-leaderboard.actions.ts deleted file mode 100644 index 42fc20c97..000000000 --- a/apps/remix/app/components/hidden/leaderboard+/fetch-leaderboard.actions.ts +++ /dev/null @@ -1,25 +0,0 @@ -'use server'; - -import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; -import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin'; -import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume'; - -type SearchOptions = { - search: string; - page: number; - perPage: number; - sortBy: 'name' | 'createdAt' | 'signingVolume'; - sortOrder: 'asc' | 'desc'; -}; - -export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) { - const { user } = await getRequiredServerComponentSession(); - - if (!isAdmin(user)) { - throw new Error('Unauthorized'); - } - - const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder }); - - return results; -} diff --git a/apps/remix/app/components/hidden/leaderboard+/page.tsx b/apps/remix/app/components/hidden/leaderboard+/page.tsx deleted file mode 100644 index 87a9c792c..000000000 --- a/apps/remix/app/components/hidden/leaderboard+/page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Trans } from '@lingui/macro'; - -import { LeaderboardTable } from './data-table-leaderboard'; -import { search } from './fetch-leaderboard.actions'; - -type AdminLeaderboardProps = { - searchParams?: { - search?: string; - page?: number; - perPage?: number; - sortBy?: 'name' | 'createdAt' | 'signingVolume'; - sortOrder?: 'asc' | 'desc'; - }; -}; - -export default async function LeaderboardPage({ searchParams = {} }: AdminLeaderboardProps) { - const page = Number(searchParams.page) || 1; - const perPage = Number(searchParams.perPage) || 10; - const searchString = searchParams.search || ''; - const sortBy = searchParams.sortBy || 'signingVolume'; - const sortOrder = searchParams.sortOrder || 'desc'; - - const { leaderboard: signingVolume, totalPages } = await search({ - search: searchString, - page, - perPage, - sortBy, - sortOrder, - }); - - return ( -
-

- Signing Volume -

-
- -
-
- ); -} diff --git a/apps/remix/app/_[id]/logs/download-audit-log-button.tsx b/apps/remix/app/components/pages/document/document-audit-log-download-button.tsx similarity index 92% rename from apps/remix/app/_[id]/logs/download-audit-log-button.tsx rename to apps/remix/app/components/pages/document/document-audit-log-download-button.tsx index d6be5318c..ee35389ed 100644 --- a/apps/remix/app/_[id]/logs/download-audit-log-button.tsx +++ b/apps/remix/app/components/pages/document/document-audit-log-download-button.tsx @@ -9,13 +9,15 @@ import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type DownloadAuditLogButtonProps = { +export type DocumentAuditLogDownloadButtonProps = { className?: string; - teamId?: number; documentId: number; }; -export const DownloadAuditLogButton = ({ className, documentId }: DownloadAuditLogButtonProps) => { +export const DocumentAuditLogDownloadButton = ({ + className, + documentId, +}: DocumentAuditLogDownloadButtonProps) => { const { toast } = useToast(); const { _ } = useLingui(); diff --git a/apps/remix/app/_[id]/logs/download-certificate-button.tsx b/apps/remix/app/components/pages/document/document-certificate-download-button.tsx similarity index 92% rename from apps/remix/app/_[id]/logs/download-certificate-button.tsx rename to apps/remix/app/components/pages/document/document-certificate-download-button.tsx index 45c78f3b6..73ec237fb 100644 --- a/apps/remix/app/_[id]/logs/download-certificate-button.tsx +++ b/apps/remix/app/components/pages/document/document-certificate-download-button.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { DocumentStatus } from '@prisma/client'; @@ -10,19 +8,17 @@ import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type DownloadCertificateButtonProps = { +export type DocumentCertificateDownloadButtonProps = { className?: string; documentId: number; documentStatus: DocumentStatus; - teamId?: number; }; -export const DownloadCertificateButton = ({ +export const DocumentCertificateDownloadButton = ({ className, documentId, documentStatus, - teamId, -}: DownloadCertificateButtonProps) => { +}: DocumentCertificateDownloadButtonProps) => { const { toast } = useToast(); const { _ } = useLingui(); diff --git a/apps/remix/app/_[id]/edit-document.tsx b/apps/remix/app/components/pages/document/document-edit-form.tsx similarity index 95% rename from apps/remix/app/_[id]/edit-document.tsx rename to apps/remix/app/components/pages/document/document-edit-form.tsx index 1e8f2cca3..b6ff86bec 100644 --- a/apps/remix/app/_[id]/edit-document.tsx +++ b/apps/remix/app/components/pages/document/document-edit-form.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'; import { msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client'; +import { useNavigate, useSearchParams } from 'react-router'; import { isValidLanguageCode } from '@documenso/lib/constants/i18n'; import { @@ -29,7 +30,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast'; import { useOptionalCurrentTeam } from '~/providers/team'; -export type EditDocumentFormProps = { +export type DocumentEditFormProps = { className?: string; initialDocument: TDocument; documentRootPath: string; @@ -39,17 +40,18 @@ export type EditDocumentFormProps = { type EditDocumentStep = 'settings' | 'signers' | 'fields' | 'subject'; const EditDocumentSteps: EditDocumentStep[] = ['settings', 'signers', 'fields', 'subject']; -export const EditDocumentForm = ({ +export const DocumentEditForm = ({ className, initialDocument, documentRootPath, isDocumentEnterprise, -}: EditDocumentFormProps) => { +}: DocumentEditFormProps) => { const { toast } = useToast(); const { _ } = useLingui(); - const router = useRouter(); - const searchParams = useSearchParams(); + const navigate = useNavigate(); + + const [searchParams] = useSearchParams(); const team = useOptionalCurrentTeam(); const [isDocumentPdfLoaded, setIsDocumentPdfLoaded] = useState(false); @@ -194,9 +196,6 @@ export const EditDocumentForm = ({ }, }); - // Router refresh is here to clear the router cache for when navigating to /documents. - router.refresh(); - setStep('signers'); } catch (err) { console.error(err); @@ -227,9 +226,6 @@ export const EditDocumentForm = ({ }), ]); - // Router refresh is here to clear the router cache for when navigating to /documents. - router.refresh(); - setStep('fields'); } catch (err) { console.error(err); @@ -265,9 +261,6 @@ export const EditDocumentForm = ({ } } - // Router refresh is here to clear the router cache for when navigating to /documents. - router.refresh(); - setStep('subject'); } catch (err) { console.error(err); @@ -301,7 +294,7 @@ export const EditDocumentForm = ({ duration: 5000, }); - router.push(documentRootPath); + void navigate(documentRootPath); return; } @@ -312,7 +305,7 @@ export const EditDocumentForm = ({ duration: 5000, }); } else { - router.push(`${documentRootPath}/${document.id}`); + void navigate(`${documentRootPath}/${document.id}`); } } catch (err) { console.error(err); diff --git a/apps/remix/app/_[id]/document-page-view-button.tsx b/apps/remix/app/components/pages/document/document-page-view-button.tsx similarity index 91% rename from apps/remix/app/_[id]/document-page-view-button.tsx rename to apps/remix/app/components/pages/document/document-page-view-button.tsx index ff38f285c..141e51e56 100644 --- a/apps/remix/app/_[id]/document-page-view-button.tsx +++ b/apps/remix/app/components/pages/document/document-page-view-button.tsx @@ -3,7 +3,7 @@ import { useLingui } from '@lingui/react'; import type { Document, Recipient, Team, User } from '@prisma/client'; import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client'; import { CheckCircle, Download, EyeIcon, Pencil } from 'lucide-react'; -import { useSession } from 'next-auth/react'; +import { Link } from 'react-router'; import { match } from 'ts-pattern'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; @@ -12,25 +12,23 @@ import { trpc as trpcClient } from '@documenso/trpc/client'; import { Button } from '@documenso/ui/primitives/button'; import { useToast } from '@documenso/ui/primitives/use-toast'; +import { useAuth } from '~/providers/auth'; + export type DocumentPageViewButtonProps = { document: Document & { user: Pick; recipients: Recipient[]; team: Pick | null; }; - team?: Pick; }; export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps) => { - const { data: session } = useSession(); + const { user } = useAuth(); + const { toast } = useToast(); const { _ } = useLingui(); - if (!session) { - return null; - } - - const recipient = document.recipients.find((recipient) => recipient.email === session.user.email); + const recipient = document.recipients.find((recipient) => recipient.email === user.email); const isRecipient = !!recipient; const isPending = document.status === DocumentStatus.PENDING; @@ -77,7 +75,7 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps }) .with({ isRecipient: true, isPending: true, isSigned: false }, () => ( diff --git a/apps/remix/app/_[id]/document-page-view-dropdown.tsx b/apps/remix/app/components/pages/document/document-page-view-dropdown.tsx similarity index 84% rename from apps/remix/app/_[id]/document-page-view-dropdown.tsx rename to apps/remix/app/components/pages/document/document-page-view-dropdown.tsx index 2b74460f9..d6fb61781 100644 --- a/apps/remix/app/_[id]/document-page-view-dropdown.tsx +++ b/apps/remix/app/components/pages/document/document-page-view-dropdown.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { DocumentStatus } from '@prisma/client'; -import type { Document, Recipient, Team, TeamEmail, User } from '@prisma/client'; +import type { Document, Recipient, Team, User } from '@prisma/client'; import { Copy, Download, @@ -14,7 +14,7 @@ import { Share, Trash2, } from 'lucide-react'; -import { useSession } from 'next-auth/react'; +import { Link } from 'react-router'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; import { formatDocumentsPath } from '@documenso/lib/utils/teams'; @@ -29,11 +29,12 @@ import { } from '@documenso/ui/primitives/dropdown-menu'; import { useToast } from '@documenso/ui/primitives/use-toast'; +import { DocumentDeleteDialog } from '~/components/dialogs/document-delete-dialog'; +import { DocumentDuplicateDialog } from '~/components/dialogs/document-duplicate-dialog'; +import { DocumentResendDialog } from '~/components/dialogs/document-resend-dialog'; import { DocumentRecipientLinkCopyDialog } from '~/components/document/document-recipient-link-copy-dialog'; - -import { DeleteDocumentDialog } from '../delete-document-dialog'; -import { DuplicateDocumentDialog } from '../duplicate-document-dialog'; -import { ResendDocumentActionItem } from '../resend-document-dialog'; +import { useAuth } from '~/providers/auth'; +import { useOptionalCurrentTeam } from '~/providers/team'; export type DocumentPageViewDropdownProps = { document: Document & { @@ -41,24 +42,21 @@ export type DocumentPageViewDropdownProps = { recipients: Recipient[]; team: Pick | null; }; - team?: Pick & { teamEmail: TeamEmail | null }; }; -export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDropdownProps) => { - const { data: session } = useSession(); +export const DocumentPageViewDropdown = ({ document }: DocumentPageViewDropdownProps) => { + const { user } = useAuth(); const { toast } = useToast(); const { _ } = useLingui(); + const team = useOptionalCurrentTeam(); + const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false); const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false); - if (!session) { - return null; - } + const recipient = document.recipients.find((recipient) => recipient.email === user.email); - const recipient = document.recipients.find((recipient) => recipient.email === session.user.email); - - const isOwner = document.user.id === session.user.id; + const isOwner = document.user.id === user.id; const isDraft = document.status === DocumentStatus.DRAFT; const isPending = document.status === DocumentStatus.PENDING; const isDeleted = document.deletedAt !== null; @@ -112,7 +110,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro {(isOwner || isCurrentTeamDocument) && !isComplete && ( - + Edit @@ -127,7 +125,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro )} - + Audit Log @@ -165,11 +163,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro /> )} - + - {isDuplicateDialogOpen && ( - )} diff --git a/apps/remix/app/_[id]/document-page-view-information.tsx b/apps/remix/app/components/pages/document/document-page-view-information.tsx similarity index 100% rename from apps/remix/app/_[id]/document-page-view-information.tsx rename to apps/remix/app/components/pages/document/document-page-view-information.tsx diff --git a/apps/remix/app/_[id]/document-page-view-recent-activity.tsx b/apps/remix/app/components/pages/document/document-page-view-recent-activity.tsx similarity index 100% rename from apps/remix/app/_[id]/document-page-view-recent-activity.tsx rename to apps/remix/app/components/pages/document/document-page-view-recent-activity.tsx diff --git a/apps/remix/app/_[id]/document-page-view-recipients.tsx b/apps/remix/app/components/pages/document/document-page-view-recipients.tsx similarity index 99% rename from apps/remix/app/_[id]/document-page-view-recipients.tsx rename to apps/remix/app/components/pages/document/document-page-view-recipients.tsx index 4644e39d1..cf9f0cbe0 100644 --- a/apps/remix/app/_[id]/document-page-view-recipients.tsx +++ b/apps/remix/app/components/pages/document/document-page-view-recipients.tsx @@ -1,3 +1,7 @@ +'use client'; + +import Link from 'next/link'; + import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client'; diff --git a/apps/remix/app/_[id]/logs/document-logs-data-table.tsx b/apps/remix/app/components/tables/document-logs-table.tsx similarity index 96% rename from apps/remix/app/_[id]/logs/document-logs-data-table.tsx rename to apps/remix/app/components/tables/document-logs-table.tsx index 68dbfbea3..3ebf6d5b3 100644 --- a/apps/remix/app/_[id]/logs/document-logs-data-table.tsx +++ b/apps/remix/app/components/tables/document-logs-table.tsx @@ -1,5 +1,9 @@ +'use client'; + import { useMemo } from 'react'; +import { useSearchParams } from 'next/navigation'; + import { msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { DateTime } from 'luxon'; @@ -16,7 +20,7 @@ import { DataTablePagination } from '@documenso/ui/primitives/data-table-paginat import { Skeleton } from '@documenso/ui/primitives/skeleton'; import { TableCell } from '@documenso/ui/primitives/table'; -export type DocumentLogsDataTableProps = { +export type DocumentLogsTableProps = { documentId: number; }; @@ -25,7 +29,7 @@ const dateFormat: DateTimeFormatOptions = { hourCycle: 'h12', }; -export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps) => { +export const DocumentLogsTable = ({ documentId }: DocumentLogsTableProps) => { const { _, i18n } = useLingui(); const searchParams = useSearchParams(); diff --git a/apps/remix/app/documents+/_documents-page-view.tsx b/apps/remix/app/documents+/_documents-page-view.tsx deleted file mode 100644 index e73462462..000000000 --- a/apps/remix/app/documents+/_documents-page-view.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { Trans } from '@lingui/macro'; -import type { Team, TeamEmail, TeamMemberRole } from '@prisma/client'; -import { Link } from 'react-router'; - -import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; -import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents'; -import { parseToIntegerArray } from '@documenso/lib/utils/params'; -import { formatDocumentsPath } from '@documenso/lib/utils/teams'; -import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status'; -import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status'; -import { trpc } from '@documenso/trpc/react'; -import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar'; -import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs'; - -import { DocumentSearch } from '~/components/(dashboard)/document-search/document-search'; -import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector'; -import { isPeriodSelectorValue } from '~/components/(dashboard)/period-selector/types'; -import { DocumentUploadDropzone } from '~/components/document/document-upload'; -import { DocumentStatus } from '~/components/formatter/document-status'; -import { DocumentsTable } from '~/components/tables/documents-table'; -import { DocumentsTableEmptyState } from '~/components/tables/documents-table-empty-state'; -import { DocumentsTableSenderFilter } from '~/components/tables/documents-table-sender-filter'; -import { useAuth } from '~/providers/auth'; - -export interface DocumentsPageViewProps { - searchParams?: { - status?: ExtendedDocumentStatus; - period?: PeriodSelectorValue; - page?: string; - perPage?: string; - senderIds?: string; - search?: string; - }; - team?: Team & { teamEmail?: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } }; -} - -export const DocumentsPageView = ({ searchParams = {}, team }: DocumentsPageViewProps) => { - const { user } = useAuth(); - - const status = isExtendedDocumentStatus(searchParams.status) ? searchParams.status : 'ALL'; - const period = isPeriodSelectorValue(searchParams.period) ? searchParams.period : ''; - const page = Number(searchParams.page) || 1; - const perPage = Number(searchParams.perPage) || 20; - const senderIds = parseToIntegerArray(searchParams.senderIds ?? ''); - const search = searchParams.search || ''; - const currentTeam = team - ? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email } - : undefined; - const currentTeamMemberRole = team?.currentTeamMember?.role; - - // const results = await findDocuments({ - // status, - // orderBy: { - // column: 'createdAt', - // direction: 'desc', - // }, - // page, - // perPage, - // period, - // senderIds, - // query: search, - // }); - - const { data, isLoading, isLoadingError } = trpc.document.findDocuments.useQuery({ - page, - perPage, - }); - - const getTabHref = (value: typeof status) => { - const params = new URLSearchParams(searchParams); - - params.set('status', value); - - if (params.has('page')) { - params.delete('page'); - } - - return `${formatDocumentsPath(team?.url)}?${params.toString()}`; - }; - - return ( -
- - -
-
- {team && ( - - {team.avatarImageId && ( - - )} - - {team.name.slice(0, 1)} - - - )} - -

- Documents -

-
- -
- - - {[ - ExtendedDocumentStatus.INBOX, - ExtendedDocumentStatus.PENDING, - ExtendedDocumentStatus.COMPLETED, - ExtendedDocumentStatus.DRAFT, - ExtendedDocumentStatus.ALL, - ].map((value) => ( - - - - - {value !== ExtendedDocumentStatus.ALL && ( - todo - )} - - - ))} - - - - {team && } - -
- -
-
- -
-
-
- -
-
- {data && data.count === 0 ? ( - - ) : ( - - )} -
-
-
- ); -}; diff --git a/apps/remix/app/documents+/index.tsx b/apps/remix/app/documents+/index.tsx deleted file mode 100644 index 47c3c961a..000000000 --- a/apps/remix/app/documents+/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useSearchParams } from 'react-router'; - -export function meta() { - return [{ title: 'Documents' }]; -} - -export default function DocumentsPage() { - const [searchParams] = useSearchParams(); - - return ( - <> -
hello
- {/* */} - - ); -} diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx index 10c075e82..13216183b 100644 --- a/apps/remix/app/root.tsx +++ b/apps/remix/app/root.tsx @@ -5,6 +5,7 @@ import { Scripts, ScrollRestoration, isRouteErrorResponse, + useLoaderData, } from 'react-router'; import { TrpcProvider } from '@documenso/trpc/react'; @@ -32,7 +33,17 @@ export const links: Route.LinksFunction = () => [ { rel: 'stylesheet', href: stylesheet }, ]; +export function loader() { + return { + __ENV__: Object.fromEntries( + Object.entries(process.env).filter(([key]) => key.startsWith('NEXT_')), + ), + }; +} + export function Layout({ children }: { children: React.ReactNode }) { + const { __ENV__ } = useLoaderData() || {}; + return ( @@ -45,6 +56,12 @@ export function Layout({ children }: { children: React.ReactNode }) { {children} + +