This commit is contained in:
David Nguyen
2025-02-12 16:41:35 +11:00
parent 548d92c2fc
commit 15922d447b
70 changed files with 889 additions and 551 deletions

View File

@ -1,9 +1,9 @@
import { type HTMLAttributes, useEffect, useState } from 'react';
import type { User } from '@prisma/client';
import { MenuIcon, SearchIcon } from 'lucide-react';
import { Link, useLocation, useParams } from 'react-router';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import type { TGetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
import { getRootHref } from '@documenso/lib/utils/params';
import { cn } from '@documenso/ui/lib/utils';
@ -16,7 +16,7 @@ import { AppNavMobile } from './app-nav-mobile';
import { MenuSwitcher } from './menu-switcher';
export type HeaderProps = HTMLAttributes<HTMLDivElement> & {
user: User;
user: SessionUser;
teams: TGetTeamsResponse;
};

View File

@ -109,14 +109,12 @@ export const DocumentSigningAuth2FA = ({
)}
</p>
{user?.identityProvider === 'DOCUMENSO' && (
<p className="mt-2">
<Trans>
By enabling 2FA, you will be required to enter a code from your authenticator app
every time you sign in.
</Trans>
</p>
)}
<p className="mt-2">
<Trans>
By enabling 2FA, you will be required to enter a code from your authenticator app
every time you sign in using email password.
</Trans>
</p>
</AlertDescription>
</Alert>
<DialogFooter>

View File

@ -35,7 +35,7 @@ export const DocumentSigningAuthAccount = ({
setIsSigningOut(true);
await authClient.signOut({
redirectUrl: `/signin#email=${email}`,
redirectPath: `/signin#email=${email}`,
});
} catch {
setIsSigningOut(false);

View File

@ -27,7 +27,7 @@ export const DocumentSigningAuthPageView = ({
setIsSigningOut(true);
await authClient.signOut({
redirectUrl: emailHasAccount ? `/signin#email=${email}` : `/signup#email=${email}`,
redirectPath: emailHasAccount ? `/signin#email=${email}` : `/signup#email=${email}`,
});
} catch {
toast({

View File

@ -32,7 +32,7 @@ import {
SelectValue,
} from '@documenso/ui/primitives/select';
import { CreatePasskeyDialog } from '~/components/dialogs/create-passkey-dialog';
import { PasskeyCreateDialog } from '~/components/dialogs/passkey-create-dialog';
import { useRequiredDocumentSigningAuthContext } from './document-signing-auth-provider';
@ -190,7 +190,7 @@ export const DocumentSigningAuthPasskey = ({
<Trans>Cancel</Trans>
</Button>
<CreatePasskeyDialog
<PasskeyCreateDialog
onSuccess={async () => refetchPasskeys()}
trigger={
<Button>

View File

@ -1,8 +1,9 @@
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { type Document, FieldType, type Passkey, type Recipient, type User } from '@prisma/client';
import { type Document, FieldType, type Passkey, type Recipient } from '@prisma/client';
import { match } from 'ts-pattern';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { MAXIMUM_PASSKEYS } from '@documenso/lib/constants/auth';
import type {
TDocumentAuthOptions,
@ -40,7 +41,7 @@ export type DocumentSigningAuthContextValue = {
passkeyData: PasskeyData;
preferredPasskeyId: string | null;
setPreferredPasskeyId: (_value: string | null) => void;
user?: User | null;
user?: SessionUser | null;
refetchPasskeys: () => Promise<void>;
};
@ -63,7 +64,7 @@ export const useRequiredDocumentSigningAuthContext = () => {
export interface DocumentSigningAuthProviderProps {
documentAuthOptions: Document['authOptions'];
recipient: Recipient;
user?: User | null;
user?: SessionUser | null;
children: React.ReactNode;
}

View File

@ -3,10 +3,10 @@ import type { HTMLAttributes } from 'react';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import type { ExtendedDocumentStatus } from '@prisma/types/extended-document-status';
import { CheckCircle2, Clock, File } from 'lucide-react';
import type { LucideIcon } from 'lucide-react/dist/lucide-react';
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { SignatureIcon } from '@documenso/ui/icons/signature';
import { cn } from '@documenso/ui/lib/utils';

View File

@ -13,6 +13,7 @@ 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';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
@ -62,30 +63,11 @@ export const DocumentUploadDropzone = ({ className }: DocumentUploadDropzoneProp
try {
setIsLoading(true);
// Todo
// const { type, data } = await putPdfFile(file);
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/file', {
method: 'POST',
body: formData,
})
.then(async (res) => res.json())
.catch((e) => {
console.error('Upload failed:', e);
throw new AppError('UPLOAD_FAILED');
});
// const { id: documentDataId } = await createDocumentData({
// type,
// data,
// });
const response = await putPdfFile(file);
const { id } = await createDocument({
title: file.name,
documentDataId: response.id, // todo
documentDataId: response.id,
timezone: userTimezone,
});

View File

@ -3,12 +3,12 @@ import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { User } from '@prisma/client';
import { motion } from 'framer-motion';
import { CheckCircle2, ChevronsUpDown, Plus, Settings2 } from 'lucide-react';
import { Link, useLocation } from 'react-router';
import { authClient } from '@documenso/auth/client';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { TEAM_MEMBER_ROLE_MAP, TEAM_URL_REGEX } from '@documenso/lib/constants/teams';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import type { TGetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
@ -32,7 +32,7 @@ import {
const MotionLink = motion(Link);
export type MenuSwitcherProps = {
user: User;
user: SessionUser;
teams: TGetTeamsResponse;
};

View File

@ -1,55 +0,0 @@
import { useCallback, useEffect, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { ClaimPublicProfileDialogForm } from '~/components/forms/public-profile-claim-dialog';
export const UpcomingProfileClaimTeaser = () => {
const { user } = useSession();
const { _ } = useLingui();
const { toast } = useToast();
const [open, setOpen] = useState(false);
const [claimed, setClaimed] = useState(false);
const onOpenChange = useCallback(
(open: boolean) => {
if (!open && !claimed) {
toast({
title: _(msg`Claim your profile later`),
description: _(
msg`You can claim your profile later on by going to your profile settings!`,
),
});
}
setOpen(open);
localStorage.setItem('app.hasShownProfileClaimDialog', 'true');
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[claimed, toast],
);
useEffect(() => {
const hasShownProfileClaimDialog =
localStorage.getItem('app.hasShownProfileClaimDialog') === 'true';
if (!user.url && !hasShownProfileClaimDialog) {
onOpenChange(true);
}
}, [onOpenChange, user.url]);
return (
<ClaimPublicProfileDialogForm
open={open}
onOpenChange={onOpenChange}
onClaimed={() => setClaimed(true)}
user={user}
/>
);
};

View File

@ -5,8 +5,8 @@ import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { AlertTriangle } from 'lucide-react';
import { authClient } from '@documenso/auth/client';
import { ONE_DAY, ONE_SECOND } from '@documenso/lib/constants/time';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import {
Dialog,
@ -27,18 +27,20 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
const { toast } = useToast();
const [isOpen, setIsOpen] = useState(false);
const [isPending, setIsPending] = useState(false);
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
// Todo
const { mutateAsync: sendConfirmationEmail, isPending } =
trpc.profile.sendConfirmationEmail.useMutation();
const onResendConfirmationEmail = async () => {
if (isPending) {
return;
}
setIsPending(true);
try {
setIsButtonDisabled(true);
await sendConfirmationEmail({ email: email });
await authClient.emailPassword.resendVerifyEmail({ email: email });
toast({
title: _(msg`Success`),
@ -56,6 +58,8 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
variant: 'destructive',
});
}
setIsPending(false);
};
useEffect(() => {