diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-22 10:14:32 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-22 10:14:32 +0700 |
| commit | 3c559031623649a67825ff47f34512f0eb946861 (patch) | |
| tree | 7b62faeebaf3e344cf01ba1c9ced03c7a4fcaf23 /src | |
| parent | 50f5a2d8897020acc11f2a20469ffdd42ca7c31b (diff) | |
fix
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/components/elements/Select/HookFormSelect.jsx | 17 | ||||
| -rw-r--r-- | src/lib/address/api/addressApi.js | 8 | ||||
| -rw-r--r-- | src/lib/address/api/cityApi.js | 8 | ||||
| -rw-r--r-- | src/lib/address/api/createAddressApi.js | 8 | ||||
| -rw-r--r-- | src/lib/address/api/districtApi.js | 8 | ||||
| -rw-r--r-- | src/lib/address/api/editAddressApi.js | 8 | ||||
| -rw-r--r-- | src/lib/address/api/subDistrictApi.js | 8 | ||||
| -rw-r--r-- | src/lib/address/components/CreateAddress.jsx | 230 | ||||
| -rw-r--r-- | src/lib/address/components/EditAddress.jsx | 256 | ||||
| -rw-r--r-- | src/lib/wishlist/components/Wishlists.jsx | 2 | ||||
| -rw-r--r-- | src/pages/my/address/[id]/edit.jsx | 30 | ||||
| -rw-r--r-- | src/pages/my/address/create.jsx | 10 | ||||
| -rw-r--r-- | src/pages/my/menu.jsx | 7 |
13 files changed, 596 insertions, 4 deletions
diff --git a/src/core/components/elements/Select/HookFormSelect.jsx b/src/core/components/elements/Select/HookFormSelect.jsx new file mode 100644 index 00000000..45cd89b6 --- /dev/null +++ b/src/core/components/elements/Select/HookFormSelect.jsx @@ -0,0 +1,17 @@ +import ReactSelect from "react-select" + +const HookFormSelect = ({ + field, + ...props +}) => ( + <ReactSelect + classNamePrefix="form-select" + ref={field.ref} + onChange={(option) => field.onChange(option.value)} + value={field.value ? props.options.find(option => option.value === field.value) : ''} + isDisabled={props.disabled} + {...props} + /> +) + +export default HookFormSelect
\ No newline at end of file diff --git a/src/lib/address/api/addressApi.js b/src/lib/address/api/addressApi.js new file mode 100644 index 00000000..f2fc6c9e --- /dev/null +++ b/src/lib/address/api/addressApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const addressApi = async ({ id }) => { + const dataAddress = await odooApi('GET', `/api/v1/partner/${id}/address`) + return dataAddress +} + +export default addressApi
\ No newline at end of file diff --git a/src/lib/address/api/cityApi.js b/src/lib/address/api/cityApi.js new file mode 100644 index 00000000..8cf1bedd --- /dev/null +++ b/src/lib/address/api/cityApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const cityApi = async () => { + const dataCities = await odooApi('GET', '/api/v1/city') + return dataCities +} + +export default cityApi
\ No newline at end of file diff --git a/src/lib/address/api/createAddressApi.js b/src/lib/address/api/createAddressApi.js new file mode 100644 index 00000000..29804d1c --- /dev/null +++ b/src/lib/address/api/createAddressApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const createAddressApi = async ({ data }) => { + const dataAddress = await odooApi('POST', '/api/v1/partner/address', data) + return dataAddress +} + +export default createAddressApi
\ No newline at end of file diff --git a/src/lib/address/api/districtApi.js b/src/lib/address/api/districtApi.js new file mode 100644 index 00000000..120757e9 --- /dev/null +++ b/src/lib/address/api/districtApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const districtApi = async ({ cityId }) => { + const dataDistricts = await odooApi('GET', `/api/v1/district?city_id=${cityId}`) + return dataDistricts +} + +export default districtApi
\ No newline at end of file diff --git a/src/lib/address/api/editAddressApi.js b/src/lib/address/api/editAddressApi.js new file mode 100644 index 00000000..e01f6015 --- /dev/null +++ b/src/lib/address/api/editAddressApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const editAddressApi = async ({ id, data }) => { + const dataAddress = await odooApi('PUT', `/api/v1/partner/${id}/address`, data) + return dataAddress +} + +export default editAddressApi
\ No newline at end of file diff --git a/src/lib/address/api/subDistrictApi.js b/src/lib/address/api/subDistrictApi.js new file mode 100644 index 00000000..64230838 --- /dev/null +++ b/src/lib/address/api/subDistrictApi.js @@ -0,0 +1,8 @@ +import odooApi from "@/core/api/odooApi" + +const subDistrictApi = async ({ districtId }) => { + const dataSubDistricts = await odooApi('GET', `/api/v1/sub_district?district_id=${districtId}`) + return dataSubDistricts +} + +export default subDistrictApi
\ No newline at end of file diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx new file mode 100644 index 00000000..4ba99820 --- /dev/null +++ b/src/lib/address/components/CreateAddress.jsx @@ -0,0 +1,230 @@ +import HookFormSelect from "@/core/components/elements/Select/HookFormSelect" +import useAuth from "@/core/hooks/useAuth" +import { useRouter } from "next/router" +import { Controller, useForm } from "react-hook-form" +import * as Yup from "yup" +import cityApi from "../api/cityApi" +import districtApi from "../api/districtApi" +import subDistrictApi from "../api/subDistrictApi" +import { useEffect, useState } from "react" +import createAddressApi from "../api/createAddressApi" +import { toast } from "react-hot-toast" +import { yupResolver } from "@hookform/resolvers/yup" + +const CreateAddress = () => { + const auth = useAuth() + const router = useRouter() + const { + register, + formState: { errors }, + handleSubmit, + watch, + setValue, + control, + } = useForm({ + resolver: yupResolver(validationSchema), + defaultValues + }) + + const [ cities, setCities ] = useState([]) + const [ districts, setDistricts ] = useState([]) + const [ subDistricts, setSubDistricts ] = useState([]) + + useEffect(() => { + const loadCities = async () => { + let dataCities = await cityApi() + dataCities = dataCities.map((city) => ({ value: city.id, label: city.name })) + setCities(dataCities) + } + loadCities() + }, []) + + const watchCity = watch('city') + useEffect(() => { + setValue('district', '') + if (watchCity) { + const loadDistricts = async () => { + let dataDistricts = await districtApi({ cityId: watchCity }) + dataDistricts = dataDistricts.map((district) => ({ value: district.id, label: district.name })) + setDistricts(dataDistricts) + } + loadDistricts() + } + }, [ watchCity, setValue ]) + + const watchDistrict = watch('district') + useEffect(() => { + setValue('subDistrict', '') + if (watchDistrict) { + const loadSubDistricts = async () => { + let dataSubDistricts = await subDistrictApi({ districtId: watchDistrict }) + dataSubDistricts = dataSubDistricts.map((district) => ({ value: district.id, label: district.name })) + setSubDistricts(dataSubDistricts) + } + loadSubDistricts() + } + }, [ watchDistrict, setValue ]) + + const onSubmitHandler = async (values) => { + const data = { + ...values, + city_id: values.city, + district_id: values.district, + sub_district_id: values.subDistrict, + parent_id: auth.partnerId + } + + const address = await createAddressApi({ data }) + if (address?.id) { + toast.success('Berhasil menambahkan alamat') + router.back() + } + } + + return ( + <form className="p-4 flex flex-col gap-y-4" onSubmit={handleSubmit(onSubmitHandler)}> + <div> + <label className="form-label mb-2">Label Alamat</label> + <Controller + name="type" + control={control} + render={props => <HookFormSelect {...props} isSearchable={false} options={types} />} + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.type?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Nama</label> + <input + {...register('name')} + placeholder="John Doe" + type="text" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.name?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Email</label> + <input + {...register('email')} + placeholder="contoh@email.com" + type="email" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.email?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Mobile</label> + <input + {...register('mobile')} + placeholder="08xxxxxxxx" + type="tel" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.mobile?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Alamat</label> + <input + {...register('street')} + placeholder="Jl. Bandengan Utara 85A" + type="text" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.street?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Kode Pos</label> + <input + {...register('zip')} + placeholder="10100" + type="number" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.zip?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Kota</label> + <Controller + name="city" + control={control} + render={props => <HookFormSelect {...props} options={cities} />} + /> + <div className="text-caption-2 text-red_r-11 mt-1">{ errors.city?.message }</div> + </div> + + <div> + <label className="form-label mb-2">Kecamatan</label> + <Controller + name="district" + control={control} + render={props => ( + <HookFormSelect + {...props} + options={districts} + disabled={!watchCity} + /> + )} + /> + </div> + + <div> + <label className="form-label mb-2">Kelurahan</label> + <Controller + name="subDistrict" + control={control} + render={props => ( + <HookFormSelect + {...props} + options={subDistricts} + disabled={!watchDistrict} + /> + )} + /> + </div> + + <button + type="submit" + className="btn-yellow mt-2 w-full" + > + Simpan + </button> + </form> + ) +} + +const validationSchema = Yup.object().shape({ + type: Yup.string().required('Harus di-pilih'), + name: Yup.string().min(3, 'Minimal 3 karakter').required('Harus di-isi'), + email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'), + mobile: Yup.string().required('Harus di-isi'), + street: Yup.string().required('Harus di-isi'), + zip: Yup.string().required('Harus di-isi'), + city: Yup.string().required('Harus di-pilih'), +}) + +const defaultValues = { + type: '', + name: '', + email: '', + mobile: '', + street: '', + city: '', + district: '', + subDistrict: '', + zip: '', +} + +const types = [ + { value: 'contact', label: 'Contact Address' }, + { value: 'invoice', label: 'Invoice Address' }, + { value: 'delivery', label: 'Delivery Address' }, + { value: 'other', label: 'Other Address' }, +] + +export default CreateAddress
\ No newline at end of file diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx new file mode 100644 index 00000000..b866a1e6 --- /dev/null +++ b/src/lib/address/components/EditAddress.jsx @@ -0,0 +1,256 @@ +import { yupResolver } from "@hookform/resolvers/yup" +import { useRouter } from "next/router" +import { useEffect, useState } from "react" +import * as Yup from "yup" +import cityApi from "../api/cityApi" +import { Controller, useForm } from "react-hook-form" +import districtApi from "../api/districtApi" +import subDistrictApi from "../api/subDistrictApi" +import editAddressApi from "../api/editAddressApi" +import HookFormSelect from "@/core/components/elements/Select/HookFormSelect" +import { toast } from "react-hot-toast" + +const EditAddress = ({ id, defaultValues }) => { + const router = useRouter() + const { + register, + formState: { errors }, + handleSubmit, + watch, + setValue, + getValues, + control, + } = useForm({ + resolver: yupResolver(validationSchema), + defaultValues, + }) + + const [cities, setCities] = useState([]) + const [districts, setDistricts] = useState([]) + const [subDistricts, setSubDistricts] = useState([]) + + useEffect(() => { + const loadCities = async () => { + let dataCities = await cityApi() + dataCities = dataCities.map((city) => ({ + value: city.id, + label: city.name, + })) + setCities(dataCities) + } + loadCities() + }, []) + + const watchCity = watch("city") + useEffect(() => { + setValue("district", "") + if (watchCity) { + const loadDistricts = async () => { + let dataDistricts = await districtApi({ cityId: watchCity }) + dataDistricts = dataDistricts.map((district) => ({ + value: district.id, + label: district.name, + })) + setDistricts(dataDistricts) + let oldDistrict = getValues("oldDistrict") + if (oldDistrict) { + setValue("district", oldDistrict) + setValue("oldDistrict", "") + } + } + loadDistricts() + } + }, [watchCity, setValue, getValues]) + + const watchDistrict = watch("district") + useEffect(() => { + setValue("subDistrict", "") + if (watchDistrict) { + const loadSubDistricts = async () => { + let dataSubDistricts = await subDistrictApi({ + districtId: watchDistrict, + }) + dataSubDistricts = dataSubDistricts.map((district) => ({ + value: district.id, + label: district.name, + })) + setSubDistricts(dataSubDistricts) + let oldSubDistrict = getValues("oldSubDistrict") + + if (oldSubDistrict) { + setValue("subDistrict", oldSubDistrict) + setValue("oldSubDistrict", "") + } + } + loadSubDistricts() + } + }, [watchDistrict, setValue, getValues]) + + const onSubmitHandler = async (values) => { + const data = { + ...values, + city_id: values.city, + district_id: values.district, + sub_district_id: values.subDistrict, + } + + const address = await editAddressApi({ id, data }) + if (address?.id) { + toast.success("Berhasil mengubah alamat") + router.back() + } + } + + return ( + <form + className="p-4 flex flex-col gap-y-4" + onSubmit={handleSubmit(onSubmitHandler)} + > + <div> + <label className="form-label mb-2">Label Alamat</label> + <Controller + name="type" + control={control} + render={(props) => ( + <HookFormSelect {...props} isSearchable={false} options={types} /> + )} + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.type?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Nama</label> + <input + {...register("name")} + placeholder="John Doe" + type="text" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.name?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Email</label> + <input + {...register("email")} + placeholder="johndoe@example.com" + type="email" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.email?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Mobile</label> + <input + {...register("mobile")} + placeholder="08xxxxxxxx" + type="tel" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.mobile?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Alamat</label> + <input + {...register("street")} + placeholder="Jl. Bandengan Utara 85A" + type="text" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.street?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Kode Pos</label> + <input + {...register("zip")} + placeholder="10100" + type="number" + className="form-input" + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.zip?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Kota</label> + <Controller + name="city" + control={control} + render={(props) => <HookFormSelect {...props} options={cities} />} + /> + <div className="text-caption-2 text-red_r-11 mt-1"> + {errors.city?.message} + </div> + </div> + + <div> + <label className="form-label mb-2">Kecamatan</label> + <Controller + name="district" + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={districts} + disabled={!watchCity} + /> + )} + /> + </div> + + <div> + <label className="form-label mb-2">Kelurahan</label> + <Controller + name="subDistrict" + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={subDistricts} + disabled={!watchDistrict} + /> + )} + /> + </div> + + <button type="submit" className="btn-yellow mt-2 w-full"> + Simpan + </button> + </form> + ) +} + +const validationSchema = Yup.object().shape({ + type: Yup.string().required("Harus di-pilih"), + name: Yup.string().min(3, "Minimal 3 karakter").required("Harus di-isi"), + email: Yup.string() + .email("Format harus seperti johndoe@example.com") + .required("Harus di-isi"), + mobile: Yup.string().required("Harus di-isi"), + street: Yup.string().required("Harus di-isi"), + zip: Yup.string().required("Harus di-isi"), + city: Yup.string().required("Harus di-pilih"), +}) + +const types = [ + { value: "contact", label: "Contact Address" }, + { value: "invoice", label: "Invoice Address" }, + { value: "delivery", label: "Delivery Address" }, + { value: "other", label: "Other Address" }, +] + +export default EditAddress diff --git a/src/lib/wishlist/components/Wishlists.jsx b/src/lib/wishlist/components/Wishlists.jsx index 4bb63933..8cbbb0a2 100644 --- a/src/lib/wishlist/components/Wishlists.jsx +++ b/src/lib/wishlist/components/Wishlists.jsx @@ -33,7 +33,7 @@ const Wishlists = () => { <div className="grid grid-cols-2 gap-3"> {wishlists.data?.products.map((product) => ( - <ProductCard key={product.id} data={product} /> + <ProductCard key={product.id} product={product} /> ))} </div> diff --git a/src/pages/my/address/[id]/edit.jsx b/src/pages/my/address/[id]/edit.jsx new file mode 100644 index 00000000..feff85fd --- /dev/null +++ b/src/pages/my/address/[id]/edit.jsx @@ -0,0 +1,30 @@ +import AppLayout from "@/core/components/layouts/AppLayout" +import addressApi from "@/lib/address/api/addressApi" +import EditAddressComponent from "@/lib/address/components/EditAddress" + +export default function EditAddress({ id, defaultValues }) { + return ( + <AppLayout title="Ubah Alamat"> + <EditAddressComponent id={id} defaultValues={defaultValues} /> + </AppLayout> + ) +} + +export async function getServerSideProps(context) { + const { id } = context.query + const address = await addressApi({ id }) + const defaultValues = { + type: address.type, + name: address.name, + email: address.email, + mobile: address.mobile, + street: address.street, + zip: address.zip, + city: address.city?.id || "", + oldDistrict: address.district?.id || "", + district: "", + oldSubDistrict: address.subDistrict?.id || "", + subDistrict: "", + } + return { props: { id, defaultValues } } +} diff --git a/src/pages/my/address/create.jsx b/src/pages/my/address/create.jsx new file mode 100644 index 00000000..dfc84444 --- /dev/null +++ b/src/pages/my/address/create.jsx @@ -0,0 +1,10 @@ +import AppLayout from "@/core/components/layouts/AppLayout" +import CreateAddressComponent from "@/lib/address/components/CreateAddress" + +export default function CreateAddress() { + return ( + <AppLayout title="Tambah Alamat"> + <CreateAddressComponent /> + </AppLayout> + ) +}
\ No newline at end of file diff --git a/src/pages/my/menu.jsx b/src/pages/my/menu.jsx index 46a0e4b3..3620fc36 100644 --- a/src/pages/my/menu.jsx +++ b/src/pages/my/menu.jsx @@ -80,9 +80,10 @@ export default function Menu() { <LinkItem href="/my/address"> Daftar Alamat </LinkItem> - <div onClick={logout} className="!text-gray_r-11 font-normal p-4 flex items-center"> - Keluar Akun - </div> + </div> + + <div onClick={logout} className="p-4 mt-2"> + <button className="w-full btn-red">Keluar Akun</button> </div> </div> </div> |
