This commit is contained in:
Gautam-Hegde
2024-01-24 12:08:21 +05:30
16 changed files with 170 additions and 49 deletions

View File

@ -33,3 +33,4 @@ body:
- label: I have explained the use case or scenario for this feature. - label: I have explained the use case or scenario for this feature.
- label: I have included any relevant technical details or design suggestions. - label: I have included any relevant technical details or design suggestions.
- label: I understand that this is a suggestion and that there is no guarantee of implementation. - label: I understand that this is a suggestion and that there is no guarantee of implementation.
- label: I want to work on creating a PR for this issue if approved

View File

@ -47,6 +47,14 @@ export const TEAM_MEMBERS = [
engagement: 'Full-Time', engagement: 'Full-Time',
joinDate: 'October 9th, 2023', joinDate: 'October 9th, 2023',
}, },
{
name: 'Adithya Krishna',
role: 'Software Engineer - II',
salary: '-',
location: 'India',
engagement: 'Full-Time',
joinDate: 'December 1st, 2023',
},
]; ];
export const FUNDING_RAISED = [ export const FUNDING_RAISED = [

View File

@ -1,3 +1,4 @@
import { IDENTITY_PROVIDER_NAME } from '@documenso/lib/constants/auth';
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { AuthenticatorApp } from '~/components/forms/2fa/authenticator-app'; import { AuthenticatorApp } from '~/components/forms/2fa/authenticator-app';
@ -17,28 +18,43 @@ export default async function SecuritySettingsPage() {
<hr className="my-4" /> <hr className="my-4" />
<PasswordForm user={user} className="max-w-xl" /> {user.identityProvider === 'DOCUMENSO' ? (
<div>
<PasswordForm user={user} className="max-w-xl" />
<hr className="mb-4 mt-8" /> <hr className="mb-4 mt-8" />
<h4 className="text-lg font-medium">Two Factor Authentication</h4> <h4 className="text-lg font-medium">Two Factor Authentication</h4>
<p className="text-muted-foreground mt-2 text-sm"> <p className="text-muted-foreground mt-2 text-sm">
Add and manage your two factor security settings to add an extra layer of security to your Add and manage your two factor security settings to add an extra layer of security to
account! your account!
</p> </p>
<div className="mt-4 max-w-xl"> <div className="mt-4 max-w-xl">
<h5 className="font-medium">Two-factor methods</h5> <h5 className="font-medium">Two-factor methods</h5>
<AuthenticatorApp isTwoFactorEnabled={user.twoFactorEnabled} /> <AuthenticatorApp isTwoFactorEnabled={user.twoFactorEnabled} />
</div> </div>
{user.twoFactorEnabled && ( {user.twoFactorEnabled && (
<div className="mt-4 max-w-xl"> <div className="mt-4 max-w-xl">
<h5 className="font-medium">Recovery methods</h5> <h5 className="font-medium">Recovery methods</h5>
<RecoveryCodes isTwoFactorEnabled={user.twoFactorEnabled} /> <RecoveryCodes isTwoFactorEnabled={user.twoFactorEnabled} />
</div>
)}
</div>
) : (
<div>
<h4 className="text-lg font-medium">
Your account is managed by {IDENTITY_PROVIDER_NAME[user.identityProvider]}
</h4>
<p className="text-muted-foreground mt-2 text-sm">
To update your password, enable two-factor authentication, and manage other security
settings, please go to your {IDENTITY_PROVIDER_NAME[user.identityProvider]} account
settings.
</p>
</div> </div>
)} )}
</div> </div>

View File

@ -0,0 +1,39 @@
'use client';
import { useState } from 'react';
import { FileSearch } from 'lucide-react';
import type { DocumentData } from '@documenso/prisma/client';
import DocumentDialog from '@documenso/ui/components/document/document-dialog';
import type { ButtonProps } from '@documenso/ui/primitives/button';
import { Button } from '@documenso/ui/primitives/button';
export type DocumentPreviewButtonProps = {
className?: string;
documentData: DocumentData;
} & ButtonProps;
export const DocumentPreviewButton = ({
className,
documentData,
...props
}: DocumentPreviewButtonProps) => {
const [showDialog, setShowDialog] = useState(false);
return (
<>
<Button
className={className}
variant="outline"
onClick={() => setShowDialog((visible) => !visible)}
{...props}
>
<FileSearch className="mr-2 h-5 w-5" strokeWidth={1.7} />
View Document
</Button>
<DocumentDialog documentData={documentData} open={showDialog} onOpenChange={setShowDialog} />
</>
);
};

View File

@ -0,0 +1,17 @@
import React from 'react';
import { RefreshOnFocus } from '~/components/(dashboard)/refresh-on-focus/refresh-on-focus';
export type SigningLayoutProps = {
children: React.ReactNode;
};
export default function SigningLayout({ children }: SigningLayoutProps) {
return (
<div>
{children}
<RefreshOnFocus />
</div>
);
}

View File

@ -17,6 +17,8 @@ import { SigningCard3D } from '@documenso/ui/components/signing-card';
import { truncateTitle } from '~/helpers/truncate-title'; import { truncateTitle } from '~/helpers/truncate-title';
import { DocumentPreviewButton } from './document-preview-button';
export type CompletedSigningPageProps = { export type CompletedSigningPageProps = {
params: { params: {
token?: string; token?: string;
@ -117,12 +119,20 @@ export default async function CompletedSigningPage({
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4"> <div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
<DocumentShareButton documentId={document.id} token={recipient.token} /> <DocumentShareButton documentId={document.id} token={recipient.token} />
<DocumentDownloadButton {document.status === DocumentStatus.COMPLETED ? (
className="flex-1" <DocumentDownloadButton
fileName={document.title} className="flex-1"
documentData={documentData} fileName={document.title}
disabled={document.status !== DocumentStatus.COMPLETED} documentData={documentData}
/> disabled={document.status !== DocumentStatus.COMPLETED}
/>
) : (
<DocumentPreviewButton
className="flex-1"
title="Signatures will appear once the document has been completed"
documentData={documentData}
/>
)}
</div> </div>
{isLoggedIn ? ( {isLoggedIn ? (

View File

@ -1,5 +1,7 @@
import Link from 'next/link'; import Link from 'next/link';
import { IS_GOOGLE_SSO_ENABLED } from '@documenso/lib/constants/auth';
import { SignInForm } from '~/components/forms/signin'; import { SignInForm } from '~/components/forms/signin';
export default function SignInPage() { export default function SignInPage() {
@ -11,7 +13,7 @@ export default function SignInPage() {
Welcome back, we are lucky to have you. Welcome back, we are lucky to have you.
</p> </p>
<SignInForm className="mt-4" /> <SignInForm className="mt-4" isGoogleSSOEnabled={IS_GOOGLE_SSO_ENABLED} />
{process.env.NEXT_PUBLIC_DISABLE_SIGNUP !== 'true' && ( {process.env.NEXT_PUBLIC_DISABLE_SIGNUP !== 'true' && (
<p className="text-muted-foreground mt-6 text-center text-sm"> <p className="text-muted-foreground mt-6 text-center text-sm">

View File

@ -252,7 +252,11 @@ const ThemeCommands = ({ setTheme }: { setTheme: (_theme: string) => void }) =>
); );
return THEMES.map((theme) => ( return THEMES.map((theme) => (
<CommandItem key={theme.theme} onSelect={() => setTheme(theme.theme)}> <CommandItem
key={theme.theme}
onSelect={() => setTheme(theme.theme)}
className="mx-2 first:mt-2 last:mb-2"
>
<theme.icon className="mr-2" /> <theme.icon className="mr-2" />
{theme.label} {theme.label}
</CommandItem> </CommandItem>

View File

@ -141,7 +141,11 @@ export const ProfileDropdown = ({ user }: ProfileDropdownProps) => {
</DropdownMenuSub> </DropdownMenuSub>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href="https://github.com/documenso/documenso" className="cursor-pointer"> <Link
href="https://github.com/documenso/documenso"
className="cursor-pointer"
target="_blank"
>
<LuGithub className="mr-2 h-4 w-4" /> <LuGithub className="mr-2 h-4 w-4" />
Star on Github Star on Github
</Link> </Link>

View File

@ -112,7 +112,6 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
</Label> </Label>
<Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled /> <Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled />
</div> </div>
<FormField <FormField
control={form.control} control={form.control}
name="signature" name="signature"
@ -122,7 +121,10 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
<FormControl> <FormControl>
<SignaturePad <SignaturePad
className="h-44 w-full" className="h-44 w-full"
containerClassName="rounded-lg border bg-background" containerClassName={cn(
'rounded-lg border bg-background',
isSubmitting ? 'pointer-events-none opacity-50' : null,
)}
defaultValue={user.signature ?? undefined} defaultValue={user.signature ?? undefined}
onChange={(v) => onChange(v ?? '')} onChange={(v) => onChange(v ?? '')}
/> />

View File

@ -48,9 +48,10 @@ export type TSignInFormSchema = z.infer<typeof ZSignInFormSchema>;
export type SignInFormProps = { export type SignInFormProps = {
className?: string; className?: string;
isGoogleSSOEnabled?: boolean;
}; };
export const SignInForm = ({ className }: SignInFormProps) => { export const SignInForm = ({ className, isGoogleSSOEnabled }: SignInFormProps) => {
const { toast } = useToast(); const { toast } = useToast();
const [isTwoFactorAuthenticationDialogOpen, setIsTwoFactorAuthenticationDialogOpen] = const [isTwoFactorAuthenticationDialogOpen, setIsTwoFactorAuthenticationDialogOpen] =
useState(false); useState(false);
@ -203,24 +204,29 @@ export const SignInForm = ({ className }: SignInFormProps) => {
{isSubmitting ? 'Signing in...' : 'Sign In'} {isSubmitting ? 'Signing in...' : 'Sign In'}
</Button> </Button>
<div className="relative flex items-center justify-center gap-x-4 py-2 text-xs uppercase"> {isGoogleSSOEnabled && (
<div className="bg-border h-px flex-1" /> <>
<span className="text-muted-foreground bg-transparent">Or continue with</span> <div className="relative flex items-center justify-center gap-x-4 py-2 text-xs uppercase">
<div className="bg-border h-px flex-1" /> <div className="bg-border h-px flex-1" />
</div> <span className="text-muted-foreground bg-transparent">Or continue with</span>
<div className="bg-border h-px flex-1" />
</div>
<Button <Button
type="button" type="button"
size="lg" size="lg"
variant={'outline'} variant="outline"
className="bg-background text-muted-foreground border" className="bg-background text-muted-foreground border"
disabled={isSubmitting} disabled={isSubmitting}
onClick={onSignInWithGoogleClick} onClick={onSignInWithGoogleClick}
> >
<FcGoogle className="mr-2 h-5 w-5" /> <FcGoogle className="mr-2 h-5 w-5" />
Google Google
</Button> </Button>
</>
)}
</form> </form>
<Dialog <Dialog
open={isTwoFactorAuthenticationDialogOpen} open={isTwoFactorAuthenticationDialogOpen}
onOpenChange={onCloseTwoFactorAuthenticationDialog} onOpenChange={onCloseTwoFactorAuthenticationDialog}

View File

@ -23,7 +23,8 @@ services:
- database - database
- inbucket - inbucket
environment: environment:
- DATABASE_URL=postgres://documenso:password@database:5432/documenso - NEXT_PRIVATE_DATABASE_URL=postgres://documenso:password@database:5432/documenso
- NEXT_PRIVATE_DIRECT_DATABASE_URL=postgres://documenso:password@database:5432/documenso
- NEXT_PUBLIC_WEBAPP_URL=http://localhost:3000 - NEXT_PUBLIC_WEBAPP_URL=http://localhost:3000
- NEXTAUTH_SECRET=my-super-secure-secret - NEXTAUTH_SECRET=my-super-secure-secret
- NEXTAUTH_URL=http://localhost:3000 - NEXTAUTH_URL=http://localhost:3000

View File

@ -10,7 +10,7 @@ export const TemplateFooter = ({ isDocument = true }: TemplateFooterProps) => {
{isDocument && ( {isDocument && (
<Text className="my-4 text-base text-slate-400"> <Text className="my-4 text-base text-slate-400">
This document was sent using{' '} This document was sent using{' '}
<Link className="text-[#7AC455]" href="https://documenso.com"> <Link className="text-[#7AC455]" href="https://documen.so/mail-footer">
Documenso. Documenso.
</Link> </Link>
</Text> </Text>

View File

@ -1 +1,12 @@
import { IdentityProvider } from '@documenso/prisma/client';
export const SALT_ROUNDS = 12; export const SALT_ROUNDS = 12;
export const IDENTITY_PROVIDER_NAME: { [key in IdentityProvider]: string } = {
[IdentityProvider.DOCUMENSO]: 'Documenso',
[IdentityProvider.GOOGLE]: 'Google',
};
export const IS_GOOGLE_SSO_ENABLED = Boolean(
process.env.NEXT_PRIVATE_GOOGLE_CLIENT_ID && process.env.NEXT_PRIVATE_GOOGLE_CLIENT_SECRET,
);

View File

@ -5,7 +5,7 @@ import { useState } from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog'; import * as DialogPrimitive from '@radix-ui/react-dialog';
import { X } from 'lucide-react'; import { X } from 'lucide-react';
import { DocumentData } from '@documenso/prisma/client'; import type { DocumentData } from '@documenso/prisma/client';
import { cn } from '../../lib/utils'; import { cn } from '../../lib/utils';
import { Dialog, DialogOverlay, DialogPortal } from '../../primitives/dialog'; import { Dialog, DialogOverlay, DialogPortal } from '../../primitives/dialog';

View File

@ -35,7 +35,7 @@ const CommandDialog = ({ children, commandProps, ...props }: CommandDialogProps)
<DialogContent className="overflow-hidden p-0 shadow-2xl"> <DialogContent className="overflow-hidden p-0 shadow-2xl">
<Command <Command
{...commandProps} {...commandProps}
className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-4 [&_[cmdk-item]_svg]:w-4" className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-0 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-4 [&_[cmdk-item]_svg]:w-4"
> >
{children} {children}
</Command> </Command>
@ -92,7 +92,7 @@ const CommandGroup = React.forwardRef<
<CommandPrimitive.Group <CommandPrimitive.Group
ref={ref} ref={ref}
className={cn( className={cn(
'text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium', 'text-foreground [&_[cmdk-group-heading]]:text-muted-foreground mx-2 overflow-hidden border-b pb-2 last:border-0 [&_[cmdk-group-heading]]:mt-2 [&_[cmdk-group-heading]]:px-0 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-normal [&_[cmdk-group-heading]]:opacity-50 ',
className, className,
)} )}
{...props} {...props}
@ -124,7 +124,7 @@ const CommandItem = React.forwardRef<
<CommandPrimitive.Item <CommandPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50', 'aria-selected:bg-accent aria-selected:text-accent-foreground relative -mx-2 -my-1 flex cursor-default select-none items-center rounded-lg px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className, className,
)} )}
{...props} {...props}