summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json5
-rw-r--r--src-migrate/modules/product-promo/components/Card.tsx4
-rw-r--r--src/api/promoApi.js4
-rw-r--r--src/lib/home/components/PromotionProgram.jsx14
-rw-r--r--src/pages/shop/promo/[slug].tsx77
-rw-r--r--src/pages/shop/promo/index.tsx188
-rw-r--r--tsconfig.json3
7 files changed, 267 insertions, 28 deletions
diff --git a/package.json b/package.json
index 6b5f20d7..32c701a5 100644
--- a/package.json
+++ b/package.json
@@ -26,15 +26,17 @@
"cookies-next": "^2.1.1",
"flowbite": "^1.6.4",
"framer-motion": "^7.10.3",
+ "http-proxy-middleware": "^3.0.0",
"lodash-contrib": "^4.1200.1",
"lucide-react": "^0.279.0",
"midtrans-client": "^1.3.1",
"moment": "^2.29.4",
- "next": "13.0.0",
+ "next": "^13.5.6",
"next-auth": "^4.22.3",
"next-progress": "^2.2.0",
"next-pwa": "^5.6.0",
"next-seo": "^5.15.0",
+ "node-fetch": "^3.3.2",
"nodemailer": "^6.8.0",
"react": "18.2.0",
"react-dom": "18.2.0",
@@ -50,6 +52,7 @@
"react-web-share": "^2.0.2",
"sharp": "^0.33.2",
"snakecase-keys": "^5.5.0",
+ "striptags": "^3.2.0",
"swiper": "^8.4.4",
"tw-merge": "^0.0.1-alpha.3",
"usehooks-ts": "^2.9.1",
diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx
index be7d5b6e..0be27af2 100644
--- a/src-migrate/modules/product-promo/components/Card.tsx
+++ b/src-migrate/modules/product-promo/components/Card.tsx
@@ -77,7 +77,7 @@ const ProductPromoCard = ({ promotion, slug }: Props) => {
const shouldRender = !slug || promotion.type.value === slug
return (
- shouldRender && (
+ // shouldRender && (
<div className={style.card}>
<ProductPromoCardCountdown promotion={promotion} />
@@ -133,7 +133,7 @@ const ProductPromoCard = ({ promotion, slug }: Props) => {
</div>
</div>
</div>
- )
+ // )
)
}
diff --git a/src/api/promoApi.js b/src/api/promoApi.js
index 535df021..ecae5080 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) => {
+ // let query = type ? `type_value_s:${type}` : '*:*';
let start = 0
let rows = 120
try {
- const queryParams = new URLSearchParams({ q: `type_value_s:${type}` });
+ const queryParams = new URLSearchParams({ q: type });
const response = await fetch(`/solr/promotion_program_lines/select?${queryParams.toString()}&rows=${rows}&start=${start}`);
+ console.log("Constructed URL SLOR:", `/solr/promotion_program_lines/select?${queryParams.toString()}&rows=${rows}&start=${start}`)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx
index a3c09a9b..98bc7c7f 100644
--- a/src/lib/home/components/PromotionProgram.jsx
+++ b/src/lib/home/components/PromotionProgram.jsx
@@ -9,17 +9,19 @@ const BannerSection = () => {
return (
<div className='px-4 sm:px-0'>
- <div className='flex justify-between items-center mb-4'>
+ <div className='flex justify-between items-center mb-4 '>
<div className='font-semibold sm:text-h-lg'>Promo Tersedia</div>
{isDesktop && (
- <Link href='/shop/promo' className='!text-red-500 font-semibold'>
- Lihat Semua
- </Link>
+ <Link href='/shop/promo'
+ className="rounded-md bg-[#FAD147] px-3.5 py-2.5 text-sm font-semibold text-neutral-950 shadow-sm hover:bg-yellow-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-yellow-600"
+ >
+ Lihat Semua
+ </Link>
)}
</div>
{promotionProgram.data &&
promotionProgram.data?.length > 0 && (
- <div className='bg-red-300 grid grid-cols-3 sm:grid-cols-3 gap-4 rounded-md'>
+ <div className='grid grid-cols-3 sm:grid-cols-3 gap-4 rounded-md'>
{promotionProgram.data?.map((banner) => (
<Link key={banner.id} href={banner.url}>
<Image
@@ -28,7 +30,7 @@ const BannerSection = () => {
quality={100}
src={banner.image}
alt={banner.name}
- className='h-auto w-full rounded'
+ className='h-auto w-full rounded hover:scale-105 transition duration-500 ease-in-out'
/>
</Link>
))}
diff --git a/src/pages/shop/promo/[slug].tsx b/src/pages/shop/promo/[slug].tsx
index cd3e93c4..be5a715d 100644
--- a/src/pages/shop/promo/[slug].tsx
+++ b/src/pages/shop/promo/[slug].tsx
@@ -10,7 +10,6 @@ import { IPromotion } from '../../../../src-migrate/types/promotion'
import React from 'react'
import { SolrResponse } from "../../../../src-migrate/types/solr.ts";
-
const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout'))
export default function PromoDetail() {
@@ -22,14 +21,15 @@ export default function PromoDetail() {
const itemsPerPage = 12; // Jumlah item yang ingin ditampilkan per halaman
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = Math.min(startIndex + itemsPerPage, promoData?.length || 0);
- const visiblePromotions = promoData?.slice(startIndex, endIndex);
+ // const visiblePromotions = promoData?.slice(startIndex, endIndex);
const [loading, setLoading] = useState(true); // Menambahkan status loading
+ const [fetchingData, setFetchingData] = useState(false)
useEffect(() => {
const loadPromo = async () => {
try {
- const items = await fetchPromoItemsSolr(Array.isArray(slug) ? slug[0] : slug)
+ const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`)
console.log("slug sekarang ", slug)
setPromoItems(items)
@@ -112,18 +112,60 @@ export default function PromoDetail() {
console.log("data yg dikirim ke ProductPromoCard", promoData)
function capitalizeFirstLetter(string) {
- return string.charAt(0).toUpperCase() + string.slice(1);
+ // Ganti semua tanda _ dengan spasi
+ string = string.replace(/_/g, ' ');
+
+ // Kapitalisasi huruf pertama setelah spasi atau awal string
+ return string.replace(/(^\w|\s\w)/g, function(match) {
+ return match.toUpperCase();
+ });
+
+
+ }
+
+ 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 goToNextPage = () => {
- setCurrentPage((prevPage) => prevPage + 1);
- window.scrollTo({ top: 0, behavior: 'smooth' });
- };
+ const visiblePromotions = promoData?.slice(0, currentPage * 12)
+
- const goToPreviousPage = () => {
- setCurrentPage((prevPage) => Math.max(prevPage - 1, 1));
- window.scrollTo({ top: 0, behavior: 'smooth' });
- };
return (
<BasicLayout>
@@ -142,14 +184,15 @@ export default function PromoDetail() {
<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} slug={Array.isArray(slug) ? slug[0] : slug} />
+ <ProductPromoCard promotion={promotion}/>
</div>
))}
</div>
- <div className="flex justify-center mt-4">
- <button onClick={goToPreviousPage} disabled={currentPage === 1} className="mr-2 px-4 py-2 bg-gray-200 rounded">Back</button>
- <button onClick={goToNextPage} disabled={endIndex >= promoData.length} className="px-4 py-2 bg-gray-200 rounded">Next</button>
- </div>
+ {fetchingData && (
+ <div className='container flex justify-center my-4'>
+ <LogoSpinner width={48} height={48} />
+ </div>
+ )}
</>
) : (
<div className="text-center my-8">
diff --git a/src/pages/shop/promo/index.tsx b/src/pages/shop/promo/index.tsx
new file mode 100644
index 00000000..6f5134a3
--- /dev/null
+++ b/src/pages/shop/promo/index.tsx
@@ -0,0 +1,188 @@
+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(`*:*`)
+ console.log("slug sekarang ", slug)
+
+ setPromoItems(items)
+ console.log("data dari promotion pakai SOLR", items)
+
+ if (items.length === 0) {
+ setPromoData([])
+ setLoading(false);
+ return;
+ }
+
+ const promoDataPromises = items.map(async (item) => {
+ const queryParams = new URLSearchParams({ q: `id:${item.id}` })
+ console.log("Constructed URL:", `/solr/promotion_program_lines/select?${queryParams.toString()}`)
+
+ try {
+ const response = await fetch(`/solr/promotion_program_lines/select?${queryParams.toString()}`)
+ console.log("respon data ", response)
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`)
+ }
+
+ const data: SolrResponse<any[]> = await response.json()
+ console.log("data promo IPromotion[]", data)
+
+ 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
+ }
+
+ console.log("data yg dikirim ke ProductPromoCard", promoData)
+ function capitalizeFirstLetter(string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ }
+
+ 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>
+ )
+}
diff --git a/tsconfig.json b/tsconfig.json
index b2e205a3..8613c022 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -32,8 +32,9 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
+ "**/*.jsx",
".next/types/**/*.ts"
- ],
+, "src/pages/shop/promo/index.tsx", "src/pages/shop/promo/[slug].jsx" ],
"exclude": [
"node_modules",
"src"