summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortrisusilo48 <tri.susilo@altama.co.id>2024-11-13 14:46:59 +0700
committertrisusilo48 <tri.susilo@altama.co.id>2024-11-13 14:46:59 +0700
commit7966f67569d01c25f7a337962d7d0bb1a0c57808 (patch)
tree7069dae9adba31fd8dba56bbdaecaaa391e86654
parentebba81f144b860eaf3bd7a9ef2b1c63a2ff021e0 (diff)
get couries, mapping couries and service
-rw-r--r--src/core/api/biteShip.js20
-rw-r--r--src/lib/checkout/api/ExpedisiList.js18
-rw-r--r--src/lib/checkout/api/getRatesCourier.js22
-rw-r--r--src/lib/checkout/components/Checkout.jsx53
-rw-r--r--src/lib/checkout/components/SectionExpedition.tsx256
-rw-r--r--src/lib/checkout/stores/useAdress.js23
-rw-r--r--src/pages/api/biteship-service.js24
7 files changed, 363 insertions, 53 deletions
diff --git a/src/core/api/biteShip.js b/src/core/api/biteShip.js
index 9e9e8567..f18421d8 100644
--- a/src/core/api/biteShip.js
+++ b/src/core/api/biteShip.js
@@ -2,23 +2,27 @@ import axios from 'axios';
const biteShipAPI = async (method, url, body = {}) => {
try {
- const key = process.env.NEXT_PUBLIC_BITE_SHIP_KEY;
- let axiosParameter = {
+ const key = process.env.NEXT_PUBLIC_BITSEHIP_KEY;
+ const baseUrl = process.env.NEXT_PUBLIC_BITE_SHIP_HOST;
+
+ const axiosParameter = {
method,
- url: process.env.NEXT_PUBLIC_BITE_SHIP_HOST + url,
- headers: { Authorization: key, 'Content-Type': 'application/json' },
+ url: baseUrl + url,
+ headers: {
+ Authorization: `Bearer ${key}`, // Tambahkan "Bearer " di depan key
+ 'Content-Type': 'application/json',
+ },
+ data: body, // Tidak perlu JSON.stringify
};
- if (Object.keys(body).length > 0)
- axiosParameter.data = JSON.stringify(body);
const data = await axios(axiosParameter);
- return { success: true, data: data};
+ return { success: true, data: data };
} catch (error) {
console.log(error);
return {
success: false,
- data : {}
+ data: {},
};
}
};
diff --git a/src/lib/checkout/api/ExpedisiList.js b/src/lib/checkout/api/ExpedisiList.js
index 110295b7..67ef93e2 100644
--- a/src/lib/checkout/api/ExpedisiList.js
+++ b/src/lib/checkout/api/ExpedisiList.js
@@ -1,23 +1,7 @@
import odooApi from '@/core/api/odooApi';
-import axios from 'axios';
-import biteShipAPI from '../../../core/api/biteShip';
-
const ExpedisiList = async () => {
const dataExpedisi = await odooApi('GET', '/api/v1/courier');
return dataExpedisi;
};
-const GetRatesCourierBiteship = async ({ destination, items }) => {
- const couriers = process.env.NEXT_PUBLIC_BITESHIP_CODE_COURIERS;
- let body = {
- destination,
- couriers: couriers,
- items
- };
-
- const featch = await biteShipAPI('POST', '/v1/rates/couriers', body);
-
- return featch;
-};
-
-export { GetRatesCourierBiteship, ExpedisiList} ;
+export default ExpedisiList;
diff --git a/src/lib/checkout/api/getRatesCourier.js b/src/lib/checkout/api/getRatesCourier.js
new file mode 100644
index 00000000..8db02d50
--- /dev/null
+++ b/src/lib/checkout/api/getRatesCourier.js
@@ -0,0 +1,22 @@
+import axios from "axios";
+import biteShipAPI from "../../../core/api/biteShip";
+
+const GetRatesCourierBiteship = async ({ destination, items }) => {
+ const couriers = process.env.NEXT_PUBLIC_BITESHIP_CODE_COURIERS;
+ let body = {
+ ...destination,
+ couriers: 'gojek, grab, deliveree, lalamove, jne, tiki, ninja, lion, rara, sicepat, jnt, pos, idexpress, rpx, wahana, jdl, pos, anteraja, sap, paxel, borzo',
+ items: items,
+ };
+
+ const response = await axios(`${process.env.NEXT_PUBLIC_SELF_HOST}/api/biteship-service?method=POST&url=/v1/rates/couriers&body=` + JSON.stringify(body));
+
+ // const featch = await biteShipAPI('POST', '/v1/rates/couriers', body);
+ console.log('ini featch', response);
+
+
+ return response;
+};
+
+
+export default GetRatesCourierBiteship \ No newline at end of file
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx
index 6da27745..b4c311a9 100644
--- a/src/lib/checkout/components/Checkout.jsx
+++ b/src/lib/checkout/components/Checkout.jsx
@@ -1,4 +1,4 @@
-import { Button, Skeleton, Spinner } from '@chakra-ui/react';
+import { Skeleton, Spinner } from '@chakra-ui/react';
import {
BanknotesIcon,
ChevronLeftIcon,
@@ -28,10 +28,12 @@ import getFileBase64 from '@/core/utils/getFileBase64';
import { gtagPurchase } from '@/core/utils/googleTag';
import whatsappUrl from '@/core/utils/whatsappUrl';
import addressesApi from '@/lib/address/api/addressesApi';
+import { MapPinIcon } from 'lucide-react';
import CartItem from '~/modules/cart/components/Item.tsx';
import ExpedisiList from '../api/ExpedisiList';
-import { findVoucher, getVoucher, getVoucherNew } from '../api/getVoucher';
-import { MapPinIcon } from 'lucide-react';
+import { getVoucher } from '../api/getVoucher';
+import { useAddress } from '../stores/useAdress';
+import SectionExpedition from './SectionExpedition';
const SELF_PICKUP_ID = 32;
@@ -57,11 +59,15 @@ const Checkout = () => {
})
);
- const [selectedAddress, setSelectedAddress] = useState({
- shipping: null,
- invoicing: null,
- });
- const [addresses, setAddresses] = useState(null);
+ const {
+ selectedAddress,
+ setSelectedAddress,
+ addresses,
+ setAddresses,
+ setAddressMaps,
+ setCoordinate,
+ setPostalCode,
+ } = useAddress();
useEffect(() => {
if (!auth) return;
@@ -91,10 +97,20 @@ const Checkout = () => {
return addresses[0];
};
+ let ship = matchAddress('shipping');
+
setSelectedAddress({
shipping: matchAddress('shipping'),
invoicing: matchAddress('invoicing'),
});
+ setPostalCode(ship?.zip);
+ if (ship?.addressMap) {
+ setAddressMaps(ship?.addressMap);
+ setCoordinate({
+ destination_latitude: ship?.latitude,
+ destination_longitude: ship?.longtitude,
+ });
+ }
}, [addresses]);
const [products, setProducts] = useState(null);
@@ -1340,6 +1356,7 @@ const Checkout = () => {
/>
</Skeleton>
)}
+ {products && <SectionExpedition products={products} />}
<Divider />
<SectionValidation address={selectedAddress.invoicing} />
<SectionExpedisi
@@ -1621,12 +1638,22 @@ const SectionAddress = ({ address, label, url }) => (
{address.street}, {address?.city?.name}
</p>
<div className='flex gap-x-2 items-center mt-4'>
- <MapPinIcon className={address.addressMap ? `h-6 w-6 text-gray-500` : `h-6 w-6 text-red-500`} />
- {address.addressMap ? <label>Sudah Pinpoint</label> : <label className='text-red-500 '>Belum Pinpoint</label>}
+ <MapPinIcon
+ className={
+ address.addressMap
+ ? `h-6 w-6 text-gray-500`
+ : `h-6 w-6 text-red-500`
+ }
+ />
+ {address.addressMap ? (
+ <label>Sudah Pinpoint</label>
+ ) : (
+ <label className='text-red-500 '>Belum Pinpoint</label>
+ )}
</div>
</div>
)}
- </div>
+ </div>
);
const SectionValidation = ({ address }) =>
@@ -1647,11 +1674,9 @@ const SectionValidation = ({ address }) =>
);
const SectionExpedisiBiteship = ({ listExpedisi, setSelectedExpedisi }) => (
- <>
- </>
+ <></>
);
-
const SectionExpedisi = ({
address,
listExpedisi,
diff --git a/src/lib/checkout/components/SectionExpedition.tsx b/src/lib/checkout/components/SectionExpedition.tsx
index d2dd5763..a6e05893 100644
--- a/src/lib/checkout/components/SectionExpedition.tsx
+++ b/src/lib/checkout/components/SectionExpedition.tsx
@@ -1,12 +1,248 @@
-import React from 'react';
-import { GetRatesCourierBiteship } from '../api/ExpedisiList';
+import React, { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
+import GetRatesCourierBiteship from '../api/getRatesCourier';
+import { useAddress } from '../stores/useAdress';
+import axios, { AxiosResponse } from 'axios';
+import { useForm, Controller } from 'react-hook-form';
+import { AnimatePresence, motion } from 'framer-motion';
+import { Spinner } from '@chakra-ui/react';
-export default function SectionExpedition() {
- let destination = {}
- let items = {}
- const fetchExpedition = async () => await GetRatesCourierBiteship({destination, items});
- const {data : coursers, isLoading} = useQuery('expedition-'+destination, fetchExpedition)
-
- return <div>SectionExpedition</div>;
-} \ No newline at end of file
+function mappingItems(products) {
+ return products?.map((item) => ({
+ name: item.parent.name,
+ description: `${item.code} - ${item.name}`,
+ value: item.price.priceDiscount,
+ weight: item.weight,
+ quantity: item.quantity,
+ }));
+}
+
+function mappingCourier(couriers) {
+ return couriers?.reduce((result, item) => {
+ const { courier_code, courier_service_code } = item;
+
+ // Jika courier_code belum ada di result, buat objek baru untuknya
+ if (!result[courier_code]) {
+ result[courier_code] = {
+ courier_name: item.courier_name,
+ courier_code: courier_code,
+ service_type: {
+ [courier_service_code]: {
+ service_name: item.courier_service_name,
+ duration: item.duration,
+ shipment_duration: item.duration,
+ price: item.price,
+ service_type: item.service_type,
+ description: item.description,
+ },
+ },
+ };
+ } else {
+ result[courier_code].service_type[courier_service_code] = {
+ service_name: item.courier_service_name,
+ duration: item.duration,
+ shipment_duration: item.duration,
+ price: item.price,
+ service_type: item.service_type,
+ description: item.description,
+ };
+ }
+
+ return result;
+ }, {});
+}
+
+interface CourierService {
+ courier_name: string;
+ courier_code: string;
+ service_type: {
+ [key: string]: {
+ service_name: string;
+ duration: number;
+ shipment_duration: number;
+ price: number;
+ service_type: string;
+ description: string;
+ };
+ };
+}
+
+export default function SectionExpedition({ products }) {
+ const { addressMaps, coordinate, postalCode } = useAddress();
+ const { control, handleSubmit } = useForm();
+ const [selectedCourier, setSelectedCourier] = useState('');
+ const [serviceOptions, setServiceOptions] = useState([]);
+
+ let destination = {};
+ let items = mappingItems(products);
+ if (addressMaps) {
+ destination = {
+ origin_latitude: -6.3031123,
+ origin_longitude: 106.7794934999,
+ ...coordinate,
+ };
+ } else if (postalCode) {
+ destination = {
+ origin_postal_code: 12440,
+ destination_postal_code: postalCode,
+ };
+ }
+
+ const fetchExpedition = async () => {
+ let body = {
+ ...destination,
+ couriers:
+ 'gojek, grab, deliveree, lalamove, jne, tiki, ninja, lion, rara, sicepat, jnt, pos, idexpress, rpx, wahana, jdl, pos, anteraja, sap, paxel, borzo',
+ items: items,
+ };
+ try {
+ const response = await axios.get(`/api/biteship-service`, {
+ params: { body: JSON.stringify(body) },
+ });
+ return response;
+ } catch (error) {
+ console.error('Failed to fetch expedition rates:', error);
+ }
+ };
+
+ // const fetchExpedition = async () => await GetRatesCourierBiteship({ destination, items });
+ const { data, isLoading } = useQuery(
+ ['expedition', JSON.stringify(destination), JSON.stringify(items)],
+ fetchExpedition,
+ {
+ enabled: Boolean(Object.keys(destination).length) && items?.length > 0,
+ staleTime: Infinity,
+ cacheTime: Infinity,
+ }
+ );
+
+ const couriers: CourierService = mappingCourier(data?.data?.pricing);
+
+ console.log('couriers', couriers);
+
+ const onCourierChange = (e) => {
+ const courier = e.target.value;
+ console.log('courier', courier);
+ setSelectedCourier(courier);
+ // Menentukan layanan berdasarkan pengiriman yang dipilih
+ if (courier && couriers[courier]) {
+ setServiceOptions(Object.values(couriers[courier]?.service_type));
+ } else {
+ setServiceOptions([]);
+ }
+ };
+
+ console.log(
+ 'serviceOptions',
+ couriers[selectedCourier]?.service_type,
+ selectedCourier
+ );
+ const onSubmit = (data) => {
+ console.log(data);
+ };
+
+ return (
+ <form onSubmit={handleSubmit(onSubmit)}>
+ {/* Dropdown untuk memilih jenis pengiriman */}
+ <div className='px-4 py-2'>
+ <div className='flex justify-between items-center'>
+ <div className='font-medium'>Pilih Ekspedisi: </div>
+ <div className='w-[250px]'>
+ <div className='flex items-center gap-x-4'>
+ <select
+ className={`form-input `}
+ onChange={onCourierChange}
+ required
+ >
+ <option value='0,0'>Pilih Pengiriman</option>
+ <option value='1,32'>SELF PICKUP</option>
+ {couriers &&
+ Object.values(couriers)?.map((expedisi) => (
+ <option
+ value={expedisi.courier_code}
+ key={expedisi.courier_name}
+ >
+ {' '}
+ {expedisi.courier_name}
+ </option>
+ ))}
+ </select>
+
+ <AnimatePresence>
+ {isLoading && (
+ <motion.div
+ initial={{ opacity: 0, width: 0 }}
+ animate={{ opacity: 1, width: '28px' }}
+ exit={{ opacity: 0, width: 0 }}
+ transition={{
+ duration: 0.25,
+ }}
+ className='overflow-hidden'
+ >
+ <Spinner thickness='3px' speed='0.5s' color='red.500' />
+ </motion.div>
+ )}
+ </AnimatePresence>
+ </div>
+ {/* {checkoutValidation && (
+ <span className='text-sm text-red-500'>
+ *silahkan pilih expedisi
+ </span>
+ )} */}
+ </div>
+ {/* <style jsx>{`
+ .shake {
+ animation: shake 0.4s ease-in-out;
+ }
+ `}</style> */}
+ </div>
+ {/* {checkWeigth == true && (
+ <p className='mt-4 text-gray_r-11 leading-6'>
+ Mohon maaf, pengiriman hanya tersedia untuk self pickup karena
+ terdapat barang yang belum diatur beratnya. Mohon atur berat barang
+ dengan menghubungi admin melalui{' '}
+ <a
+ className='text-danger-500 inline'
+ href='https://api.whatsapp.com/send?phone=6281717181922'
+ >
+ tautan ini
+ </a>
+ </p>
+ )} */}
+ </div>
+
+ {selectedCourier && (
+ <div className='px-4 py-2'>
+ <div className='flex justify-between items-center'>
+ <div className='font-medium'>Tipe Layanan Ekspedisi: </div>
+ <div className='w-[250px]'>
+ <select
+ className='form-input'
+ // onChange={(e) => setSelectedServiceType(e.target.value)}
+ >
+ {serviceOptions.map(
+ (service: { service_name: string; service_type: string; duration: string, price: number }) => (
+ <option
+ value={service?.service_type}
+ key={service.service_type}
+ >
+ <div className='flex items-center gap-x-4'>
+ <div>
+ <label>{service?.service_name}</label>
+ <span> Estimasi Tiba {service?.duration} </span>
+ </div>
+ <div>
+ <label>{service.price}</label>
+ </div>
+ </div>
+ </option>
+ )
+ )}
+ </select>
+ </div>
+ </div>
+ </div>
+ )}
+ </form>
+ );
+}
diff --git a/src/lib/checkout/stores/useAdress.js b/src/lib/checkout/stores/useAdress.js
index 1c17258d..5274ecfe 100644
--- a/src/lib/checkout/stores/useAdress.js
+++ b/src/lib/checkout/stores/useAdress.js
@@ -1,6 +1,21 @@
-import { create } from "zustand";
+import { create } from 'zustand';
export const useAddress = create((set) => ({
- address: {},
- setAddress: (address) => set({ address }),
-})); \ No newline at end of file
+ selectedAddress: {
+ shipping: null,
+ invoicing: null,
+ },
+ addresses: null,
+ addressMaps : null,
+ coordinate : {
+ destination_latitude : null,
+ destination_longitude : null
+ },
+ postalCode : null,
+ setAddresses: (addresses) => set({ addresses }),
+ setSelectedAddress: (selectedAddress) => set({ selectedAddress }),
+ setCoordinate: (coordinate) => set({ coordinate }),
+ setPostalCode: (postalCode) => set({ postalCode }),
+ setAddressMaps: (addressMaps) => set({ addressMaps }),
+
+}));
diff --git a/src/pages/api/biteship-service.js b/src/pages/api/biteship-service.js
new file mode 100644
index 00000000..ed9e2a9f
--- /dev/null
+++ b/src/pages/api/biteship-service.js
@@ -0,0 +1,24 @@
+import biteShipAPI from '../../core/api/biteShip';
+
+export default async function handler(req, res) {
+ const { body } = req.query;
+
+ const parsedBody = JSON.parse(body);
+ console.log(parsedBody);
+
+ try {
+ let result = await biteShipAPI('POST', '/v1/rates/couriers', parsedBody);
+ console.log('ini result', result);
+
+ if (result && result.data && result.data.data) {
+ res.status(200).json(result.data.data);
+ } else {
+ res
+ .status(500)
+ .json({ error: 'Unexpected response structure from Biteship API' });
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ res.status(400).json({ error: error.message });
+ }
+}