Created
May 30, 2024 09:50
-
-
Save imtheaman/fe72fb1697fb80b36942267ab270719b to your computer and use it in GitHub Desktop.
Basic container & presentor pattern in react
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { BinaryEnum } from '@/utils/enums'; | |
import * as z from 'zod'; | |
const createFormSchema = (address_type: string) => z.object({ | |
'user': z.object({ | |
/* TODO: use BinaryEnum */ | |
'single_address': z.string({ | |
required_error: 'Whether user has same current and permanent address is required' | |
}).nullable().optional(), | |
[address_type]: z.object({ | |
'id': z.string({ | |
/* it'd be added by default in defaultValues */ | |
required_error: 'Address Id is required' | |
}), | |
'address_line1': z.string({ | |
required_error: 'Address Line1 is required' | |
}), | |
'address_line2': z.string().nullable().optional(), | |
'landmark': z.string().nullable().optional(), | |
admin_country_id: z.object({ | |
id: z.string({ | |
required_error: "Country Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}), | |
name: z.string({ | |
required_error: "Country Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}) | |
}).optional(), | |
admin_state_id: z.object({ | |
id: z.string({ | |
required_error: "State Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}), | |
name: z.string({ | |
required_error: "State Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}) | |
}).refine(arg => arg.id && arg.name, "Please select a state"), | |
admin_district_id: z.object({ | |
id: z.string({ | |
required_error: "District Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}), | |
name: z.string({ | |
required_error: "District Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}) | |
}).refine(arg => arg.id && arg.name, "Please select a district"), | |
admin_pincode_id: z.object({ | |
id: z.string({ | |
required_error: "Pincode Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}), | |
name: z.string({ | |
required_error: "Pincode Field is mandatory", | |
invalid_type_error: "Not a valid type" | |
}) | |
}).refine(arg => arg.id && arg.name, "Please select a pincode"), | |
'city': z.string().nullable().optional(), | |
'date_of_verification': z.string().nullable().optional(), | |
'period_of_stay_years': z.string().nullable().optional(), | |
'period_of_stay_months': z.string().nullable().optional(), | |
'own_house': z.nativeEnum(BinaryEnum).nullable().optional(), | |
'contacted_person': z.string().nullable().optional(), | |
'contacted_number': z.string().nullable().optional(), | |
'address_proof_images': z.array(z.any()).nullable().optional() | |
}), | |
}) | |
}) | |
export default createFormSchema |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState } from "react"; | |
import { useForm } from "react-hook-form"; | |
import { zodResolver } from "@hookform/resolvers/zod"; | |
import * as z from "zod"; | |
import { networkRequest } from "@/utils"; | |
import CONSTANTS from "@/constants"; | |
import { useLocation } from "@/utils/hooks"; | |
import createFormSchema from "./schema"; | |
import { useCookies } from "next-client-cookies"; | |
import { BinaryEnum } from "@/utils/enums"; | |
export default function useLogic(defaultValues, userId, onComplete, address_type) { | |
let method = "POST"; | |
const cookies = useCookies(); | |
const FormSchema = createFormSchema(address_type); | |
const [pincodeQuery, setPincodeQuery] = useState(''); | |
const form = useForm<z.infer<typeof FormSchema>>({ | |
resolver: zodResolver(FormSchema), | |
defaultValues: { | |
user: { | |
[address_type]: { | |
id: defaultValues?.id, | |
address_line1: defaultValues?.address_line1, | |
address_line2: defaultValues?.address_line2, | |
admin_country_id: { id: defaultValues?.admin_country_id, name: defaultValues?.country_name }, | |
admin_state_id: { id: defaultValues?.admin_state_id, name: defaultValues?.state_name }, | |
admin_district_id: { id: defaultValues?.admin_district_id, name: defaultValues?.district_name }, | |
admin_pincode_id: { id: defaultValues?.admin_pincode_id, name: defaultValues?.pincode }, | |
city: defaultValues?.city, | |
landmark: defaultValues?.landmark, | |
own_house: defaultValues?.physical_address_verifier?.own_house ? BinaryEnum.YES : BinaryEnum.NO, | |
contacted_number: defaultValues?.physical_address_verifier?.contacted_number, | |
contacted_person: defaultValues?.physical_address_verifier?.contacted_person, | |
date_of_verification: defaultValues?.physical_address_verifier?.date_of_verification, | |
period_of_stay_years: defaultValues?.physical_address_verifier?.period_of_stay_years, | |
period_of_stay_months: defaultValues?.physical_address_verifier?.period_of_stay_months, | |
address_proof_images: [] | |
}, | |
single_address: BinaryEnum.YES | |
} | |
} | |
}) | |
const { countryIndia, stateData, districtData, pincodeData } = useLocation(form.watch("user[current_address_attributes][admin_state_id]"), form.watch('user[current_address_attributes][admin_district_id]'), pincodeQuery); | |
async function onSubmit(data: z.infer<typeof FormSchema>) { | |
let endpoint = CONSTANTS.apiEndpoints.addressesC(userId); | |
if (!data.user.single_address) data.user.single_address = BinaryEnum.NO; | |
if (!data.user[address_type].own_house) data.user[address_type].own_house = BinaryEnum.YES; | |
// @ts-ignore | |
data.user[address_type].admin_country_id = countryIndia.id; | |
// @ts-ignore | |
data.user[address_type].admin_state_id = data.user[address_type].admin_state_id.id; | |
// @ts-ignore | |
data.user[address_type].admin_district_id = data.user[address_type].admin_district_id.id; | |
// @ts-ignore | |
data.user[address_type].admin_pincode_id = data.user[address_type].admin_pincode_id.id; | |
if (defaultValues) { | |
method = "PATCH" | |
endpoint = CONSTANTS.apiEndpoints.addressesU(userId) | |
} | |
const response = await networkRequest(endpoint, { | |
method, | |
headers: { | |
'Authorization': `Bearer ${cookies.get('token')}`, | |
}, | |
body: data, | |
notify: true | |
}) | |
response?.success && onComplete() | |
} | |
return { onSubmit, form, stateData, districtData, pincodeData, setPincodeQuery } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use client'; | |
import { Button, Form, Input } from "@/components/ui"; | |
import { AppFormField, AppSelect, Combobox } from "@/components"; | |
import { BinaryEnum } from "@/utils/enums"; | |
import { MouseEventHandler } from "react"; | |
import useLogic from "./useLogic"; | |
function AddressDetails({ defaultValues, onDismiss, onComplete, userId }: { userId?: string, defaultValues?: any, onDismiss: MouseEventHandler<HTMLButtonElement>, onComplete: Function }) { | |
const address_type = defaultValues?.address_type === 'Permanent' ? 'permanent_address_attributes' : 'current_address_attributes' | |
const { onSubmit, form, stateData, districtData, pincodeData, setPincodeQuery } = useLogic(defaultValues, userId, onComplete, address_type); | |
return ( | |
<Form {...form}> | |
<form className="flex flex-col gap-8" onSubmit={form.handleSubmit(onSubmit)}> | |
<h2 className="text-xl font-medium">Address Details</h2> | |
<div className="grid gap-4 md:gap-8 grid-cols-1 md:grid-cols-2"> | |
<Combobox disabled label="Country" name={`user[${address_type}][admin_country_id]`} form={form} placeholder="India" /> | |
<Combobox required name={`user[${address_type}][admin_state_id]`} label="State" form={form} data={stateData} placeholder="Select State" /> | |
<Combobox required name={`user[${address_type}][admin_district_id]`} label="District" form={form} data={districtData} placeholder="Select District" /> | |
<Combobox required form={form} name={`user[${address_type}][admin_pincode_id]`} label="Pincode" data={pincodeData} placeholder="Select Pincode" onQuery={setPincodeQuery} /> | |
<AppFormField control={form.control} label="Is Current And Permanent Address Same?" name="user[single_address]" content={IsSameAddress} /> | |
<AppFormField control={form.control} rules={{ required: true }} label="City" name={`user[${address_type}][city]`} content={TextInputField} /> | |
<AppFormField control={form.control} rules={{ required: true }} label="Address Line1" name={`user[${address_type}][address_line1]`} content={TextInputField} /> | |
<AppFormField control={form.control} label="Address Line2" name={`user[${address_type}][address_line2]`} content={TextInputField} /> | |
<AppFormField control={form.control} label="Landmark" name={`user[${address_type}][landmark]`} content={TextInputField} /> | |
<AppFormField control={form.control} label="Own House" name={`user[${address_type}][own_house]`} content={OwnHouse} /> | |
</div> | |
<div className="flex items-center mt-4 space-x-2"> | |
<Button variant={"default"} type="submit">Save Details</Button> | |
<Button variant={"outline"} onClick={onDismiss}>Cancel</Button> | |
</div> | |
</form> | |
</Form> | |
); | |
} | |
export default AddressDetails; | |
function TextInputField({ field }) { | |
return <Input className="w-full" variant={"default"} elementSize={"lg"} radius={"md"} {...field} /> | |
} | |
function IsSameAddress({ field }) { | |
return <AppSelect data={BinaryEnum} placeholder="Select Yes or No" value={field.value} variant="outline" radius="md" onValueChange={field.onChange} id="user[single_address]" /> | |
} | |
function OwnHouse({ field }) { | |
return <AppSelect data={BinaryEnum} placeholder="Select Yes or No" value={field.value} variant="outline" radius="md" onValueChange={field.onChange} id="user[own_house]" /> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment