summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/header.js68
-rw-r--r--src/components/productCard.js35
-rw-r--r--src/helpers/apiOdoo.js21
-rw-r--r--src/helpers/currencyFormat.js8
-rw-r--r--src/helpers/slug.js13
-rw-r--r--src/icons/menu.svg5
-rw-r--r--src/icons/search.svg4
-rw-r--r--src/icons/shopping-cart.svg12
-rw-r--r--src/pages/_app.js7
-rw-r--r--src/pages/index.js12
-rw-r--r--src/pages/shop/product/[slug].js172
-rw-r--r--src/styles/globals.css220
12 files changed, 577 insertions, 0 deletions
diff --git a/src/components/header.js b/src/components/header.js
new file mode 100644
index 00000000..baebbd3a
--- /dev/null
+++ b/src/components/header.js
@@ -0,0 +1,68 @@
+import Image from "next/image";
+import Link from "next/link";
+import ShoppingCartIcon from "../icons/shopping-cart.svg";
+import SearchIcon from "../icons/search.svg";
+import MenuIcon from "../icons/menu.svg";
+import { useState } from "react";
+
+
+export default function Header() {
+ const [isMenuActive, setIsMenuActive] = useState(false);
+
+ const openMenu = () => setIsMenuActive(true);
+ const closeMenu = () => setIsMenuActive(false);
+
+ return (
+ <>
+ <div className={isMenuActive ? 'menu-wrapper active' : 'menu-wrapper'}>
+ <div className="flex gap-x-2 items-center">
+ <Link href="/login" className="w-full py-2 btn-light">Masuk</Link>
+ <Link href="/register" className="w-full py-2 btn-primary">Daftar</Link>
+ </div>
+ <div className="flex flex-col gap-y-4 mt-5">
+ <Link className="flex w-full font-normal text-gray-800" href="/shop/brands">
+ <span>Brand</span>
+ <div className="ml-auto">
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" className="stroke-gray-700 feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
+ </div>
+ </Link>
+ <Link className="flex w-full font-normal text-gray-800" href="/blog">
+ <span>Blog</span>
+ <div className="ml-auto">
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" className="stroke-gray-700 feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
+ </div>
+ </Link>
+ <button className="flex w-full font-normal text-gray-800" id="open_category_parent_menu">
+ <span>Kategori</span>
+ <div className="ml-auto">
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" className="stroke-gray-700 feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
+ </div>
+ </button>
+ </div>
+ </div>
+ <div className={isMenuActive ? 'menu-overlay block opacity-100' : 'menu-overlay hidden opacity-0'} onClick={closeMenu}></div>
+
+ <div className="sticky-header">
+ <div className="flex justify-between items-center">
+ <Link href="/">
+ <Image src="/images/logo.png" alt="Logo Indoteknik" width={120} height={40} />
+ </Link>
+ <div className="flex gap-4">
+ <Link href="/shop/cart">
+ <ShoppingCartIcon className="w-6" />
+ </Link>
+ <button onClick={openMenu}>
+ <MenuIcon className="w-6" />
+ </button>
+ </div>
+ </div>
+ <form action="" method="GET" className="flex mt-2">
+ <input type="text" name="product_name" className="form-input rounded-r-none border-r-0 focus:outline-none" placeholder="Ketikan nama, merek, part number"/>
+ <button type="submit" aria-label="search" className="btn-light bg-transparent px-2 py-1 rounded-l-none border-l-0">
+ <SearchIcon />
+ </button>
+ </form>
+ </div>
+ </>
+ )
+} \ No newline at end of file
diff --git a/src/components/productCard.js b/src/components/productCard.js
new file mode 100644
index 00000000..dc292316
--- /dev/null
+++ b/src/components/productCard.js
@@ -0,0 +1,35 @@
+import Link from "next/link";
+import currencyFormat from "../helpers/currencyFormat";
+import { createSlug } from "../helpers/slug";
+
+export default function productCard({ data }) {
+ let product = data;
+ return (
+ <div className="product-card">
+ <Link href={'/shop/product/' + createSlug(product.name, product.id)} className="block">
+ <img src={product.image} alt={product.name} className="product-card__image" loading="lazy" />
+ </Link>
+ <div className="product-card__description">
+ <div>
+ {typeof product.manufacture.name !== undefined ? (
+ <a href={'/shop/brands/' + createSlug(product.manufacture.name, product.manufacture.id)} className="product-card__brand">{product.manufacture.name}</a>
+ ) : (
+ <span className="product-card__brand">-</span>
+ )}
+ <a href={'/shop/product/' + createSlug(product.name, product.id)} className="product-card__title wrap-line-ellipsis-3">
+ {product.name}
+ </a>
+ </div>
+ <div>
+ {product.lowest_price.discount_percentage > 0 ? (
+ <div className="flex gap-x-1 items-center mt-2">
+ <span className="badge-yellow">{product.lowest_price.discount_percentage}%</span>
+ <p className="text-xs text-gray-800 line-through">{currencyFormat(product.lowest_price.price)}</p>
+ </div>
+ ) : ''}
+ <p className="text-sm text-gray-900 font-semibold">{product.lowest_price.price_discount > 0 ? currencyFormat(product.lowest_price.price_discount) : 'Tanya harga'}</p>
+ </div>
+ </div>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/src/helpers/apiOdoo.js b/src/helpers/apiOdoo.js
new file mode 100644
index 00000000..632edf61
--- /dev/null
+++ b/src/helpers/apiOdoo.js
@@ -0,0 +1,21 @@
+const axios = require('axios');
+
+const getOdoo = async (url) => {
+ try {
+ let res = await axios(process.env.DB_HOST + url, {
+ headers: {
+ db: process.env.DB_NAME,
+ username: process.env.DB_USER,
+ password: process.env.DB_PASS,
+ }
+ });
+
+ return res.data.result || [];
+ } catch (error) {
+ console.log(error);
+ }
+}
+
+export {
+ getOdoo,
+}; \ No newline at end of file
diff --git a/src/helpers/currencyFormat.js b/src/helpers/currencyFormat.js
new file mode 100644
index 00000000..dadeaec6
--- /dev/null
+++ b/src/helpers/currencyFormat.js
@@ -0,0 +1,8 @@
+export default function currencyFormat(value) {
+ const currency = new Intl.NumberFormat('id-ID', {
+ style: 'currency',
+ currency: 'IDR',
+ maximumFractionDigits: 0
+ });
+ return currency.format(value);
+} \ No newline at end of file
diff --git a/src/helpers/slug.js b/src/helpers/slug.js
new file mode 100644
index 00000000..5a8db08f
--- /dev/null
+++ b/src/helpers/slug.js
@@ -0,0 +1,13 @@
+const createSlug = (name, id) => {
+ return name.trim().replace(new RegExp(/[^A-Za-z0-9]/, 'g'), '-').toLowerCase() + '-' + id;
+}
+
+const getId = (slug) => {
+ let id = slug.split('-');
+ return id[id.length-1];
+}
+
+export {
+ createSlug,
+ getId
+}; \ No newline at end of file
diff --git a/src/icons/menu.svg b/src/icons/menu.svg
new file mode 100644
index 00000000..f4ff45ad
--- /dev/null
+++ b/src/icons/menu.svg
@@ -0,0 +1,5 @@
+<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.04395 10H18.0439" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M3.04395 5H18.0439" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M3.04395 15H18.0439" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/src/icons/search.svg b/src/icons/search.svg
new file mode 100644
index 00000000..6de1cdfa
--- /dev/null
+++ b/src/icons/search.svg
@@ -0,0 +1,4 @@
+<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.71061 15.8333C13.3925 15.8333 16.3773 12.8486 16.3773 9.16667C16.3773 5.48477 13.3925 2.5 9.71061 2.5C6.02871 2.5 3.04395 5.48477 3.04395 9.16667C3.04395 12.8486 6.02871 15.8333 9.71061 15.8333Z" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M18.0439 17.5L14.4189 13.875" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/src/icons/shopping-cart.svg b/src/icons/shopping-cart.svg
new file mode 100644
index 00000000..5c899876
--- /dev/null
+++ b/src/icons/shopping-cart.svg
@@ -0,0 +1,12 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_10_13)">
+<path d="M7.50008 18.3334C7.96032 18.3334 8.33341 17.9603 8.33341 17.5C8.33341 17.0398 7.96032 16.6667 7.50008 16.6667C7.03984 16.6667 6.66675 17.0398 6.66675 17.5C6.66675 17.9603 7.03984 18.3334 7.50008 18.3334Z" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M16.6666 18.3334C17.1268 18.3334 17.4999 17.9603 17.4999 17.5C17.4999 17.0398 17.1268 16.6667 16.6666 16.6667C16.2063 16.6667 15.8333 17.0398 15.8333 17.5C15.8333 17.9603 16.2063 18.3334 16.6666 18.3334Z" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M0.833252 0.833313H4.16659L6.39992 11.9916C6.47612 12.3753 6.68484 12.7199 6.98954 12.9652C7.29424 13.2105 7.6755 13.3408 8.06659 13.3333H16.1666C16.5577 13.3408 16.9389 13.2105 17.2436 12.9652C17.5483 12.7199 17.757 12.3753 17.8333 11.9916L19.1666 4.99998H4.99992" stroke="#2B2B2B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+<defs>
+<clipPath id="clip0_10_13">
+<rect width="20" height="20" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/src/pages/_app.js b/src/pages/_app.js
new file mode 100644
index 00000000..1e1cec92
--- /dev/null
+++ b/src/pages/_app.js
@@ -0,0 +1,7 @@
+import '../styles/globals.css'
+
+function MyApp({ Component, pageProps }) {
+ return <Component {...pageProps} />
+}
+
+export default MyApp
diff --git a/src/pages/index.js b/src/pages/index.js
new file mode 100644
index 00000000..57f96ec9
--- /dev/null
+++ b/src/pages/index.js
@@ -0,0 +1,12 @@
+import { useEffect, useState } from "react";
+import Header from "../components/header";
+
+export default function Home() {
+ const [product, setProduct] = useState({});
+
+ return (
+ <>
+ <Header />
+ </>
+ )
+}
diff --git a/src/pages/shop/product/[slug].js b/src/pages/shop/product/[slug].js
new file mode 100644
index 00000000..519f8160
--- /dev/null
+++ b/src/pages/shop/product/[slug].js
@@ -0,0 +1,172 @@
+import Link from "next/link";
+import { useRouter } from "next/router";
+import { useEffect, useState } from "react";
+import Header from "../../../components/header";
+import ProductCard from "../../../components/productCard";
+import { getOdoo } from "../../../helpers/apiOdoo";
+import { createSlug, getId } from "../../../helpers/slug";
+import currencyFormat from "../../../helpers/currencyFormat";
+import Head from "next/head";
+import { Swiper, SwiperSlide } from "swiper/react";
+
+import 'swiper/css';
+
+
+export async function getServerSideProps(context) {
+ const { slug } = context.query;
+ let product = await getOdoo('/api/v1/product/' + getId(slug));
+ product = product[0];
+
+ const similarProducts = await getOdoo(`/api/v1/product/${getId(slug)}/similar?limit=20`);
+
+ return {props: {product, similarProducts}};
+}
+
+export default function ProductDetail({product, similarProducts}) {
+ const router = useRouter();
+ const { slug } = router.query;
+ const [selectedVariant, setSelectedVariant] = useState("");
+ const [quantity, setQuantity] = useState("1");
+ const [activeVariant, setActiveVariant] = useState({
+ id: product.id,
+ code: product.code,
+ price: product.lowest_price,
+ stock: product.stock_total,
+ weight: product.weight,
+ attributes: '',
+ });
+
+ useEffect(() => {
+ if (product.variants.length == 1) {
+ setSelectedVariant(product.variants[0].id);
+ }
+ }, [product.variants]);
+
+ useEffect(() => {
+ if (selectedVariant != '') {
+ let newActiveVariant = product.variants.filter((variant) => {
+ return variant.id == selectedVariant;
+ });
+
+ if (newActiveVariant.length == 1) {
+ newActiveVariant = newActiveVariant[0];
+ setActiveVariant({
+ id: newActiveVariant.id,
+ code: newActiveVariant.code,
+ price: newActiveVariant.price,
+ stock: newActiveVariant.stock,
+ weight: newActiveVariant.weight,
+ attributes: newActiveVariant.attributes.join(', '),
+ });
+ }
+ }
+ }, [selectedVariant])
+
+ let onchangeVariant = (e) => {
+ setSelectedVariant(e.target.value);
+ setQuantity("1");
+ }
+
+ let addToCart = () => {
+ return true;
+ }
+
+ return (
+ <>
+ <Head>
+ <title>{product.name + '- Indoteknik'}</title>
+ </Head>
+ <Header />
+ <div className="px-4 pt-5 pb-10">
+ <img src={product.image} alt={product.name} className="border border-gray-300 rounded-md mb-4 w-full h-[300px] object-contain object-center" />
+ <Link href={'/shop/brands/' + createSlug(product.manufacture.name, product.manufacture.id)}>
+ {product.manufacture.name || '-'}
+ </Link>
+ <h1 className="mb-3">{product.name}{activeVariant.attributes != '' ? ' - ' + activeVariant.attributes : ''}</h1>
+
+ {product.variant_total > 1 && selectedVariant == "" ? (
+ <p className="text-xs text-gray-800 mb-1">Harga mulai dari:</p>
+ ) : ''}
+
+ {product.lowest_price.discount_percentage > 0 ? (
+ <div className="flex gap-x-1 items-center">
+ <span className="badge-yellow">{activeVariant.price.discount_percentage}%</span>
+ <p className="text-xs text-gray-800 line-through">{currencyFormat(activeVariant.price.price)}</p>
+ </div>
+ ) : ''}
+
+ {product.lowest_price.price > 0 ? (
+ <p className="text-lg text-gray-900 font-semibold">{currencyFormat(activeVariant.price.price_discount)}</p>
+ ) : (
+ <p className="text-gray-800">Dapatkan harga terbaik, <a href="">hubungi kami.</a></p>
+ )}
+
+ <div className="flex gap-x-2 mt-5">
+ <div className="w-9/12">
+ <label className="form-label">Pilih: <span className="text-gray-800">{product.variant_total} Varian</span></label>
+ <select name="variant" className="form-input" value={selectedVariant} onChange={onchangeVariant} >
+ <option value="" disabled={selectedVariant != "" ? true : false}>Pilih Varian...</option>
+ {product.variants.length > 1 ? (
+ product.variants.map((variant) => {
+ return (
+ <option key={variant.id} value={variant.id}>{variant.attributes.join(', ')}</option>
+ );
+ })
+ ) : (
+ <option key={product.variants[0].id} value={product.variants[0].id}>{product.variants[0].name}</option>
+ )}
+ </select>
+ </div>
+ <div className="w-3/12">
+ <label htmlFor="quantity" className="form-label">Jumlah</label>
+ <input type="number" name="quantity" id="quantity" className="form-input text-center is-invalid" value={quantity} onChange={(e) => setQuantity(e.target.value)} />
+ </div>
+ </div>
+
+ <div className="flex gap-x-2 mt-2">
+ <button className="btn-light w-full" >+ Quotation</button>
+ <button className="btn-primary w-full" onClick={addToCart} disabled={(product.lowest_price.price == 0 ? true : false)}>+ Keranjang</button>
+ </div>
+
+ <div className="mt-10">
+ <h2 className="h1 mb-2">Detail Produk</h2>
+ <div className="flex py-2 justify-between items-center gap-x-1 border-b border-gray-300">
+ <h3 className="text-gray-900">Jumlah Varian</h3>
+ <p className="text-gray-800">{product.variant_total} Varian</p>
+ </div>
+ <div className="flex py-2 justify-between items-center gap-x-1 border-b border-gray-300">
+ <h3 className="text-gray-900">Nomor SKU</h3>
+ <p className="text-gray-800" id="sku_number">SKU-{activeVariant.id}</p>
+ </div>
+ <div className="flex py-2 justify-between items-center gap-x-1 border-b border-gray-300">
+ <h3 className="text-gray-900">Part Number</h3>
+ <p className="text-gray-800" id="part_number">{activeVariant.code}</p>
+ </div>
+ <div className="flex py-2 justify-between items-center gap-x-1 border-b border-gray-300">
+ <h3 className="text-gray-900">Stok</h3>
+ <p className="text-gray-800" id="stock">
+ {activeVariant.stock > 0 ? (activeVariant.stock > 5 ? 'Lebih dari 5' : 'Kurang dari 5') : '0'}
+ </p>
+ </div>
+ <div className="flex py-2 justify-between items-center gap-x-1 border-b border-gray-300">
+ <h3 className="text-gray-900">Berat Barang</h3>
+ <p className="text-gray-800" id="weight">{activeVariant.weight > 0 ? activeVariant.weight : '1'} KG</p>
+ </div>
+ </div>
+
+ <div className="mt-10">
+ <h2 className="h1 mb-4">Deskripsi Produk</h2>
+ <div className="text-gray-800 leading-7" dangerouslySetInnerHTML={{__html: (product.description.trim() != '' ? product.description.replaceAll(/<*b>/g, '') : 'Belum ada deskripsi produk.')}}></div>
+ </div>
+
+ <div className="mt-10">
+ <h2 className="h1 mb-4">Produk Lainnya</h2>
+ <Swiper freeMode={true} slidesPerView={2.15} spaceBetween={8} loop={true}>
+ {similarProducts.products.map((product, index) => (<SwiperSlide key={index}><ProductCard data={product} /></SwiperSlide>))}
+ </Swiper>
+ </div>
+
+ </div>
+ </>
+ );
+} \ No newline at end of file
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 00000000..763a6d39
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,220 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+html, body {
+ @apply max-w-full;
+ @apply text-base;
+ @apply text-gray-900;
+}
+
+@layer base {
+ h1, .h1 {
+ @apply text-lg;
+ @apply font-semibold;
+ @apply text-gray-900;
+ }
+
+ a {
+ @apply font-medium;
+ @apply text-yellow-900;
+ }
+}
+
+@layer components {
+ .badge-red {
+ @apply text-xs;
+ @apply font-medium;
+ @apply px-1;
+ @apply py-0.5;
+ @apply rounded;
+ @apply bg-red-300;
+ @apply text-red-900;
+ }
+
+ .badge-yellow {
+ @apply text-xs;
+ @apply font-medium;
+ @apply px-1;
+ @apply py-0.5;
+ @apply rounded;
+ @apply bg-yellow-300;
+ @apply text-yellow-900;
+ }
+
+ .form-label {
+ @apply text-sm;
+ @apply mb-1;
+ @apply block;
+ }
+
+ .form-input {
+ @apply p-3;
+ @apply rounded;
+ @apply border;
+ @apply border-gray-300;
+ @apply bg-transparent;
+ @apply w-full;
+ @apply leading-none;
+ }
+
+ .btn-primary {
+ @apply p-3;
+ @apply bg-yellow-900;
+ @apply border-yellow-900;
+ @apply rounded;
+ @apply border;
+ @apply text-white;
+ @apply text-center;
+ @apply disabled:bg-yellow-700;
+ @apply disabled:border-yellow-700;
+ }
+
+ .btn-light {
+ @apply p-3;
+ @apply bg-gray-100;
+ @apply border-gray-300;
+ @apply rounded;
+ @apply border;
+ @apply text-gray-900;
+ @apply text-center;
+ }
+
+ .product-card {
+ @apply w-full;
+ @apply h-full;
+ @apply border;
+ @apply border-gray-300;
+ @apply rounded;
+ @apply relative;
+ @apply flex;
+ @apply flex-col;
+ }
+
+ .product-card__image {
+ @apply w-full;
+ @apply h-[160px];
+ @apply object-contain;
+ @apply object-center;
+ @apply border-b;
+ @apply border-gray-300;
+ }
+
+ .product-card__description {
+ @apply p-2;
+ @apply pb-3;
+ @apply flex;
+ @apply flex-col;
+ @apply flex-1;
+ @apply justify-between;
+ }
+
+ .product-card__title {
+ @apply text-sm;
+ @apply text-gray-900;
+ }
+
+ .product-card__brand {
+ @apply text-sm;
+ }
+}
+
+@layer utilities {
+ .wrap-line-ellipsis-2 {
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .wrap-line-ellipsis-3 {
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+}
+
+.menu-wrapper {
+ @apply fixed;
+ @apply top-0;
+ @apply left-0;
+ @apply bg-white;
+ @apply w-[85%];
+ @apply h-full;
+ @apply z-[60];
+ @apply py-4;
+ @apply px-4;
+ @apply overflow-y-auto;
+ @apply translate-x-[-100%];
+ @apply ease-linear;
+ @apply duration-150;
+}
+
+.menu-wrapper.active{
+ @apply translate-x-0;
+}
+
+.menu-overlay {
+ @apply fixed;
+ @apply top-0;
+ @apply left-0;
+ @apply w-full;
+ @apply h-full;
+ @apply z-[55];
+ @apply bg-gray-900/60;
+}
+
+.sticky-header {
+ @apply px-4;
+ @apply py-3;
+ @apply bg-white;
+ @apply border-b;
+ @apply sticky;
+ @apply top-0;
+ @apply shadow;
+ @apply z-50;
+}
+
+.content-container {
+ @apply max-w-full;
+ @apply overflow-x-hidden;
+}
+
+#indoteknik_toast {
+ @apply fixed;
+ @apply bottom-4;
+ @apply translate-y-[200%];
+ @apply left-[50%];
+ @apply translate-x-[-50%];
+ @apply z-[100];
+ @apply flex;
+ @apply items-center;
+ @apply p-4;
+ @apply mb-4;
+ @apply w-[90%];
+ @apply text-gray-500;
+ @apply bg-white;
+ @apply border;
+ @apply border-gray-300;
+ @apply rounded-lg;
+ @apply shadow;
+ @apply ease-linear;
+ @apply duration-300;
+}
+
+#indoteknik_toast.active {
+ @apply translate-y-0;
+}
+
+.category-menu {
+ @apply hidden;
+}
+
+.swiper-slide {
+ @apply !h-auto;
+} \ No newline at end of file