diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/promoApi.js | 4 | ||||
| -rw-r--r-- | src/core/components/elements/Footer/PromoOffer.tsx | 112 | ||||
| -rw-r--r-- | src/core/components/elements/Footer/style/promoOffer.module.css | 39 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/NavbarDesktop.jsx | 15 | ||||
| -rw-r--r-- | src/core/components/elements/Sidebar/Sidebar.jsx | 3 | ||||
| -rw-r--r-- | src/lib/home/components/PromotionProgram.jsx | 7 | ||||
| -rw-r--r-- | src/pages/shop/promo/[slug].tsx | 8 | ||||
| -rw-r--r-- | src/pages/shop/promo/index.jsx | 40 | ||||
| -rw-r--r-- | src/pages/shop/promo/index.tsx | 186 |
9 files changed, 216 insertions, 198 deletions
diff --git a/src/api/promoApi.js b/src/api/promoApi.js index 0e82c8b9..967aeab2 100644 --- a/src/api/promoApi.js +++ b/src/api/promoApi.js @@ -13,11 +13,13 @@ export const fetchPromoItems = async (type) => { } }; -export const fetchPromoItemsSolr = async (type) => { +export const fetchPromoItemsSolr = async (type, start, rows) => { // let query = type ? `type_value_s:${type}` : '*:*'; let sort ='sort=if(exists(sequence_i),0,1) asc, sequence_i asc, if(exists(total_qty_sold_f), total_qty_sold_f, -1) desc'; let start = 0 let rows = 100 + // let start = 0 + // let rows = 10 try { const queryParams = new URLSearchParams({ q: type }); const response = await fetch(`/solr/promotion_program_lines/select?${queryParams.toString()}&rows=${rows}&start=${start}&${sort}`); diff --git a/src/core/components/elements/Footer/PromoOffer.tsx b/src/core/components/elements/Footer/PromoOffer.tsx new file mode 100644 index 00000000..a5432b6a --- /dev/null +++ b/src/core/components/elements/Footer/PromoOffer.tsx @@ -0,0 +1,112 @@ + +import React from "react"; +// import { useGeneralSetting } from "@/common/state-management/general-setting"; +import { FormEvent, useEffect, useState } from "react"; +import toast from "react-hot-toast"; +import style from "../Footer/style/promoOffer.module.css" +const PromoOffer = ()=>{ + // const { data, isLoading, fetchData } = useGeneralSetting(); + const [formData, setFormData] = useState<FormData>({ + email: "", + name: "", + telephone: "", + message: "", + }); + + useEffect(() => { + // fetchData(); + }, []); + + type FormData = { + email: string; + name: string; + telephone: string; + message: string; + }; + + const [errors, setErrors] = useState({ + email: false, + name: false, + message: false, + }); + + +const handleGetOffer = async (e: FormEvent) => { + e.preventDefault(); + let loadingToast; + try { + loadingToast = toast.loading("Mengirimkan formulir..."); + const response = await fetch("/api/contactus", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ ...formData, from: "newsletter" }), + }); + + if (response.ok) { + toast.dismiss(loadingToast); + toast.success("Terima kasih telah menghubungi kami"); + setFormData({ + email: "", + name: "", + telephone: "", + message: "", + }); + } else { + toast.dismiss(loadingToast); + toast.error("Gagal mengirimkan formulir. Silakan coba lagi nanti."); + } + } catch (error) { + toast.dismiss(loadingToast); + console.error("Gagal mengirimkan formulir", error); + toast.error("Terjadi kesalahan. Silakan coba lagi nanti."); + } + }; + + + + + return( + <div className=" py-3 mb-3"> + <div className="md:flex container mx-auto md:justify-between md:items-center md:gap-x-20"> + <div className=""> + <span className="text-black font-semibold text-sm md:text-lg"> + Dapatkan Promo Menarik Setiap Bulan{" "} + </span> + <p> + Promo produk dengan penawaran terbatas setiap bulannya! + </p> + </div> + <div className=" flex-1 flex items-center h-full justify-end text-sm text-slate-950"> + <form onSubmit={handleGetOffer} className={style['form-input']}> + <div className="flex justify-start w-full"> + <div className="flex justify-end"> + <input + type="email" + value={formData.email} + onChange={(e) => + setFormData({ ...formData, email: e.target.value }) + } + className={style['input']} + placeholder="Masukkan email anda disini" + /> + <button + type="submit" + className={style['button']} + > + Dapatkan + </button> + </div> + + + </div> + </form> + + </div> + </div> + </div> + ) + }; + + export default PromoOffer; diff --git a/src/core/components/elements/Footer/style/promoOffer.module.css b/src/core/components/elements/Footer/style/promoOffer.module.css new file mode 100644 index 00000000..3184182d --- /dev/null +++ b/src/core/components/elements/Footer/style/promoOffer.module.css @@ -0,0 +1,39 @@ +.form-input { + @apply + h-full + w-[514px] + text-slate-950 + flex + justify-center; +} + +.input{ + @apply w-[320px] + sm:w-[320px] + md:w-[500px] + xl:w-[514px] + lg:w-[514px] + 2xl:w-[514px] + text-black + py-2 + h-11 + md:py-3 + px-4 + bg-[#FDF1C7] + rounded-3xl + focus:outline-none + ; +} + +.button{ + @apply bg-[#FAD147] + absolute + py-1.5 + rounded-3xl + text-black + md:py-2.5 + px-4 + h-11 + z-0 + ; +}
\ No newline at end of file diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index 308f2623..e73a35ee 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -171,6 +171,17 @@ const NavbarDesktop = () => { </button> <div className='w-6/12 flex px-1 divide-x divide-gray_r-6'> <Link + href='/shop/promo' + className={`${ + router.asPath === '/shop/promo' && 'bg-gray_r-3' + } p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition`} + // className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition' + target='_blank' + rel='noreferrer' + > + Semua Promo + </Link> + <Link href='/shop/brands' className={`${ router.asPath === '/shop/brands' && 'bg-gray_r-3' @@ -201,9 +212,7 @@ const NavbarDesktop = () => { </Link> <Link href='/video' - className={`${ - router.asPath === '/video' && 'bg-gray_r-3' - } p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition`} + className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition' target='_blank' rel='noreferrer' > diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx index 38fcdef8..cde68515 100644 --- a/src/core/components/elements/Sidebar/Sidebar.jsx +++ b/src/core/components/elements/Sidebar/Sidebar.jsx @@ -117,6 +117,9 @@ const Sidebar = ({ active, close }) => { </> )} </div> + <SidebarLink className={itemClassName} href='/shop/promo'> + Semua Promo + </SidebarLink> <SidebarLink className={itemClassName} href='/shop/brands'> Semua Brand </SidebarLink> diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx index b204df8e..5034fd28 100644 --- a/src/lib/home/components/PromotionProgram.jsx +++ b/src/lib/home/components/PromotionProgram.jsx @@ -13,10 +13,9 @@ const BannerSection = () => { <div className='flex justify-between items-center mb-4 '> <div className='font-semibold sm:text-h-lg'>Promo Tersedia</div> {isDesktop && ( - <div></div> - // <Link href='/shop/promo' className='!text-red-500 font-semibold'> - // Lihat Semua - // </Link> + <Link href='/shop/promo' className='!text-red-500 font-semibold'> + Lihat Semua + </Link> )} </div> {isDesktop && (promotionProgram.data && diff --git a/src/pages/shop/promo/[slug].tsx b/src/pages/shop/promo/[slug].tsx index bd69c071..aaee1249 100644 --- a/src/pages/shop/promo/[slug].tsx +++ b/src/pages/shop/promo/[slug].tsx @@ -91,7 +91,7 @@ export default function PromoDetail() { setCurrentPage(pageNumber) try { - const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`); + const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`,0,100); setPromoItems(items); if (items.length === 0) { @@ -147,16 +147,16 @@ export default function PromoDetail() { const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); const product = response.response.docs[0]; const product_id = product.id; - const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} AND ${combinedQueryPrice}`); + const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} AND ${combinedQueryPrice}`,0,100); return response2; }else if(combinedQuery){ const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); const product = response.response.docs[0]; const product_id = product.id; - const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} `); + const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} `,0,100); return response2; } else { - const response = await fetchPromoItemsSolr(`id:${item.id}`); + const response = await fetchPromoItemsSolr(`id:${item.id}`,0,100); return response; } } catch (fetchError) { diff --git a/src/pages/shop/promo/index.jsx b/src/pages/shop/promo/index.jsx new file mode 100644 index 00000000..01a11aad --- /dev/null +++ b/src/pages/shop/promo/index.jsx @@ -0,0 +1,40 @@ +import Seo from '@/core/components/Seo' +import BasicLayout from '@/core/components/layouts/BasicLayout'; +import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@chakra-ui/react'; +import Link from 'next/link'; +import Promo from '~/pages/shop/promo'; + +import React from 'react'; + +const PromoPage = () => { + return ( + <BasicLayout> + <Seo title='Promo Indoteknik.com' /> + <div className='container mx-auto py-4 md:py-6 pb-0'> + <Breadcrumb> + <BreadcrumbItem> + <BreadcrumbLink + as={Link} + href='/' + className='!text-danger-500 whitespace-nowrap' + > + Home + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem isCurrentPage> + <BreadcrumbLink className='whitespace-nowrap'> + Promo + </BreadcrumbLink> + </BreadcrumbItem> + </Breadcrumb> + + <div className='h-10' /> + + <Promo /> + </div> + </BasicLayout> + ); +}; + +export default PromoPage; diff --git a/src/pages/shop/promo/index.tsx b/src/pages/shop/promo/index.tsx deleted file mode 100644 index 7ec4f6b0..00000000 --- a/src/pages/shop/promo/index.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import dynamic from 'next/dynamic' -import { useEffect, useState } from 'react' -import { useRouter } from 'next/router' -import Seo from '../../../core/components/Seo.jsx' -import Promocrumb from '../../../lib/promo/components/Promocrumb.jsx' -import { fetchPromoItemsSolr } from '../../../api/promoApi.js' -import LogoSpinner from '../../../core/components/elements/Spinner/LogoSpinner.jsx' -import ProductPromoCard from '../../../../src-migrate/modules/product-promo/components/Card.tsx' -import { IPromotion } from '../../../../src-migrate/types/promotion.ts' -import React from 'react' -import { SolrResponse } from "../../../../src-migrate/types/solr.ts"; - -const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout.jsx')) - -export default function Promo() { - const router = useRouter() - const { slug = '' } = router.query - const [promoItems, setPromoItems] = useState<any[]>([]) - const [promoData, setPromoData] = useState<IPromotion[] | null>(null) - const [loading, setLoading] = useState(true) - const [currentPage, setCurrentPage] = useState(1) - const [fetchingData, setFetchingData] = useState(false) - - useEffect(() => { - const loadPromo = async () => { - try { - const items = await fetchPromoItemsSolr(`*:*`) - - - setPromoItems(items) - - - if (items.length === 0) { - setPromoData([]) - setLoading(false); - return; - } - - const promoDataPromises = items.map(async (item) => { - const queryParams = new URLSearchParams({ q: `id:${item.id}` }) - - - try { - const response = await fetch(`/solr/promotion_program_lines/select?${queryParams.toString()}`) - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) - } - - const data: SolrResponse<any[]> = await response.json() - - - const promotions = await map(data.response.docs) - return promotions; - } catch (fetchError) { - console.error("Error fetching promotion data:", fetchError) - return []; - } - }); - - const promoDataArray = await Promise.all(promoDataPromises); - const mergedPromoData = promoDataArray.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); - setPromoData(mergedPromoData); - setTimeout(() => setLoading(false), 120); // Menambahkan delay 200ms sebelum mengubah status loading - } catch (loadError) { - console.error("Error loading promo items:", loadError) - setLoading(false); - } - } - - if (slug) { - loadPromo() - } - }, [slug]) - - const map = async (promotions: any[]): Promise<IPromotion[]> => { - const result: IPromotion[] = [] - - for (const promotion of promotions) { - const data: IPromotion = { - id: promotion.id, - program_id: promotion.program_id_i, - name: promotion.name_s, - type: { - value: promotion.type_value_s, - label: promotion.type_label_s, - }, - limit: promotion.package_limit_i, - limit_user: promotion.package_limit_user_i, - limit_trx: promotion.package_limit_trx_i, - price: promotion.price_f, - total_qty: promotion.total_qty_i, - products: JSON.parse(promotion.products_s), - free_products: JSON.parse(promotion.free_products_s), - } - - result.push(data) - } - - return result - } - - - - - useEffect(() => { - const handleScroll = () => { - if ( - !fetchingData && - window.innerHeight + document.documentElement.scrollTop >= 0.95 * document.documentElement.offsetHeight - ) { - // User has scrolled to 95% of page height - - setTimeout(() => setFetchingData(true), 120); - setCurrentPage((prevPage) => prevPage + 1) - } - } - - window.addEventListener('scroll', handleScroll) - return () => window.removeEventListener('scroll', handleScroll) - }, [fetchingData]) - - useEffect(() => { - if (fetchingData) { - // Fetch more data - // You may need to adjust this logic according to your API - fetchMoreData() - } - }, [fetchingData]) - - const fetchMoreData = async () => { - try { - // Add a delay of approximately 150ms - setTimeout(async () => { - // Fetch more data - // Update promoData state with the new data - }, 150) - } catch (error) { - console.error('Error fetching more data:', error) - } finally { - setTimeout(() => setFetchingData(false), 120); - - } - } - - const visiblePromotions = promoData?.slice(0, currentPage * 12) - - return ( - <BasicLayout> - <Seo - title={`Promo ${Array.isArray(slug) ? slug[0] : slug} Terkini`} - description='B2B Marketplace MRO & Industri dengan Layanan Pembayaran Tempo, Faktur Pajak, Online Quotation, Garansi Resmi & Harga Kompetitif' - /> - {/* <Promocrumb brandName={capitalizeFirstLetter(Array.isArray(slug) ? slug[0] : slug)} /> */} - <div className='container mx-auto mt-1 flex mb-1'> - <div className=''> - <h1 className='font-semibold'>Semua Promo di Indoteknik</h1> - </div> - </div> - {loading ? ( - <div className='container flex justify-center my-4'> - <LogoSpinner width={48} height={48} /> - </div> - ) : promoData && promoItems.length >= 1 ? ( - <> - <div className='flex flex-wrap justify-center'> - {visiblePromotions?.map((promotion) => ( - <div key={promotion.id} className="min-w-[40px] max-w-[400px] mr-[20px] mb-[20px] sm:w-full md:w-1/2 lg:w-1/3 xl:w-1/4"> - <ProductPromoCard promotion={promotion} /> - </div> - ))} - </div> - {fetchingData && ( - <div className='container flex justify-center my-4'> - <LogoSpinner width={48} height={48} /> - </div> - )} - </> - ) : ( - <div className="text-center my-8"> - <p>Belum ada promo pada kategori ini</p> - </div> - )} - </BasicLayout> - ) -} |
