Skip to content

Instantly share code, notes, and snippets.

@imtheaman
Created May 30, 2024 09:50
Show Gist options
  • Save imtheaman/fe72fb1697fb80b36942267ab270719b to your computer and use it in GitHub Desktop.
Save imtheaman/fe72fb1697fb80b36942267ab270719b to your computer and use it in GitHub Desktop.
Basic container & presentor pattern in react
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
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 }
}
'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