refactor: useContext & remove enum

This commit is contained in:
mikezzb
2023-12-03 01:15:59 -05:00
parent eccf63dcfd
commit 40a4ec4436
6 changed files with 66 additions and 65 deletions

View File

@ -4,6 +4,7 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { DocumentStatus } 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 { trpc } from '@documenso/trpc/react';
@ -35,12 +36,8 @@ export type EditDocumentFormProps = {
documentData: DocumentData;
};
enum EditDocumentStepEnum {
TITLE,
SIGNERS,
FIELDS,
SUBJECT,
}
type EditDocumentStep = 'title' | 'signers' | 'fields' | 'subject';
const EditDocumentSteps: EditDocumentStep[] = ['title', 'signers', 'fields', 'subject'];
export const EditDocumentForm = ({
className,
@ -54,34 +51,37 @@ export const EditDocumentForm = ({
const router = useRouter();
// 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: addFields } = trpc.field.addFields.useMutation();
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation();
const { mutateAsync: sendDocument } = trpc.document.sendDocument.useMutation();
// controlled stepper next
const nextStep = () => setStepIdx(stepIdx + 1);
const documentFlow: DocumentFlowStep[] = [
{
const documentFlow: Record<EditDocumentStep, DocumentFlowStep> = {
title: {
title: 'Add Title',
description: 'Add the title to the document.',
stepIndex: 1,
},
{
signers: {
title: 'Add Signers',
description: 'Add the people who will sign the document.',
stepIndex: 2,
},
{
fields: {
title: 'Add Fields',
description: 'Add all relevant fields for each recipient.',
stepIndex: 3,
},
{
subject: {
title: 'Add Subject',
description: 'Add the subject and message you wish to send to signers.',
stepIndex: 4,
},
];
};
const onAddTitleFormSubmit = async (data: TAddTitleFormSchema) => {
try {
@ -93,7 +93,7 @@ export const EditDocumentForm = ({
router.refresh();
nextStep();
setStep('signers');
} catch (err) {
console.error(err);
@ -114,7 +114,7 @@ export const EditDocumentForm = ({
});
router.refresh();
nextStep();
setStep('fields');
} catch (err) {
console.error(err);
@ -135,7 +135,7 @@ export const EditDocumentForm = ({
});
router.refresh();
nextStep();
setStep('subject');
} catch (err) {
console.error(err);
@ -177,7 +177,7 @@ export const EditDocumentForm = ({
}
};
const currentDocumentFlow = documentFlow[stepIdx];
const currentDocumentFlow = documentFlow[step];
return (
<div className={cn('grid w-full grid-cols-12 gap-8', className)}>
@ -199,10 +199,13 @@ export const EditDocumentForm = ({
title={currentDocumentFlow.title}
description={currentDocumentFlow.description}
/>
<Stepper currentStep={stepIdx + 1} setCurrentStep={(step) => setStepIdx(step - 1)}>
<Stepper
currentStep={currentDocumentFlow.stepIndex}
setCurrentStep={(step) => setStep(EditDocumentSteps[step - 1])}
>
<AddTitleFormPartial
key={recipients.length}
documentFlow={documentFlow[EditDocumentStepEnum.TITLE]}
documentFlow={documentFlow.title}
recipients={recipients}
fields={fields}
document={document}
@ -210,7 +213,7 @@ export const EditDocumentForm = ({
/>
<AddSignersFormPartial
key={recipients.length}
documentFlow={documentFlow[EditDocumentStepEnum.SIGNERS]}
documentFlow={documentFlow.signers}
document={document}
recipients={recipients}
fields={fields}
@ -218,14 +221,14 @@ export const EditDocumentForm = ({
/>
<AddFieldsFormPartial
key={fields.length}
documentFlow={documentFlow[EditDocumentStepEnum.FIELDS]}
documentFlow={documentFlow.fields}
recipients={recipients}
fields={fields}
onSubmit={onAddFieldsFormSubmit}
/>
<AddSubjectFormPartial
key={recipients.length}
documentFlow={documentFlow[EditDocumentStepEnum.SUBJECT]}
documentFlow={documentFlow.subject}
document={document}
recipients={recipients}
fields={fields}

View File

@ -26,7 +26,7 @@ import {
import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover';
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 {
DocumentFlowFormContainerActions,
@ -65,10 +65,9 @@ export const AddFieldsFormPartial = ({
recipients,
fields,
onSubmit,
useStep, // Stepper
}: WithStep<AddFieldsFormProps>) => {
}: AddFieldsFormProps) => {
const { isWithinPageBounds, getFieldPosition, getPage } = useDocumentElement();
const { currentStep, totalSteps, nextStep, previousStep } = useStep();
const { currentStep, totalSteps, previousStep } = useStep();
const {
control,

View File

@ -18,7 +18,7 @@ import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
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 { ZAddSignersFormSchema } from './add-signers.types';
import {
@ -43,14 +43,13 @@ export const AddSignersFormPartial = ({
document,
fields: _fields,
onSubmit,
useStep, // Stepper
}: WithStep<AddSignersFormProps>) => {
}: AddSignersFormProps) => {
const { toast } = useToast();
const { remaining } = useLimits();
const initialId = useId();
const { currentStep, totalSteps, nextStep, previousStep } = useStep();
const { currentStep, totalSteps, previousStep } = useStep();
const {
control,

View File

@ -10,7 +10,7 @@ import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { Textarea } from '@documenso/ui/primitives/textarea';
import type { WithStep } from '../stepper';
import { useStep } from '../stepper';
import type { TAddSubjectFormSchema } from './add-subject.types';
import {
DocumentFlowFormContainerActions,
@ -34,8 +34,7 @@ export const AddSubjectFormPartial = ({
fields: _fields,
document,
onSubmit,
useStep,
}: WithStep<AddSubjectFormProps>) => {
}: AddSubjectFormProps) => {
const {
register,
handleSubmit,
@ -50,7 +49,7 @@ export const AddSubjectFormPartial = ({
});
const onFormSubmit = handleSubmit(onSubmit);
const { currentStep, totalSteps, nextStep, previousStep } = useStep();
const { currentStep, totalSteps, previousStep } = useStep();
return (
<>

View File

@ -8,7 +8,7 @@ import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-messa
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import type { WithStep } from '../stepper';
import { useStep } from '../stepper';
import type { TAddTitleFormSchema } from './add-title.types';
import {
DocumentFlowFormContainerActions,
@ -32,8 +32,7 @@ export const AddTitleFormPartial = ({
fields: _fields,
document,
onSubmit,
useStep,
}: WithStep<AddTitleFormProps>) => {
}: AddTitleFormProps) => {
const {
register,
handleSubmit,

View File

@ -1,8 +1,7 @@
import React, { useEffect, useState } from 'react';
import React, { createContext, useContext, useEffect, useState } from 'react';
import type { FC } from 'react';
type StepProps = {
readonly useStep: () => {
type StepContextType = {
stepIndex: number;
currentStep: number;
totalSteps: number;
@ -11,16 +10,15 @@ type StepProps = {
nextStep: () => void;
previousStep: () => void;
};
};
export type WithStep<T> = T & StepProps;
const StepContext = createContext<StepContextType | null>(null);
type StepperProps = {
children: React.ReactNode;
onComplete?: () => void;
onStepChanged?: (currentStep: number) => void;
currentStep?: number;
setCurrentStep?: (step: number) => void;
currentStep?: number; // external control prop
setCurrentStep?: (step: number) => void; // external control function
};
export const Stepper: FC<StepperProps> = ({
@ -57,7 +55,12 @@ export const Stepper: FC<StepperProps> = ({
onStepChanged && onStepChanged(currentStep);
}, [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,
currentStep,
totalSteps,
@ -65,15 +68,14 @@ export const Stepper: FC<StepperProps> = ({
isLast: currentStep === totalSteps,
nextStep,
previousStep,
});
// empty stepper
if (totalSteps === 0) return null;
const currentChild = React.Children.toArray(children)[currentStep - 1];
// type validation
if (!React.isValidElement<StepProps>(currentChild)) return null;
return <>{React.cloneElement(currentChild, { useStep })}</>;
};
return <StepContext.Provider value={stepContextValue}>{currentChild}</StepContext.Provider>;
};
/** Hook for children to use the step context */
export const useStep = (): StepContextType => {
const context = useContext(StepContext);
if (!context) throw new Error('useStep must be used within a Stepper');
return context;
};