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? */} + +