summaryrefslogtreecommitdiff
path: root/src/lib/address
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/address')
-rw-r--r--src/lib/address/api/addressApi.js8
-rw-r--r--src/lib/address/api/addressesApi.js10
-rw-r--r--src/lib/address/api/cityApi.js8
-rw-r--r--src/lib/address/api/createAddressApi.js8
-rw-r--r--src/lib/address/api/districtApi.js8
-rw-r--r--src/lib/address/api/editAddressApi.js8
-rw-r--r--src/lib/address/api/subDistrictApi.js8
-rw-r--r--src/lib/address/components/Addresses.jsx70
-rw-r--r--src/lib/address/components/CreateAddress.jsx250
-rw-r--r--src/lib/address/components/EditAddress.jsx252
-rw-r--r--src/lib/address/hooks/useAddresses.js13
11 files changed, 643 insertions, 0 deletions
diff --git a/src/lib/address/api/addressApi.js b/src/lib/address/api/addressApi.js
new file mode 100644
index 00000000..f99d81c0
--- /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
diff --git a/src/lib/address/api/addressesApi.js b/src/lib/address/api/addressesApi.js
new file mode 100644
index 00000000..96f9e9d9
--- /dev/null
+++ b/src/lib/address/api/addressesApi.js
@@ -0,0 +1,10 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const addressesApi = async () => {
+ const auth = getAuth()
+ const dataAddresses = await odooApi('GET', `/api/v1/user/${auth.id}/address`)
+ return dataAddresses
+}
+
+export default addressesApi
diff --git a/src/lib/address/api/cityApi.js b/src/lib/address/api/cityApi.js
new file mode 100644
index 00000000..7873435b
--- /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
diff --git a/src/lib/address/api/createAddressApi.js b/src/lib/address/api/createAddressApi.js
new file mode 100644
index 00000000..b33b7ae1
--- /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
diff --git a/src/lib/address/api/districtApi.js b/src/lib/address/api/districtApi.js
new file mode 100644
index 00000000..b0bcff16
--- /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
diff --git a/src/lib/address/api/editAddressApi.js b/src/lib/address/api/editAddressApi.js
new file mode 100644
index 00000000..ba383ef0
--- /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
diff --git a/src/lib/address/api/subDistrictApi.js b/src/lib/address/api/subDistrictApi.js
new file mode 100644
index 00000000..3f834420
--- /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
diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx
new file mode 100644
index 00000000..3ac06b6c
--- /dev/null
+++ b/src/lib/address/components/Addresses.jsx
@@ -0,0 +1,70 @@
+import Link from '@/core/components/elements/Link/Link'
+import Spinner from '@/core/components/elements/Spinner/Spinner'
+import useAuth from '@/core/hooks/useAuth'
+import { getItemAddress, updateItemAddress } from '@/core/utils/address'
+import { useRouter } from 'next/router'
+import useAddresses from '../hooks/useAddresses'
+
+const Addresses = () => {
+ const router = useRouter()
+ const { select = null } = router.query
+ const auth = useAuth()
+ const { addresses } = useAddresses()
+ const selectedAdress = getItemAddress(select || '')
+ const changeSelectedAddress = (id) => {
+ if (!select) return
+ updateItemAddress(select, id)
+ router.back()
+ }
+
+ if (addresses.isLoading) {
+ return (
+ <div className='flex justify-center my-6'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ )
+ }
+
+ return (
+ <div className='p-4'>
+ <div className='text-right'>
+ <Link href='/my/address/create'>Tambah Alamat</Link>
+ </div>
+
+ <div className='grid gap-y-4 mt-4'>
+ {addresses.data?.map((address, index) => {
+ let type = address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address'
+ return (
+ <div
+ key={index}
+ className={
+ 'p-4 rounded-md border ' +
+ (selectedAdress && selectedAdress == address.id
+ ? 'border-gray_r-7 bg-gray_r-4'
+ : 'border-gray_r-7')
+ }
+ >
+ <div onClick={() => changeSelectedAddress(address.id)}>
+ <div className='flex gap-x-2'>
+ <div className='badge-red'>{type}</div>
+ {auth?.partnerId == address.id && <div className='badge-green'>Utama</div>}
+ </div>
+ <p className='font-medium mt-2'>{address.name}</p>
+ {address.mobile && <p className='mt-2 text-gray_r-11'>{address.mobile}</p>}
+ <p className='mt-1 leading-6 text-gray_r-11'>{address.street}</p>
+ </div>
+ <Link
+ href={`/my/address/${address.id}/edit`}
+ className='btn-light bg-white mt-3 w-full !text-gray_r-11'
+ >
+ Ubah Alamat
+ </Link>
+ </div>
+ )
+ })}
+ </div>
+ </div>
+ )
+}
+
+export default Addresses
diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx
new file mode 100644
index 00000000..849b4c01
--- /dev/null
+++ b/src/lib/address/components/CreateAddress.jsx
@@ -0,0 +1,250 @@
+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
diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx
new file mode 100644
index 00000000..a832edbc
--- /dev/null
+++ b/src/lib/address/components/EditAddress.jsx
@@ -0,0 +1,252 @@
+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/address/hooks/useAddresses.js b/src/lib/address/hooks/useAddresses.js
new file mode 100644
index 00000000..629e367c
--- /dev/null
+++ b/src/lib/address/hooks/useAddresses.js
@@ -0,0 +1,13 @@
+import { useQuery } from 'react-query'
+import addressesApi from '../api/addressesApi'
+
+const useAddresses = () => {
+ const fetchAddresses = async () => await addressesApi()
+ const { data, isLoading } = useQuery('addresses', fetchAddresses)
+
+ return {
+ addresses: { data, isLoading }
+ }
+}
+
+export default useAddresses