From 1057ae6d2a9e398ed15ac2c11b5dd2c509089954 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 5 Feb 2025 00:57:00 +1100 Subject: [PATCH] fix: wip --- .../(dashboard)/period-selector/types.ts | 6 -- .../refresh-on-focus/refresh-on-focus.tsx | 18 ----- .../(dashboard)/settings/token/contants.ts | 9 --- .../dialogs/admin-user-delete-dialog.tsx | 8 +-- .../dialogs/document-delete-dialog.tsx | 31 ++++---- .../dialogs/document-move-dialog.tsx | 10 +-- .../dialogs/document-resend-dialog.tsx | 3 +- .../dialogs/template-delete-dialog.tsx | 12 +++- .../dialogs/template-duplicate-dialog.tsx | 2 - .../dialogs/template-move-dialog.tsx | 39 +++++++--- .../token-delete-dialog.tsx} | 14 ++-- .../dialogs/webhook-create-dialog.tsx | 2 - .../dialogs/webhook-delete-dialog.tsx | 4 -- .../app/components/forms/avatar-image.tsx | 13 ++-- .../forms/public-profile-claim-dialog.tsx | 2 +- apps/remix/app/components/forms/signup.tsx | 7 +- .../team-update-form.tsx} | 10 +-- apps/remix/app/components/forms/token.tsx | 72 +++++++++---------- .../layout => general}/app-banner.tsx | 0 .../app-command-menu.tsx} | 4 +- .../header.tsx => general/app-header.tsx} | 14 ++-- .../app-nav-desktop.tsx} | 8 ++- .../app-nav-mobile.tsx} | 4 +- .../avatar-with-recipient.tsx | 0 .../components/{ui => general}/background.tsx | 0 .../document-signing-page-view.tsx | 2 +- .../document-history-sheet-changes.tsx | 0 .../document/document-history-sheet.tsx | 0 .../document/document-page-view-dropdown.tsx | 7 +- .../document/document-read-only-fields.tsx | 0 .../document-recipient-link-copy-dialog.tsx | 0 .../document}/document-search.tsx | 0 .../document}/document-status.tsx | 0 .../document/document-upload.tsx | 23 +++--- .../layout => general}/menu-switcher.tsx | 0 .../metric-card => general}/metric-card.tsx | 0 .../period-selector.tsx | 6 +- .../components/general/refresh-on-focus.tsx | 21 ++++++ .../settings-header.tsx} | 0 .../settings-nav-desktop.tsx} | 34 +++++---- .../settings-nav-mobile.tsx} | 34 +++++---- .../avatar => general}/stack-avatar.tsx | 0 .../stack-avatars-with-tooltip.tsx | 0 .../avatar => general}/stack-avatars.tsx | 0 ...-nav.tsx => team-settings-nav-desktop.tsx} | 4 +- ...e-nav.tsx => team-settings-nav-mobile.tsx} | 4 +- .../template-page-view-documents-table.tsx | 9 +-- .../template}/template-type.tsx | 0 .../{ui => general}/user-profile-skeleton.tsx | 0 .../{ui => general}/user-profile-timur.tsx | 0 .../verify-email-banner.tsx | 1 + .../documents-table-action-dropdown.tsx | 2 +- .../app/components/tables/documents-table.tsx | 4 +- .../team-settings-billing-invoices-table.tsx} | 6 +- .../templates-table-action-dropdown.tsx | 12 ++++ .../app/components/tables/templates-table.tsx | 2 +- .../user-settings-current-teams-table.tsx} | 2 +- ...-settings-pending-teams-table-actions.tsx} | 6 +- .../user-settings-pending-teams-table.tsx} | 6 +- .../user-settings-teams-page-table.tsx} | 10 ++- apps/remix/app/root.tsx | 4 ++ .../app/routes/_authenticated+/_layout.tsx | 6 +- .../routes/_authenticated+/admin+/_layout.tsx | 2 +- .../_authenticated+/admin+/documents.$id.tsx | 2 +- .../_authenticated+/admin+/documents.tsx | 2 +- .../_authenticated+/admin+/site-settings.tsx | 2 +- .../routes/_authenticated+/admin+/stats.tsx | 2 +- .../_authenticated+/documents+/$id._index.tsx | 18 ++--- .../_authenticated+/documents+/$id.edit.tsx | 6 +- .../_authenticated+/documents+/$id.logs.tsx | 8 +-- .../_authenticated+/documents+/_index.tsx | 10 +-- .../_authenticated+/settings+/_layout.tsx | 8 +-- .../_authenticated+/settings+/profile.tsx | 2 +- .../settings+/public-profile+/index.tsx | 4 +- .../settings+/security+/activity+/index.tsx | 2 +- .../settings+/security+/index.tsx | 2 +- .../settings+/security+/passkeys+/index.tsx | 2 +- .../settings+/teams+/index.tsx | 4 +- .../settings+/tokens+/index.tsx | 28 +++----- .../settings+/webhooks+/$id.tsx | 2 +- .../settings+/webhooks+/index.tsx | 2 +- .../_authenticated+/t.$teamUrl+/_layout.tsx | 2 +- .../t.$teamUrl+/settings+/_index.tsx | 10 +-- .../t.$teamUrl+/settings+/_layout.tsx | 10 +-- .../t.$teamUrl+/settings+/billing.tsx | 8 +-- .../t.$teamUrl+/settings+/members.tsx | 2 +- .../t.$teamUrl+/settings+/preferences.tsx | 2 +- .../t.$teamUrl+/settings+/public-profile.tsx | 2 +- .../t.$teamUrl+/settings+/tokens.tsx | 8 +-- .../t.$teamUrl+/settings+/webhooks.$id.tsx | 2 +- .../t.$teamUrl+/settings+/webhooks.tsx | 2 +- .../_authenticated+/templates+/$id._index.tsx | 14 ++-- .../_authenticated+/templates+/$id.edit.tsx | 4 +- .../_authenticated+/templates+/_index.tsx | 6 +- apps/remix/app/routes/_profile+/_layout.tsx | 2 +- apps/remix/app/routes/_profile+/p.$url.tsx | 6 +- apps/remix/app/routes/_recipient+/_layout.tsx | 4 +- apps/remix/app/routes/embed+/direct.$url.tsx | 2 +- apps/remix/app/routes/embed+/sign.$url.tsx | 2 +- apps/remix/server/trpc/trpc-context.ts | 14 +--- ...ssion-context.ts => get-loader-session.ts} | 0 .../server-only/document/find-documents.ts | 4 +- .../lib/server-only/document/get-stats.ts | 2 +- .../trpc/server/document-router/router.ts | 21 +++++- packages/ui/primitives/switch.tsx | 2 - 105 files changed, 379 insertions(+), 357 deletions(-) delete mode 100644 apps/remix/app/components/(dashboard)/period-selector/types.ts delete mode 100644 apps/remix/app/components/(dashboard)/refresh-on-focus/refresh-on-focus.tsx delete mode 100644 apps/remix/app/components/(dashboard)/settings/token/contants.ts rename apps/remix/app/components/{(dashboard)/settings/token/delete-token-dialog.tsx => dialogs/token-delete-dialog.tsx} (93%) rename apps/remix/app/components/{(teams)/forms/update-team-form.tsx => forms/team-update-form.tsx} (94%) rename apps/remix/app/components/{(dashboard)/layout => general}/app-banner.tsx (100%) rename apps/remix/app/components/{(dashboard)/common/command-menu.tsx => general/app-command-menu.tsx} (98%) rename apps/remix/app/components/{(dashboard)/layout/header.tsx => general/app-header.tsx} (88%) rename apps/remix/app/components/{(dashboard)/layout/desktop-nav.tsx => general/app-nav-desktop.tsx} (93%) rename apps/remix/app/components/{(dashboard)/layout/mobile-navigation.tsx => general/app-nav-mobile.tsx} (94%) rename apps/remix/app/components/{(dashboard)/avatar => general}/avatar-with-recipient.tsx (100%) rename apps/remix/app/components/{ui => general}/background.tsx (100%) rename apps/remix/app/components/{ => general}/document/document-history-sheet-changes.tsx (100%) rename apps/remix/app/components/{ => general}/document/document-history-sheet.tsx (100%) rename apps/remix/app/components/{ => general}/document/document-read-only-fields.tsx (100%) rename apps/remix/app/components/{ => general}/document/document-recipient-link-copy-dialog.tsx (100%) rename apps/remix/app/components/{(dashboard)/document-search => general/document}/document-search.tsx (100%) rename apps/remix/app/components/{formatter => general/document}/document-status.tsx (100%) rename apps/remix/app/components/{ => general}/document/document-upload.tsx (94%) rename apps/remix/app/components/{(dashboard)/layout => general}/menu-switcher.tsx (100%) rename apps/remix/app/components/{(dashboard)/metric-card => general}/metric-card.tsx (100%) rename apps/remix/app/components/{(dashboard)/period-selector => general}/period-selector.tsx (83%) create mode 100644 apps/remix/app/components/general/refresh-on-focus.tsx rename apps/remix/app/components/{(dashboard)/settings/layout/header.tsx => general/settings-header.tsx} (100%) rename apps/remix/app/components/{(dashboard)/settings/layout/desktop-nav.tsx => general/settings-nav-desktop.tsx} (78%) rename apps/remix/app/components/{(dashboard)/settings/layout/mobile-nav.tsx => general/settings-nav-mobile.tsx} (78%) rename apps/remix/app/components/{(dashboard)/avatar => general}/stack-avatar.tsx (100%) rename apps/remix/app/components/{(dashboard)/avatar => general}/stack-avatars-with-tooltip.tsx (100%) rename apps/remix/app/components/{(dashboard)/avatar => general}/stack-avatars.tsx (100%) rename apps/remix/app/components/general/teams/{team-settings-desktop-nav.tsx => team-settings-nav-desktop.tsx} (95%) rename apps/remix/app/components/general/teams/{team-settings-mobile-nav.tsx => team-settings-nav-mobile.tsx} (95%) rename apps/remix/app/components/{formatter => general/template}/template-type.tsx (100%) rename apps/remix/app/components/{ui => general}/user-profile-skeleton.tsx (100%) rename apps/remix/app/components/{ui => general}/user-profile-timur.tsx (100%) rename apps/remix/app/components/{(dashboard)/layout => general}/verify-email-banner.tsx (99%) rename apps/remix/app/components/{(teams)/tables/team-billing-invoices-data-table.tsx => tables/team-settings-billing-invoices-table.tsx} (96%) rename apps/remix/app/components/{(teams)/tables/current-user-teams-data-table.tsx => tables/user-settings-current-teams-table.tsx} (99%) rename apps/remix/app/components/{(teams)/tables/pending-user-teams-data-table-actions.tsx => tables/user-settings-pending-teams-table-actions.tsx} (90%) rename apps/remix/app/components/{(teams)/tables/pending-user-teams-data-table.tsx => tables/user-settings-pending-teams-table.tsx} (95%) rename apps/remix/app/components/{(teams)/tables/user-settings-teams-page-data-table.tsx => tables/user-settings-teams-page-table.tsx} (88%) rename apps/remix/server/utils/{get-required-session-context.ts => get-loader-session.ts} (100%) diff --git a/apps/remix/app/components/(dashboard)/period-selector/types.ts b/apps/remix/app/components/(dashboard)/period-selector/types.ts deleted file mode 100644 index 8ae1c5fbe..000000000 --- a/apps/remix/app/components/(dashboard)/period-selector/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents'; - -export const isPeriodSelectorValue = (value: unknown): value is PeriodSelectorValue => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return ['', '7d', '14d', '30d'].includes(value as string); -}; diff --git a/apps/remix/app/components/(dashboard)/refresh-on-focus/refresh-on-focus.tsx b/apps/remix/app/components/(dashboard)/refresh-on-focus/refresh-on-focus.tsx deleted file mode 100644 index bd4c27203..000000000 --- a/apps/remix/app/components/(dashboard)/refresh-on-focus/refresh-on-focus.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const RefreshOnFocus = () => { - // Todo: Would this still work? - // const { refresh } = useRouter(); - - // const onFocus = useCallback(() => { - // refresh(); - // }, [refresh]); - - // useEffect(() => { - // window.addEventListener('focus', onFocus); - - // return () => { - // window.removeEventListener('focus', onFocus); - // }; - // }, [onFocus]); - - return null; -}; diff --git a/apps/remix/app/components/(dashboard)/settings/token/contants.ts b/apps/remix/app/components/(dashboard)/settings/token/contants.ts deleted file mode 100644 index 414425b25..000000000 --- a/apps/remix/app/components/(dashboard)/settings/token/contants.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { msg } from '@lingui/macro'; - -export const EXPIRATION_DATES = { - ONE_WEEK: msg`7 days`, - ONE_MONTH: msg`1 month`, - THREE_MONTHS: msg`3 months`, - SIX_MONTHS: msg`6 months`, - ONE_YEAR: msg`12 months`, -} as const; diff --git a/apps/remix/app/components/dialogs/admin-user-delete-dialog.tsx b/apps/remix/app/components/dialogs/admin-user-delete-dialog.tsx index 883d69623..f1dc14b70 100644 --- a/apps/remix/app/components/dialogs/admin-user-delete-dialog.tsx +++ b/apps/remix/app/components/dialogs/admin-user-delete-dialog.tsx @@ -3,6 +3,7 @@ import { useState } from 'react'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import type { User } from '@prisma/client'; +import { useNavigate } from 'react-router'; import { match } from 'ts-pattern'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; @@ -29,7 +30,7 @@ export type AdminUserDeleteDialogProps = { export const AdminUserDeleteDialog = ({ className, user }: AdminUserDeleteDialogProps) => { const { _ } = useLingui(); const { toast } = useToast(); - + const navigate = useNavigate(); const [email, setEmail] = useState(''); const { mutateAsync: deleteUser, isPending: isDeletingUser } = @@ -41,14 +42,13 @@ export const AdminUserDeleteDialog = ({ className, user }: AdminUserDeleteDialog id: user.id, }); + await navigate('/admin/users'); + toast({ title: _(msg`Account deleted`), description: _(msg`The account has been deleted successfully.`), duration: 5000, }); - - // todo - // router.push('/admin/users'); } catch (err) { const error = AppError.parseError(err); diff --git a/apps/remix/app/components/dialogs/document-delete-dialog.tsx b/apps/remix/app/components/dialogs/document-delete-dialog.tsx index 81f163e3a..d76d99654 100644 --- a/apps/remix/app/components/dialogs/document-delete-dialog.tsx +++ b/apps/remix/app/components/dialogs/document-delete-dialog.tsx @@ -24,6 +24,7 @@ type DocumentDeleteDialogProps = { id: number; open: boolean; onOpenChange: (_open: boolean) => void; + onDelete?: () => Promise | void; status: DocumentStatus; documentTitle: string; teamId?: number; @@ -34,6 +35,7 @@ export const DocumentDeleteDialog = ({ id, open, onOpenChange, + onDelete, status, documentTitle, canManageDocument, @@ -48,9 +50,7 @@ export const DocumentDeleteDialog = ({ const [isDeleteEnabled, setIsDeleteEnabled] = useState(status === DocumentStatus.DRAFT); const { mutateAsync: deleteDocument, isPending } = trpcReact.document.deleteDocument.useMutation({ - onSuccess: () => { - // todo - // router.refresh(); + onSuccess: async () => { void refreshLimits(); toast({ @@ -59,8 +59,18 @@ export const DocumentDeleteDialog = ({ duration: 5000, }); + await onDelete?.(); + onOpenChange(false); }, + onError: () => { + toast({ + title: _(msg`Something went wrong`), + description: _(msg`This document could not be deleted at this time. Please try again.`), + variant: 'destructive', + duration: 7500, + }); + }, }); useEffect(() => { @@ -70,19 +80,6 @@ export const DocumentDeleteDialog = ({ } }, [open, status]); - const onDelete = async () => { - try { - await deleteDocument({ documentId: id }); - } catch { - toast({ - title: _(msg`Something went wrong`), - description: _(msg`This document could not be deleted at this time. Please try again.`), - variant: 'destructive', - duration: 7500, - }); - } - }; - const onInputChange = (event: React.ChangeEvent) => { setInputValue(event.target.value); setIsDeleteEnabled(event.target.value === _(deleteMessage)); @@ -191,7 +188,7 @@ export const DocumentDeleteDialog = ({ - diff --git a/apps/remix/app/components/(dashboard)/settings/token/delete-token-dialog.tsx b/apps/remix/app/components/dialogs/token-delete-dialog.tsx similarity index 93% rename from apps/remix/app/components/(dashboard)/settings/token/delete-token-dialog.tsx rename to apps/remix/app/components/dialogs/token-delete-dialog.tsx index d420700f9..88eecdb3f 100644 --- a/apps/remix/app/components/(dashboard)/settings/token/delete-token-dialog.tsx +++ b/apps/remix/app/components/dialogs/token-delete-dialog.tsx @@ -29,19 +29,19 @@ import { import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; -export type DeleteTokenDialogProps = { +export type TokenDeleteDialogProps = { teamId?: number; token: Pick; onDelete?: () => void; children?: React.ReactNode; }; -export default function DeleteTokenDialog({ +export default function TokenDeleteDialog({ teamId, token, onDelete, children, -}: DeleteTokenDialogProps) { +}: TokenDeleteDialogProps) { const { _ } = useLingui(); const { toast } = useToast(); @@ -49,13 +49,13 @@ export default function DeleteTokenDialog({ const deleteMessage = _(msg`delete ${token.name}`); - const ZDeleteTokenDialogSchema = z.object({ + const ZTokenDeleteDialogSchema = z.object({ tokenName: z.literal(deleteMessage, { errorMap: () => ({ message: _(msg`You must enter '${deleteMessage}' to proceed`) }), }), }); - type TDeleteTokenByIdMutationSchema = z.infer; + type TDeleteTokenByIdMutationSchema = z.infer; const { mutateAsync: deleteTokenMutation } = trpc.apiToken.deleteTokenById.useMutation({ onSuccess() { @@ -64,7 +64,7 @@ export default function DeleteTokenDialog({ }); const form = useForm({ - resolver: zodResolver(ZDeleteTokenDialogSchema), + resolver: zodResolver(ZTokenDeleteDialogSchema), values: { tokenName: '', }, @@ -84,8 +84,6 @@ export default function DeleteTokenDialog({ }); setIsOpen(false); - - // router.refresh(); // Todo } catch (error) { toast({ title: _(msg`An unknown error occurred`), diff --git a/apps/remix/app/components/dialogs/webhook-create-dialog.tsx b/apps/remix/app/components/dialogs/webhook-create-dialog.tsx index c2c95bca0..c34d77313 100644 --- a/apps/remix/app/components/dialogs/webhook-create-dialog.tsx +++ b/apps/remix/app/components/dialogs/webhook-create-dialog.tsx @@ -88,8 +88,6 @@ export const WebhookCreateDialog = ({ trigger, ...props }: WebhookCreateDialogPr }); form.reset(); - - // router.refresh(); // Todo } catch (err) { toast({ title: _(msg`Error`), diff --git a/apps/remix/app/components/dialogs/webhook-delete-dialog.tsx b/apps/remix/app/components/dialogs/webhook-delete-dialog.tsx index d38b0a2b6..9d6fb594f 100644 --- a/apps/remix/app/components/dialogs/webhook-delete-dialog.tsx +++ b/apps/remix/app/components/dialogs/webhook-delete-dialog.tsx @@ -1,5 +1,3 @@ -'use effect'; - import { useEffect, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -77,8 +75,6 @@ export const WebhookDeleteDialog = ({ webhook, children }: WebhookDeleteDialogPr }); setOpen(false); - - // router.refresh(); // Todo } catch (error) { toast({ title: _(msg`An unknown error occurred`), diff --git a/apps/remix/app/components/forms/avatar-image.tsx b/apps/remix/app/components/forms/avatar-image.tsx index 6e612f85c..9d2b97a54 100644 --- a/apps/remix/app/components/forms/avatar-image.tsx +++ b/apps/remix/app/components/forms/avatar-image.tsx @@ -5,13 +5,14 @@ import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { ErrorCode, useDropzone } from 'react-dropzone'; import { useForm } from 'react-hook-form'; +import { useRevalidator } from 'react-router'; import { match } from 'ts-pattern'; import { z } from 'zod'; import { useSession } from '@documenso/lib/client-only/providers/session'; -import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { AppError } from '@documenso/lib/errors/app-error'; import { base64 } from '@documenso/lib/universal/base64'; +import { formatAvatarUrl } from '@documenso/lib/utils/avatars'; import { extractInitials } from '@documenso/lib/utils/recipient-formatter'; import { trpc } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; @@ -43,6 +44,7 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => { const { user } = useSession(); const { _ } = useLingui(); const { toast } = useToast(); + const { revalidate } = useRevalidator(); const team = useOptionalCurrentTeam(); @@ -106,8 +108,7 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => { duration: 5000, }); - // Todo - // router.refresh(); + void revalidate(); } catch (err) { const error = AppError.parseError(err); @@ -144,11 +145,7 @@ export const AvatarImageForm = ({ className }: AvatarImageFormProps) => {
- {avatarImageId && ( - - )} + {avatarImageId && } {initials} 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 308e8a866..d08361e0d 100644 --- a/apps/remix/app/components/forms/public-profile-claim-dialog.tsx +++ b/apps/remix/app/components/forms/public-profile-claim-dialog.tsx @@ -31,7 +31,7 @@ import { import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; -import { UserProfileSkeleton } from '../ui/user-profile-skeleton'; +import { UserProfileSkeleton } from '../general/user-profile-skeleton'; export const ZClaimPublicProfileFormSchema = z.object({ url: z diff --git a/apps/remix/app/components/forms/signup.tsx b/apps/remix/app/components/forms/signup.tsx index 9e328278c..6032ec0c2 100644 --- a/apps/remix/app/components/forms/signup.tsx +++ b/apps/remix/app/components/forms/signup.tsx @@ -32,8 +32,8 @@ import { PasswordInput } from '@documenso/ui/primitives/password-input'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { useToast } from '@documenso/ui/primitives/use-toast'; -import { UserProfileSkeleton } from '~/components/ui/user-profile-skeleton'; -import { UserProfileTimur } from '~/components/ui/user-profile-timur'; +import { UserProfileSkeleton } from '~/components/general/user-profile-skeleton'; +import { UserProfileTimur } from '~/components/general/user-profile-timur'; const SIGN_UP_REDIRECT_PATH = '/documents'; @@ -216,9 +216,8 @@ export const SignUpForm = ({
community-cards
diff --git a/apps/remix/app/components/(teams)/forms/update-team-form.tsx b/apps/remix/app/components/forms/team-update-form.tsx similarity index 94% rename from apps/remix/app/components/(teams)/forms/update-team-form.tsx rename to apps/remix/app/components/forms/team-update-form.tsx index fa26195a6..fb9e9b761 100644 --- a/apps/remix/app/components/(teams)/forms/update-team-form.tsx +++ b/apps/remix/app/components/forms/team-update-form.tsx @@ -28,20 +28,20 @@ export type UpdateTeamDialogProps = { teamUrl: string; }; -const ZUpdateTeamFormSchema = ZUpdateTeamMutationSchema.shape.data.pick({ +const ZTeamUpdateFormSchema = ZUpdateTeamMutationSchema.shape.data.pick({ name: true, url: true, }); -type TUpdateTeamFormSchema = z.infer; +type TTeamUpdateFormSchema = z.infer; -export const UpdateTeamForm = ({ teamId, teamName, teamUrl }: UpdateTeamDialogProps) => { +export const TeamUpdateForm = ({ teamId, teamName, teamUrl }: UpdateTeamDialogProps) => { const navigate = useNavigate(); const { _ } = useLingui(); const { toast } = useToast(); const form = useForm({ - resolver: zodResolver(ZUpdateTeamFormSchema), + resolver: zodResolver(ZTeamUpdateFormSchema), defaultValues: { name: teamName, url: teamUrl, @@ -50,7 +50,7 @@ export const UpdateTeamForm = ({ teamId, teamName, teamUrl }: UpdateTeamDialogPr const { mutateAsync: updateTeam } = trpc.team.updateTeam.useMutation(); - const onFormSubmit = async ({ name, url }: TUpdateTeamFormSchema) => { + const onFormSubmit = async ({ name, url }: TTeamUpdateFormSchema) => { try { await updateTeam({ data: { diff --git a/apps/remix/app/components/forms/token.tsx b/apps/remix/app/components/forms/token.tsx index add83a750..c20536ba3 100644 --- a/apps/remix/app/components/forms/token.tsx +++ b/apps/remix/app/components/forms/token.tsx @@ -37,7 +37,13 @@ import { import { Switch } from '@documenso/ui/primitives/switch'; import { useToast } from '@documenso/ui/primitives/use-toast'; -import { EXPIRATION_DATES } from '../(dashboard)/settings/token/contants'; +export const EXPIRATION_DATES = { + ONE_WEEK: msg`7 days`, + ONE_MONTH: msg`1 month`, + THREE_MONTHS: msg`3 months`, + SIX_MONTHS: msg`6 months`, + ONE_YEAR: msg`12 months`, +} as const; const ZCreateTokenFormSchema = ZCreateTokenMutationSchema.extend({ enabled: z.boolean(), @@ -67,13 +73,6 @@ export const ApiTokenForm = ({ className, teamId, tokens }: ApiTokenFormProps) = const [newlyCreatedToken, setNewlyCreatedToken] = useState(); const [noExpirationDate, setNoExpirationDate] = useState(false); - // This lets us hide the token from being copied if it has been deleted without - // resorting to a useEffect or any other fanciness. This comes at the cost of it - // taking slighly longer to appear since it will need to wait for the router.refresh() - // to finish updating. - const hasNewlyCreatedToken = - tokens?.find((token) => token.id === newlyCreatedToken?.id) !== undefined; - const { mutateAsync: createTokenMutation } = trpc.apiToken.createToken.useMutation({ onSuccess(data) { setNewlyCreatedToken(data); @@ -125,9 +124,6 @@ export const ApiTokenForm = ({ className, teamId, tokens }: ApiTokenFormProps) = }); form.reset(); - - // Todo - // startTransition(() => router.refresh()); } catch (err) { const error = AppError.parseError(err); @@ -259,34 +255,36 @@ export const ApiTokenForm = ({ className, teamId, tokens }: ApiTokenFormProps) = - - {newlyCreatedToken && hasNewlyCreatedToken && ( - - - -

- - Your token was created successfully! Make sure to copy it because you won't be - able to see it again! - -

+ + {newlyCreatedToken && + tokens && + tokens.find((token) => token.id === newlyCreatedToken.id) && ( + + + +

+ + Your token was created successfully! Make sure to copy it because you won't be + able to see it again! + +

-

- {newlyCreatedToken.token} -

+

+ {newlyCreatedToken.token} +

- -
-
-
- )} + +
+
+
+ )}
); diff --git a/apps/remix/app/components/(dashboard)/layout/app-banner.tsx b/apps/remix/app/components/general/app-banner.tsx similarity index 100% rename from apps/remix/app/components/(dashboard)/layout/app-banner.tsx rename to apps/remix/app/components/general/app-banner.tsx diff --git a/apps/remix/app/components/(dashboard)/common/command-menu.tsx b/apps/remix/app/components/general/app-command-menu.tsx similarity index 98% rename from apps/remix/app/components/(dashboard)/common/command-menu.tsx rename to apps/remix/app/components/general/app-command-menu.tsx index c5071bad2..05b31cd98 100644 --- a/apps/remix/app/components/(dashboard)/common/command-menu.tsx +++ b/apps/remix/app/components/general/app-command-menu.tsx @@ -65,12 +65,12 @@ const SETTINGS_PAGES = [ { label: msg`Password`, path: '/settings/password' }, ]; -export type CommandMenuProps = { +export type AppCommandMenuProps = { open?: boolean; onOpenChange?: (_open: boolean) => void; }; -export function CommandMenu({ open, onOpenChange }: CommandMenuProps) { +export function AppCommandMenu({ open, onOpenChange }: AppCommandMenuProps) { const { _ } = useLingui(); const navigate = useNavigate(); diff --git a/apps/remix/app/components/(dashboard)/layout/header.tsx b/apps/remix/app/components/general/app-header.tsx similarity index 88% rename from apps/remix/app/components/(dashboard)/layout/header.tsx rename to apps/remix/app/components/general/app-header.tsx index 42fbe9c2c..edc2d1f6b 100644 --- a/apps/remix/app/components/(dashboard)/layout/header.tsx +++ b/apps/remix/app/components/general/app-header.tsx @@ -10,10 +10,10 @@ import { cn } from '@documenso/ui/lib/utils'; import { BrandingLogo } from '~/components/general/branding-logo'; -import { CommandMenu } from '../common/command-menu'; -import { DesktopNav } from './desktop-nav'; +import { AppCommandMenu } from './app-command-menu'; +import { AppNavDesktop } from './app-nav-desktop'; +import { AppNavMobile } from './app-nav-mobile'; import { MenuSwitcher } from './menu-switcher'; -import { MobileNavigation } from './mobile-navigation'; export type HeaderProps = HTMLAttributes & { user: User; @@ -22,7 +22,7 @@ export type HeaderProps = HTMLAttributes & { export const Header = ({ className, user, teams, ...props }: HeaderProps) => { const params = useParams(); - const { pathname } = useLocation(); // Todo: Test + const { pathname } = useLocation(); const [isCommandMenuOpen, setIsCommandMenuOpen] = useState(false); const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false); @@ -65,7 +65,7 @@ export const Header = ({ className, user, teams, ...props }: HeaderProps) => { - +
{ - + - diff --git a/apps/remix/app/components/(dashboard)/layout/desktop-nav.tsx b/apps/remix/app/components/general/app-nav-desktop.tsx similarity index 93% rename from apps/remix/app/components/(dashboard)/layout/desktop-nav.tsx rename to apps/remix/app/components/general/app-nav-desktop.tsx index 6ef43fddb..787cf0e12 100644 --- a/apps/remix/app/components/(dashboard)/layout/desktop-nav.tsx +++ b/apps/remix/app/components/general/app-nav-desktop.tsx @@ -21,11 +21,15 @@ const navigationLinks = [ }, ]; -export type DesktopNavProps = HTMLAttributes & { +export type AppNavDesktopProps = HTMLAttributes & { setIsCommandMenuOpen: (value: boolean) => void; }; -export const DesktopNav = ({ className, setIsCommandMenuOpen, ...props }: DesktopNavProps) => { +export const AppNavDesktop = ({ + className, + setIsCommandMenuOpen, + ...props +}: AppNavDesktopProps) => { const { _ } = useLingui(); const { pathname } = useLocation(); diff --git a/apps/remix/app/components/(dashboard)/layout/mobile-navigation.tsx b/apps/remix/app/components/general/app-nav-mobile.tsx similarity index 94% rename from apps/remix/app/components/(dashboard)/layout/mobile-navigation.tsx rename to apps/remix/app/components/general/app-nav-mobile.tsx index 944c159c4..6f8785c8e 100644 --- a/apps/remix/app/components/(dashboard)/layout/mobile-navigation.tsx +++ b/apps/remix/app/components/general/app-nav-mobile.tsx @@ -8,12 +8,12 @@ import { getRootHref } from '@documenso/lib/utils/params'; import { Sheet, SheetContent } from '@documenso/ui/primitives/sheet'; import { ThemeSwitcher } from '@documenso/ui/primitives/theme-switcher'; -export type MobileNavigationProps = { +export type AppNavMobileProps = { isMenuOpen: boolean; onMenuOpenChange?: (_value: boolean) => void; }; -export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigationProps) => { +export const AppNavMobile = ({ isMenuOpen, onMenuOpenChange }: AppNavMobileProps) => { const { _ } = useLingui(); const params = useParams(); diff --git a/apps/remix/app/components/(dashboard)/avatar/avatar-with-recipient.tsx b/apps/remix/app/components/general/avatar-with-recipient.tsx similarity index 100% rename from apps/remix/app/components/(dashboard)/avatar/avatar-with-recipient.tsx rename to apps/remix/app/components/general/avatar-with-recipient.tsx diff --git a/apps/remix/app/components/ui/background.tsx b/apps/remix/app/components/general/background.tsx similarity index 100% rename from apps/remix/app/components/ui/background.tsx rename to apps/remix/app/components/general/background.tsx diff --git a/apps/remix/app/components/general/document-signing/document-signing-page-view.tsx b/apps/remix/app/components/general/document-signing/document-signing-page-view.tsx index 1d4807f69..1f7aa1817 100644 --- a/apps/remix/app/components/general/document-signing/document-signing-page-view.tsx +++ b/apps/remix/app/components/general/document-signing/document-signing-page-view.tsx @@ -20,7 +20,6 @@ import { Card, CardContent } from '@documenso/ui/primitives/card'; import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer'; -import { DocumentReadOnlyFields } from '~/components/document/document-read-only-fields'; import { DocumentSigningAutoSign } from '~/components/general/document-signing/document-signing-auto-sign'; import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field'; import { DocumentSigningDateField } from '~/components/general/document-signing/document-signing-date-field'; @@ -34,6 +33,7 @@ import { DocumentSigningRadioField } from '~/components/general/document-signing import { DocumentSigningRejectDialog } from '~/components/general/document-signing/document-signing-reject-dialog'; import { DocumentSigningSignatureField } from '~/components/general/document-signing/document-signing-signature-field'; import { DocumentSigningTextField } from '~/components/general/document-signing/document-signing-text-field'; +import { DocumentReadOnlyFields } from '~/components/general/document/document-read-only-fields'; export type SigningPageViewProps = { document: DocumentAndSender; diff --git a/apps/remix/app/components/document/document-history-sheet-changes.tsx b/apps/remix/app/components/general/document/document-history-sheet-changes.tsx similarity index 100% rename from apps/remix/app/components/document/document-history-sheet-changes.tsx rename to apps/remix/app/components/general/document/document-history-sheet-changes.tsx diff --git a/apps/remix/app/components/document/document-history-sheet.tsx b/apps/remix/app/components/general/document/document-history-sheet.tsx similarity index 100% rename from apps/remix/app/components/document/document-history-sheet.tsx rename to apps/remix/app/components/general/document/document-history-sheet.tsx diff --git a/apps/remix/app/components/general/document/document-page-view-dropdown.tsx b/apps/remix/app/components/general/document/document-page-view-dropdown.tsx index 7d54bdca5..ed06c4cb4 100644 --- a/apps/remix/app/components/general/document/document-page-view-dropdown.tsx +++ b/apps/remix/app/components/general/document/document-page-view-dropdown.tsx @@ -15,6 +15,7 @@ import { Trash2, } from 'lucide-react'; import { Link } from 'react-router'; +import { useNavigate } from 'react-router'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; import { useSession } from '@documenso/lib/client-only/providers/session'; @@ -33,7 +34,7 @@ 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 { DocumentRecipientLinkCopyDialog } from '~/components/general/document/document-recipient-link-copy-dialog'; import { useOptionalCurrentTeam } from '~/providers/team'; export type DocumentPageViewDropdownProps = { @@ -49,6 +50,7 @@ export const DocumentPageViewDropdown = ({ document }: DocumentPageViewDropdownP const { toast } = useToast(); const { _ } = useLingui(); + const navigate = useNavigate(); const team = useOptionalCurrentTeam(); const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false); @@ -186,6 +188,9 @@ export const DocumentPageViewDropdown = ({ document }: DocumentPageViewDropdownP open={isDeleteDialogOpen} canManageDocument={canManageDocument} onOpenChange={setDeleteDialogOpen} + onDelete={() => { + void navigate(documentsPath); + }} /> {isDuplicateDialogOpen && ( diff --git a/apps/remix/app/components/document/document-read-only-fields.tsx b/apps/remix/app/components/general/document/document-read-only-fields.tsx similarity index 100% rename from apps/remix/app/components/document/document-read-only-fields.tsx rename to apps/remix/app/components/general/document/document-read-only-fields.tsx diff --git a/apps/remix/app/components/document/document-recipient-link-copy-dialog.tsx b/apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx similarity index 100% rename from apps/remix/app/components/document/document-recipient-link-copy-dialog.tsx rename to apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx diff --git a/apps/remix/app/components/(dashboard)/document-search/document-search.tsx b/apps/remix/app/components/general/document/document-search.tsx similarity index 100% rename from apps/remix/app/components/(dashboard)/document-search/document-search.tsx rename to apps/remix/app/components/general/document/document-search.tsx diff --git a/apps/remix/app/components/formatter/document-status.tsx b/apps/remix/app/components/general/document/document-status.tsx similarity index 100% rename from apps/remix/app/components/formatter/document-status.tsx rename to apps/remix/app/components/general/document/document-status.tsx diff --git a/apps/remix/app/components/document/document-upload.tsx b/apps/remix/app/components/general/document/document-upload.tsx similarity index 94% rename from apps/remix/app/components/document/document-upload.tsx rename to apps/remix/app/components/general/document/document-upload.tsx index 93b9f5a1d..9e798dc0d 100644 --- a/apps/remix/app/components/document/document-upload.tsx +++ b/apps/remix/app/components/general/document/document-upload.tsx @@ -7,6 +7,7 @@ import { useNavigate } from 'react-router'; import { match } from 'ts-pattern'; import { useLimits } from '@documenso/ee/server-only/limits/provider/client'; +import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { useSession } from '@documenso/lib/client-only/providers/session'; import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app'; import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones'; @@ -24,18 +25,19 @@ export type DocumentUploadDropzoneProps = { }; export const DocumentUploadDropzone = ({ className }: DocumentUploadDropzoneProps) => { + const { _ } = useLingui(); + const { toast } = useToast(); + const { user } = useSession(); + const team = useOptionalCurrentTeam(); + const navigate = useNavigate(); + const analytics = useAnalytics(); const userTimezone = TIME_ZONES.find((timezone) => timezone === Intl.DateTimeFormat().resolvedOptions().timeZone) ?? DEFAULT_DOCUMENT_TIME_ZONE; - const { user } = useSession(); - - const { _ } = useLingui(); - const { toast } = useToast(); - const { quota, remaining, refreshLimits } = useLimits(); const [isLoading, setIsLoading] = useState(false); @@ -94,12 +96,11 @@ export const DocumentUploadDropzone = ({ className }: DocumentUploadDropzoneProp duration: 5000, }); - // Todo - // analytics.capture('App: Document Uploaded', { - // userId: session?.user.id, - // documentId: id, - // timestamp: new Date().toISOString(), - // }); + analytics.capture('App: Document Uploaded', { + userId: user.id, + documentId: id, + timestamp: new Date().toISOString(), + }); void navigate(`${formatDocumentsPath(team?.url)}/${id}/edit`); } catch (err) { diff --git a/apps/remix/app/components/(dashboard)/layout/menu-switcher.tsx b/apps/remix/app/components/general/menu-switcher.tsx similarity index 100% rename from apps/remix/app/components/(dashboard)/layout/menu-switcher.tsx rename to apps/remix/app/components/general/menu-switcher.tsx diff --git a/apps/remix/app/components/(dashboard)/metric-card/metric-card.tsx b/apps/remix/app/components/general/metric-card.tsx similarity index 100% rename from apps/remix/app/components/(dashboard)/metric-card/metric-card.tsx rename to apps/remix/app/components/general/metric-card.tsx diff --git a/apps/remix/app/components/(dashboard)/period-selector/period-selector.tsx b/apps/remix/app/components/general/period-selector.tsx similarity index 83% rename from apps/remix/app/components/(dashboard)/period-selector/period-selector.tsx rename to apps/remix/app/components/general/period-selector.tsx index 6dadfba21..4b08fd7a8 100644 --- a/apps/remix/app/components/(dashboard)/period-selector/period-selector.tsx +++ b/apps/remix/app/components/general/period-selector.tsx @@ -3,6 +3,7 @@ import { useMemo } from 'react'; import { Trans } from '@lingui/macro'; import { useLocation, useNavigate, useSearchParams } from 'react-router'; +import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents'; import { Select, SelectContent, @@ -11,7 +12,10 @@ import { SelectValue, } from '@documenso/ui/primitives/select'; -import { isPeriodSelectorValue } from './types'; +const isPeriodSelectorValue = (value: unknown): value is PeriodSelectorValue => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return ['', '7d', '14d', '30d'].includes(value as string); +}; export const PeriodSelector = () => { const { pathname } = useLocation(); diff --git a/apps/remix/app/components/general/refresh-on-focus.tsx b/apps/remix/app/components/general/refresh-on-focus.tsx new file mode 100644 index 000000000..775b722f3 --- /dev/null +++ b/apps/remix/app/components/general/refresh-on-focus.tsx @@ -0,0 +1,21 @@ +import { useCallback, useEffect } from 'react'; + +import { useRevalidator } from 'react-router'; + +export const RefreshOnFocus = () => { + const { revalidate } = useRevalidator(); + + const onFocus = useCallback(() => { + void revalidate(); + }, [revalidate]); + + useEffect(() => { + window.addEventListener('focus', onFocus); + + return () => { + window.removeEventListener('focus', onFocus); + }; + }, [onFocus]); + + return null; +}; diff --git a/apps/remix/app/components/(dashboard)/settings/layout/header.tsx b/apps/remix/app/components/general/settings-header.tsx similarity index 100% rename from apps/remix/app/components/(dashboard)/settings/layout/header.tsx rename to apps/remix/app/components/general/settings-header.tsx diff --git a/apps/remix/app/components/(dashboard)/settings/layout/desktop-nav.tsx b/apps/remix/app/components/general/settings-nav-desktop.tsx similarity index 78% rename from apps/remix/app/components/(dashboard)/settings/layout/desktop-nav.tsx rename to apps/remix/app/components/general/settings-nav-desktop.tsx index 5d55bf797..a7a227ed2 100644 --- a/apps/remix/app/components/(dashboard)/settings/layout/desktop-nav.tsx +++ b/apps/remix/app/components/general/settings-nav-desktop.tsx @@ -5,16 +5,16 @@ import { Braces, CreditCard, Globe2Icon, Lock, User, Users, Webhook } from 'luci import { useLocation } from 'react-router'; import { Link } from 'react-router'; +import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; -export type DesktopNavProps = HTMLAttributes; +export type SettingsDesktopNavProps = HTMLAttributes; -export const DesktopNav = ({ className, ...props }: DesktopNavProps) => { +export const SettingsDesktopNav = ({ className, ...props }: SettingsDesktopNavProps) => { const { pathname } = useLocation(); - const isBillingEnabled = false; // Todo getFlag('app_billing'); - const isPublicProfileEnabled = true; // Todo getFlag('app_public_profile'); + const isBillingEnabled = IS_BILLING_ENABLED(); return (
@@ -31,20 +31,18 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => { - {isPublicProfileEnabled && ( - - - - )} + + + - {isPublicProfileEnabled && ( - - - - )} + + +
- {currentTab === 'pending' ? : } + {currentTab === 'pending' ? ( + + ) : ( + + )}
); }; diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx index e4330366a..418162dca 100644 --- a/apps/remix/app/root.tsx +++ b/apps/remix/app/root.tsx @@ -22,6 +22,7 @@ import { TooltipProvider } from '@documenso/ui/primitives/tooltip'; import type { Route } from './+types/root'; import stylesheet from './app.css?url'; import { GenericErrorLayout } from './components/general/generic-error-layout'; +import { RefreshOnFocus } from './components/general/refresh-on-focus'; import { PostHogPageview } from './providers/posthog'; import { langCookie } from './storage/lang-cookie.server'; import { themeSessionResolver } from './storage/theme-session.server'; @@ -130,6 +131,9 @@ export function Layout({ children }: { children: React.ReactNode }) { + {/* Todo: Do we want this here? */} + +