mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 08:42:12 +10:00
refactor: useContext & remove enum
This commit is contained in:
@ -4,6 +4,7 @@ import { useState } from 'react';
|
|||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import { DocumentStatus } from '@documenso/prisma/client';
|
||||||
import type { DocumentData, Field, Recipient, User } from '@documenso/prisma/client';
|
import type { DocumentData, Field, Recipient, User } from '@documenso/prisma/client';
|
||||||
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
@ -35,12 +36,8 @@ export type EditDocumentFormProps = {
|
|||||||
documentData: DocumentData;
|
documentData: DocumentData;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum EditDocumentStepEnum {
|
type EditDocumentStep = 'title' | 'signers' | 'fields' | 'subject';
|
||||||
TITLE,
|
const EditDocumentSteps: EditDocumentStep[] = ['title', 'signers', 'fields', 'subject'];
|
||||||
SIGNERS,
|
|
||||||
FIELDS,
|
|
||||||
SUBJECT,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditDocumentForm = ({
|
export const EditDocumentForm = ({
|
||||||
className,
|
className,
|
||||||
@ -54,34 +51,37 @@ export const EditDocumentForm = ({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// controlled stepper state
|
// controlled stepper state
|
||||||
const [stepIdx, setStepIdx] = useState<EditDocumentStepEnum>(0);
|
const [step, setStep] = useState<EditDocumentStep>(
|
||||||
|
document.status === DocumentStatus.DRAFT ? 'title' : 'signers',
|
||||||
|
);
|
||||||
|
|
||||||
const { mutateAsync: addTitle } = trpc.document.setTitleForDocument.useMutation();
|
const { mutateAsync: addTitle } = trpc.document.setTitleForDocument.useMutation();
|
||||||
const { mutateAsync: addFields } = trpc.field.addFields.useMutation();
|
const { mutateAsync: addFields } = trpc.field.addFields.useMutation();
|
||||||
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation();
|
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation();
|
||||||
const { mutateAsync: sendDocument } = trpc.document.sendDocument.useMutation();
|
const { mutateAsync: sendDocument } = trpc.document.sendDocument.useMutation();
|
||||||
|
|
||||||
// controlled stepper next
|
const documentFlow: Record<EditDocumentStep, DocumentFlowStep> = {
|
||||||
const nextStep = () => setStepIdx(stepIdx + 1);
|
title: {
|
||||||
|
|
||||||
const documentFlow: DocumentFlowStep[] = [
|
|
||||||
{
|
|
||||||
title: 'Add Title',
|
title: 'Add Title',
|
||||||
description: 'Add the title to the document.',
|
description: 'Add the title to the document.',
|
||||||
|
stepIndex: 1,
|
||||||
},
|
},
|
||||||
{
|
signers: {
|
||||||
title: 'Add Signers',
|
title: 'Add Signers',
|
||||||
description: 'Add the people who will sign the document.',
|
description: 'Add the people who will sign the document.',
|
||||||
|
stepIndex: 2,
|
||||||
},
|
},
|
||||||
{
|
fields: {
|
||||||
title: 'Add Fields',
|
title: 'Add Fields',
|
||||||
description: 'Add all relevant fields for each recipient.',
|
description: 'Add all relevant fields for each recipient.',
|
||||||
|
stepIndex: 3,
|
||||||
},
|
},
|
||||||
{
|
subject: {
|
||||||
title: 'Add Subject',
|
title: 'Add Subject',
|
||||||
description: 'Add the subject and message you wish to send to signers.',
|
description: 'Add the subject and message you wish to send to signers.',
|
||||||
|
stepIndex: 4,
|
||||||
},
|
},
|
||||||
];
|
};
|
||||||
|
|
||||||
const onAddTitleFormSubmit = async (data: TAddTitleFormSchema) => {
|
const onAddTitleFormSubmit = async (data: TAddTitleFormSchema) => {
|
||||||
try {
|
try {
|
||||||
@ -93,7 +93,7 @@ export const EditDocumentForm = ({
|
|||||||
|
|
||||||
router.refresh();
|
router.refresh();
|
||||||
|
|
||||||
nextStep();
|
setStep('signers');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ export const EditDocumentForm = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.refresh();
|
router.refresh();
|
||||||
nextStep();
|
setStep('fields');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ export const EditDocumentForm = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.refresh();
|
router.refresh();
|
||||||
nextStep();
|
setStep('subject');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ export const EditDocumentForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentDocumentFlow = documentFlow[stepIdx];
|
const currentDocumentFlow = documentFlow[step];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('grid w-full grid-cols-12 gap-8', className)}>
|
<div className={cn('grid w-full grid-cols-12 gap-8', className)}>
|
||||||
@ -199,10 +199,13 @@ export const EditDocumentForm = ({
|
|||||||
title={currentDocumentFlow.title}
|
title={currentDocumentFlow.title}
|
||||||
description={currentDocumentFlow.description}
|
description={currentDocumentFlow.description}
|
||||||
/>
|
/>
|
||||||
<Stepper currentStep={stepIdx + 1} setCurrentStep={(step) => setStepIdx(step - 1)}>
|
<Stepper
|
||||||
|
currentStep={currentDocumentFlow.stepIndex}
|
||||||
|
setCurrentStep={(step) => setStep(EditDocumentSteps[step - 1])}
|
||||||
|
>
|
||||||
<AddTitleFormPartial
|
<AddTitleFormPartial
|
||||||
key={recipients.length}
|
key={recipients.length}
|
||||||
documentFlow={documentFlow[EditDocumentStepEnum.TITLE]}
|
documentFlow={documentFlow.title}
|
||||||
recipients={recipients}
|
recipients={recipients}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
document={document}
|
document={document}
|
||||||
@ -210,7 +213,7 @@ export const EditDocumentForm = ({
|
|||||||
/>
|
/>
|
||||||
<AddSignersFormPartial
|
<AddSignersFormPartial
|
||||||
key={recipients.length}
|
key={recipients.length}
|
||||||
documentFlow={documentFlow[EditDocumentStepEnum.SIGNERS]}
|
documentFlow={documentFlow.signers}
|
||||||
document={document}
|
document={document}
|
||||||
recipients={recipients}
|
recipients={recipients}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
@ -218,14 +221,14 @@ export const EditDocumentForm = ({
|
|||||||
/>
|
/>
|
||||||
<AddFieldsFormPartial
|
<AddFieldsFormPartial
|
||||||
key={fields.length}
|
key={fields.length}
|
||||||
documentFlow={documentFlow[EditDocumentStepEnum.FIELDS]}
|
documentFlow={documentFlow.fields}
|
||||||
recipients={recipients}
|
recipients={recipients}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
onSubmit={onAddFieldsFormSubmit}
|
onSubmit={onAddFieldsFormSubmit}
|
||||||
/>
|
/>
|
||||||
<AddSubjectFormPartial
|
<AddSubjectFormPartial
|
||||||
key={recipients.length}
|
key={recipients.length}
|
||||||
documentFlow={documentFlow[EditDocumentStepEnum.SUBJECT]}
|
documentFlow={documentFlow.subject}
|
||||||
document={document}
|
document={document}
|
||||||
recipients={recipients}
|
recipients={recipients}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import {
|
|||||||
import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
|
||||||
|
|
||||||
import type { WithStep } from '../stepper';
|
import { useStep } from '../stepper';
|
||||||
import type { TAddFieldsFormSchema } from './add-fields.types';
|
import type { TAddFieldsFormSchema } from './add-fields.types';
|
||||||
import {
|
import {
|
||||||
DocumentFlowFormContainerActions,
|
DocumentFlowFormContainerActions,
|
||||||
@ -65,10 +65,9 @@ export const AddFieldsFormPartial = ({
|
|||||||
recipients,
|
recipients,
|
||||||
fields,
|
fields,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
useStep, // Stepper
|
}: AddFieldsFormProps) => {
|
||||||
}: WithStep<AddFieldsFormProps>) => {
|
|
||||||
const { isWithinPageBounds, getFieldPosition, getPage } = useDocumentElement();
|
const { isWithinPageBounds, getFieldPosition, getPage } = useDocumentElement();
|
||||||
const { currentStep, totalSteps, nextStep, previousStep } = useStep();
|
const { currentStep, totalSteps, previousStep } = useStep();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { Input } from '@documenso/ui/primitives/input';
|
|||||||
import { Label } from '@documenso/ui/primitives/label';
|
import { Label } from '@documenso/ui/primitives/label';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
import type { WithStep } from '../stepper';
|
import { useStep } from '../stepper';
|
||||||
import type { TAddSignersFormSchema } from './add-signers.types';
|
import type { TAddSignersFormSchema } from './add-signers.types';
|
||||||
import { ZAddSignersFormSchema } from './add-signers.types';
|
import { ZAddSignersFormSchema } from './add-signers.types';
|
||||||
import {
|
import {
|
||||||
@ -43,14 +43,13 @@ export const AddSignersFormPartial = ({
|
|||||||
document,
|
document,
|
||||||
fields: _fields,
|
fields: _fields,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
useStep, // Stepper
|
}: AddSignersFormProps) => {
|
||||||
}: WithStep<AddSignersFormProps>) => {
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { remaining } = useLimits();
|
const { remaining } = useLimits();
|
||||||
|
|
||||||
const initialId = useId();
|
const initialId = useId();
|
||||||
|
|
||||||
const { currentStep, totalSteps, nextStep, previousStep } = useStep();
|
const { currentStep, totalSteps, previousStep } = useStep();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { Input } from '@documenso/ui/primitives/input';
|
|||||||
import { Label } from '@documenso/ui/primitives/label';
|
import { Label } from '@documenso/ui/primitives/label';
|
||||||
import { Textarea } from '@documenso/ui/primitives/textarea';
|
import { Textarea } from '@documenso/ui/primitives/textarea';
|
||||||
|
|
||||||
import type { WithStep } from '../stepper';
|
import { useStep } from '../stepper';
|
||||||
import type { TAddSubjectFormSchema } from './add-subject.types';
|
import type { TAddSubjectFormSchema } from './add-subject.types';
|
||||||
import {
|
import {
|
||||||
DocumentFlowFormContainerActions,
|
DocumentFlowFormContainerActions,
|
||||||
@ -34,8 +34,7 @@ export const AddSubjectFormPartial = ({
|
|||||||
fields: _fields,
|
fields: _fields,
|
||||||
document,
|
document,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
useStep,
|
}: AddSubjectFormProps) => {
|
||||||
}: WithStep<AddSubjectFormProps>) => {
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -50,7 +49,7 @@ export const AddSubjectFormPartial = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onFormSubmit = handleSubmit(onSubmit);
|
const onFormSubmit = handleSubmit(onSubmit);
|
||||||
const { currentStep, totalSteps, nextStep, previousStep } = useStep();
|
const { currentStep, totalSteps, previousStep } = useStep();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-messa
|
|||||||
import { Input } from '@documenso/ui/primitives/input';
|
import { Input } from '@documenso/ui/primitives/input';
|
||||||
import { Label } from '@documenso/ui/primitives/label';
|
import { Label } from '@documenso/ui/primitives/label';
|
||||||
|
|
||||||
import type { WithStep } from '../stepper';
|
import { useStep } from '../stepper';
|
||||||
import type { TAddTitleFormSchema } from './add-title.types';
|
import type { TAddTitleFormSchema } from './add-title.types';
|
||||||
import {
|
import {
|
||||||
DocumentFlowFormContainerActions,
|
DocumentFlowFormContainerActions,
|
||||||
@ -32,8 +32,7 @@ export const AddTitleFormPartial = ({
|
|||||||
fields: _fields,
|
fields: _fields,
|
||||||
document,
|
document,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
useStep,
|
}: AddTitleFormProps) => {
|
||||||
}: WithStep<AddTitleFormProps>) => {
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
type StepProps = {
|
type StepContextType = {
|
||||||
readonly useStep: () => {
|
|
||||||
stepIndex: number;
|
stepIndex: number;
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
totalSteps: number;
|
totalSteps: number;
|
||||||
@ -10,17 +9,16 @@ type StepProps = {
|
|||||||
isLast: boolean;
|
isLast: boolean;
|
||||||
nextStep: () => void;
|
nextStep: () => void;
|
||||||
previousStep: () => void;
|
previousStep: () => void;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WithStep<T> = T & StepProps;
|
const StepContext = createContext<StepContextType | null>(null);
|
||||||
|
|
||||||
type StepperProps = {
|
type StepperProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
onComplete?: () => void;
|
onComplete?: () => void;
|
||||||
onStepChanged?: (currentStep: number) => void;
|
onStepChanged?: (currentStep: number) => void;
|
||||||
currentStep?: number;
|
currentStep?: number; // external control prop
|
||||||
setCurrentStep?: (step: number) => void;
|
setCurrentStep?: (step: number) => void; // external control function
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Stepper: FC<StepperProps> = ({
|
export const Stepper: FC<StepperProps> = ({
|
||||||
@ -57,7 +55,12 @@ export const Stepper: FC<StepperProps> = ({
|
|||||||
onStepChanged && onStepChanged(currentStep);
|
onStepChanged && onStepChanged(currentStep);
|
||||||
}, [currentStep, onStepChanged]);
|
}, [currentStep, onStepChanged]);
|
||||||
|
|
||||||
const useStep = () => ({
|
// Empty stepper
|
||||||
|
if (totalSteps === 0) return null;
|
||||||
|
|
||||||
|
const currentChild = React.Children.toArray(children)[currentStep - 1];
|
||||||
|
|
||||||
|
const stepContextValue: StepContextType = {
|
||||||
stepIndex: currentStep - 1,
|
stepIndex: currentStep - 1,
|
||||||
currentStep,
|
currentStep,
|
||||||
totalSteps,
|
totalSteps,
|
||||||
@ -65,15 +68,14 @@ export const Stepper: FC<StepperProps> = ({
|
|||||||
isLast: currentStep === totalSteps,
|
isLast: currentStep === totalSteps,
|
||||||
nextStep,
|
nextStep,
|
||||||
previousStep,
|
previousStep,
|
||||||
});
|
};
|
||||||
|
|
||||||
// empty stepper
|
return <StepContext.Provider value={stepContextValue}>{currentChild}</StepContext.Provider>;
|
||||||
if (totalSteps === 0) return null;
|
};
|
||||||
|
|
||||||
const currentChild = React.Children.toArray(children)[currentStep - 1];
|
/** Hook for children to use the step context */
|
||||||
|
export const useStep = (): StepContextType => {
|
||||||
// type validation
|
const context = useContext(StepContext);
|
||||||
if (!React.isValidElement<StepProps>(currentChild)) return null;
|
if (!context) throw new Error('useStep must be used within a Stepper');
|
||||||
|
return context;
|
||||||
return <>{React.cloneElement(currentChild, { useStep })}</>;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user