diff options
| -rw-r--r-- | src/core/components/elements/Image/Image.jsx | 16 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/Navbar.jsx | 4 | ||||
| -rw-r--r-- | src/core/components/elements/Sidebar/Sidebar.jsx | 8 | ||||
| -rw-r--r-- | src/core/utils/address.js | 31 | ||||
| -rw-r--r-- | src/lib/address/api/addressesApi.js | 10 | ||||
| -rw-r--r-- | src/lib/address/components/Addresses.jsx | 68 | ||||
| -rw-r--r-- | src/lib/address/hooks/useAddresses.js | 13 | ||||
| -rw-r--r-- | src/lib/invoice/components/Invoice.jsx | 2 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transaction.jsx | 81 | ||||
| -rw-r--r-- | src/lib/wishlist/api/wishlistsApi.js | 10 | ||||
| -rw-r--r-- | src/lib/wishlist/components/Wishlists.jsx | 47 | ||||
| -rw-r--r-- | src/lib/wishlist/hooks/useWishlists.js | 14 | ||||
| -rw-r--r-- | src/pages/my/address/index.jsx | 10 | ||||
| -rw-r--r-- | src/pages/my/menu.jsx | 4 | ||||
| -rw-r--r-- | src/pages/my/wishlist.jsx | 10 |
15 files changed, 275 insertions, 53 deletions
diff --git a/src/core/components/elements/Image/Image.jsx b/src/core/components/elements/Image/Image.jsx index 579660a4..a6f0b00c 100644 --- a/src/core/components/elements/Image/Image.jsx +++ b/src/core/components/elements/Image/Image.jsx @@ -2,13 +2,15 @@ import { LazyLoadImage } from "react-lazy-load-image-component" import "react-lazy-load-image-component/src/effects/opacity.css" const Image = ({ ...props }) => ( - <LazyLoadImage - { ...props } - src={props.src || '/images/noimage.jpeg'} - placeholderSrc="/images/indoteknik-placeholder.png" - alt={props.src ? props.alt : 'Image Not Found - Indoteknik'} - wrapperClassName="bg-white" - /> + <> + <LazyLoadImage + { ...props } + src={props.src || '/images/noimage.jpeg'} + placeholderSrc="/images/indoteknik-placeholder.png" + alt={props.src ? props.alt : 'Image Not Found - Indoteknik'} + wrapperClassName="bg-white" + /> + </> ) Image.defaultProps = LazyLoadImage.defaultProps diff --git a/src/core/components/elements/Navbar/Navbar.jsx b/src/core/components/elements/Navbar/Navbar.jsx index 99fdc446..f10ebd63 100644 --- a/src/core/components/elements/Navbar/Navbar.jsx +++ b/src/core/components/elements/Navbar/Navbar.jsx @@ -17,9 +17,9 @@ const Navbar = () => { <Image src={IndoteknikLogo} alt="Indoteknik Logo" width={120} height={40} /> </Link> <div className="flex gap-x-3"> - <button type="button"> + <Link href="/my/wishlist"> <HeartIcon className="w-6 text-gray_r-12" /> - </button> + </Link> <Link href="/shop/cart"> <ShoppingCartIcon className="w-6 text-gray_r-12" /> </Link> diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx index 88de1c1c..48ceacf6 100644 --- a/src/core/components/elements/Sidebar/Sidebar.jsx +++ b/src/core/components/elements/Sidebar/Sidebar.jsx @@ -1,8 +1,8 @@ import Link from "../Link/Link" import greeting from "@/core/utils/greeting" -import { Cog6ToothIcon } from "@heroicons/react/24/solid" import useAuth from "@/core/hooks/useAuth" import { AnimatePresence, motion } from "framer-motion" +import { CogIcon } from "@heroicons/react/24/outline" const Sidebar = ({ active, @@ -44,8 +44,8 @@ const Sidebar = ({ <div className="p-4 flex gap-x-3"> { !auth && ( <> - <Link href="/register" className="btn-yellow !text-gray_r-12 py-2 flex-1">Daftar</Link> - <Link href="/login" className="btn-solid-red !text-gray_r-1 py-2 flex-1">Masuk</Link> + <Link onClick={close} href="/register" className="btn-yellow !text-gray_r-12 py-2 flex-1">Daftar</Link> + <Link onClick={close} href="/login" className="btn-solid-red !text-gray_r-1 py-2 flex-1">Masuk</Link> </> ) } { auth && ( @@ -61,7 +61,7 @@ const Sidebar = ({ href="/my/menu" className="!text-gray_r-11 ml-auto my-auto" > - <Cog6ToothIcon className="w-6" /> + <CogIcon className="w-6" /> </Link> </> ) } diff --git a/src/core/utils/address.js b/src/core/utils/address.js new file mode 100644 index 00000000..b89dd924 --- /dev/null +++ b/src/core/utils/address.js @@ -0,0 +1,31 @@ +const getAddress = () => { + if (typeof window !== 'undefined') { + const address = localStorage.getItem('address') + if (address) return JSON.parse(address) + } + return {} +} + +const setAddress = (address) => { + if (typeof window !== 'undefined') { + localStorage.setItem('address', JSON.stringify(address)) + } + return +} + +const getItemAddress = (key) => { + let address = getAddress() + return address[key] +} + +const updateItemAddress = (key, value) => { + let address = getAddress() + address[key] = value + setAddress(address) + return +} + +export { + getItemAddress, + updateItemAddress +}
\ No newline at end of file diff --git a/src/lib/address/api/addressesApi.js b/src/lib/address/api/addressesApi.js new file mode 100644 index 00000000..1edfc077 --- /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
\ No newline at end of file diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx new file mode 100644 index 00000000..7a82c0da --- /dev/null +++ b/src/lib/address/components/Addresses.jsx @@ -0,0 +1,68 @@ +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-2" : "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 ${selectedAdress && selectedAdress == address.id ? "text-gray_r-12" : "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
\ No newline at end of file diff --git a/src/lib/address/hooks/useAddresses.js b/src/lib/address/hooks/useAddresses.js new file mode 100644 index 00000000..9968d790 --- /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
\ No newline at end of file diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx index 3e0baaee..aee4a498 100644 --- a/src/lib/invoice/components/Invoice.jsx +++ b/src/lib/invoice/components/Invoice.jsx @@ -10,7 +10,7 @@ const Invoice = ({ id }) => { if (invoice.isLoading) { return ( - <div className="flex justify-center my-4"> + <div className="flex justify-center my-6"> <Spinner className="w-6 text-gray_r-12/50 fill-gray_r-12" /> </div> ) diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index e049a9ac..0017afba 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -46,15 +46,6 @@ const Transaction = ({ id }) => { toast.error('Terjadi kesalahan internal, coba lagi nanti atau hubungi kami') } - const [ section, setSection ] = useState({ - customer: false, - invoice: false, - shipping: false - }) - const toggleSection = (name) => { - setSection({ ...section, [name]: !section[name] }) - } - const [ cancelTransaction, setCancelTransaction ] = useState(false) const openCancelTransaction = () => setCancelTransaction(true) const closeCancelTransaction = () => setCancelTransaction(false) @@ -79,7 +70,7 @@ const Transaction = ({ id }) => { if (transaction.isLoading) { return ( - <div className="flex justify-center my-4"> + <div className="flex justify-center my-6"> <Spinner className="w-6 text-gray_r-12/50 fill-gray_r-12" /> </div> ) @@ -142,33 +133,7 @@ const Transaction = ({ id }) => { <Divider /> - <SectionButton - label="Detail Pelanggan" - active={section.customer} - toggle={() => toggleSection('customer')} - /> - - { section.customer && <SectionContent address={transaction.data?.address?.customer} /> } - - <Divider /> - - <SectionButton - label="Detail Pengiriman" - active={section.shipping} - toggle={() => toggleSection('shipping')} - /> - - { section.shipping && <SectionContent address={transaction.data?.address?.shipping} /> } - - <Divider /> - - <SectionButton - label="Detail Penagihan" - active={section.invoice} - toggle={() => toggleSection('invoice')} - /> - - { section.invoice && <SectionContent address={transaction.data?.address?.invoice} /> } + <SectionAddress address={transaction.data?.address} /> <Divider /> @@ -288,6 +253,48 @@ const Transaction = ({ id }) => { ) } +const SectionAddress = ({ address }) => { + const [ section, setSection ] = useState({ + customer: false, + invoice: false, + shipping: false + }) + const toggleSection = (name) => { + setSection({ ...section, [name]: !section[name] }) + } + + return ( + <> + <SectionButton + label="Detail Pelanggan" + active={section.customer} + toggle={() => toggleSection('customer')} + /> + + { section.customer && <SectionContent address={address?.customer} /> } + + <Divider /> + + <SectionButton + label="Detail Pengiriman" + active={section.shipping} + toggle={() => toggleSection('shipping')} + /> + + { section.shipping && <SectionContent address={address?.shipping} /> } + + <Divider /> + + <SectionButton + label="Detail Penagihan" + active={section.invoice} + toggle={() => toggleSection('invoice')} + /> + { section.invoice && <SectionContent address={address?.invoice} /> } + </> + ) +} + const SectionButton = ({ label, active, toggle }) => ( <button className="p-4 font-medium flex justify-between w-full" onClick={toggle}> <span>{label}</span> diff --git a/src/lib/wishlist/api/wishlistsApi.js b/src/lib/wishlist/api/wishlistsApi.js new file mode 100644 index 00000000..49ef56ee --- /dev/null +++ b/src/lib/wishlist/api/wishlistsApi.js @@ -0,0 +1,10 @@ +import odooApi from "@/core/api/odooApi" +import { getAuth } from "@/core/utils/auth" + +const wishlistsApi = async ({ limit, offset }) => { + const auth = getAuth() + const dataWishlists = await odooApi('GET', `/api/v1/user/${auth.id}/wishlist?limit=${limit}&offset=${offset}`) + return dataWishlists +} + +export default wishlistsApi
\ No newline at end of file diff --git a/src/lib/wishlist/components/Wishlists.jsx b/src/lib/wishlist/components/Wishlists.jsx new file mode 100644 index 00000000..4bb63933 --- /dev/null +++ b/src/lib/wishlist/components/Wishlists.jsx @@ -0,0 +1,47 @@ +import Alert from "@/core/components/elements/Alert/Alert" +import Pagination from "@/core/components/elements/Pagination/Pagination" +import Spinner from "@/core/components/elements/Spinner/Spinner" +import ProductCard from "@/lib/product/components/ProductCard" +import { useRouter } from "next/router" +import useWishlists from "../hooks/useWishlists" + +const Wishlists = () => { + const router = useRouter() + const { + page = 1 + } = router.query + const limit = 30 + const { wishlists } = useWishlists({ page, limit }) + + const pageCount = Math.ceil(wishlists.data?.productTotal / limit) + + if (wishlists.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="px-4 py-6"> + { wishlists.data?.products?.length == 0 && ( + <Alert type='info' className='text-center'> + Wishlist anda masih kosong + </Alert> + ) } + + <div className="grid grid-cols-2 gap-3"> + {wishlists.data?.products.map((product) => ( + <ProductCard key={product.id} data={product} /> + ))} + </div> + + <div className="mt-6"> + <Pagination currentPage={page} pageCount={pageCount} url={`/my/wishlist`} /> + </div> + </div> + ) +} + +export default Wishlists
\ No newline at end of file diff --git a/src/lib/wishlist/hooks/useWishlists.js b/src/lib/wishlist/hooks/useWishlists.js new file mode 100644 index 00000000..a219ab69 --- /dev/null +++ b/src/lib/wishlist/hooks/useWishlists.js @@ -0,0 +1,14 @@ +import { useQuery } from "react-query" +import wishlistsApi from "../api/wishlistsApi" + +const useWishlists = ({ page, limit }) => { + const offset = (page - 1) * limit + const fetchWishlists = async () => await wishlistsApi({ limit, offset }) + const { data, isLoading } = useQuery(`wishlists-${limit}-${offset}`, fetchWishlists) + + return { + wishlists: { data, isLoading } + } +} + +export default useWishlists
\ No newline at end of file diff --git a/src/pages/my/address/index.jsx b/src/pages/my/address/index.jsx new file mode 100644 index 00000000..29e21c30 --- /dev/null +++ b/src/pages/my/address/index.jsx @@ -0,0 +1,10 @@ +import AppLayout from "@/core/components/layouts/AppLayout" +import AddressesComponent from "@/lib/address/components/Addresses" + +export default function Addresses() { + return ( + <AppLayout title="Daftar Alamat"> + <AddressesComponent /> + </AppLayout> + ) +}
\ No newline at end of file diff --git a/src/pages/my/menu.jsx b/src/pages/my/menu.jsx index 67ea1171..46a0e4b3 100644 --- a/src/pages/my/menu.jsx +++ b/src/pages/my/menu.jsx @@ -77,7 +77,7 @@ export default function Menu() { </MenuHeader> <div className="divide-y divide-gray_r-6 border-y border-gray_r-6 mt-4"> - <LinkItem href="/"> + <LinkItem href="/my/address"> Daftar Alamat </LinkItem> <div onClick={logout} className="!text-gray_r-11 font-normal p-4 flex items-center"> @@ -98,7 +98,7 @@ const MenuHeader = ({ children, ...props }) => ( ) const LinkItem = ({ children, ...props }) => ( - <Link {...props} className="!text-gray_r-11 font-normal p-4 flex items-center"> + <Link {...props} className="!text-gray_r-11 !font-normal p-4 flex items-center"> { children } <div className="ml-auto !text-gray_r-11"> <ChevronRightIcon className="w-5" /> diff --git a/src/pages/my/wishlist.jsx b/src/pages/my/wishlist.jsx new file mode 100644 index 00000000..b7a3e4fe --- /dev/null +++ b/src/pages/my/wishlist.jsx @@ -0,0 +1,10 @@ +import AppLayout from "@/core/components/layouts/AppLayout" +import Wishlists from "@/lib/wishlist/components/Wishlists" + +export default function Wishlist() { + return ( + <AppLayout title="Wishlist"> + <Wishlists /> + </AppLayout> + ) +}
\ No newline at end of file |
