Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c7e360327 | |||
| 716ea56196 | |||
| d000453995 | |||
| d10397e028 | |||
| bebd6e5d0d | |||
| f709119a89 | |||
| 53dec4ecd2 | |||
| a872bc0819 | |||
| 5a33a38d8f | |||
| dd8d75750e |
@@ -0,0 +1,13 @@
|
||||
node_modules
|
||||
.next
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
.env*
|
||||
npm-debug.log*
|
||||
.DS_Store
|
||||
coverage
|
||||
.cursor
|
||||
.idea
|
||||
.vscode
|
||||
*.log
|
||||
+39
-71
@@ -1,104 +1,72 @@
|
||||
# ------------------------
|
||||
# Base image
|
||||
# ------------------------
|
||||
FROM node:22-alpine AS base
|
||||
|
||||
RUN apk add --no-cache tzdata \
|
||||
&& cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime \
|
||||
&& echo "Asia/Tehran" > /etc/timezone
|
||||
|
||||
WORKDIR /app
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ------------------------
|
||||
# Dependencies stage
|
||||
# Dependencies
|
||||
# ------------------------
|
||||
FROM base AS deps
|
||||
FROM node:22-alpine AS deps
|
||||
|
||||
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.arvancloud.ir/alpine|g' /etc/apk/repositories \
|
||||
&& npm config set registry https://package-mirror.liara.ir/repository/npm/
|
||||
|
||||
ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
||||
|
||||
RUN apk add --no-cache libc6-compat git
|
||||
|
||||
COPY package*.json ./
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm ci
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN --mount=type=cache,target=/root/.npm \
|
||||
npm ci
|
||||
|
||||
# ------------------------
|
||||
# Builder stage
|
||||
# Builder
|
||||
# ------------------------
|
||||
FROM base AS builder
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.arvancloud.ir/alpine|g' /etc/apk/repositories \
|
||||
&& npm config set registry https://package-mirror.liara.ir/repository/npm/
|
||||
|
||||
ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN NEXT_TELEMETRY_DISABLED=1 npm run build -- --no-lint || true
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# ------------------------
|
||||
# Runner stage (production)
|
||||
# Runner (production)
|
||||
# ------------------------
|
||||
FROM base AS runner
|
||||
FROM node:22-alpine AS runner
|
||||
|
||||
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.arvancloud.ir/alpine|g' /etc/apk/repositories \
|
||||
&& npm config set registry https://package-mirror.liara.ir/repository/npm/
|
||||
|
||||
ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
ENV NODE_ENV=production \
|
||||
NEXT_TELEMETRY_DISABLED=1 \
|
||||
PORT=3000 \
|
||||
HOSTNAME=0.0.0.0 \
|
||||
TZ=Asia/Tehran
|
||||
|
||||
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||
|
||||
RUN mkdir .next && chown appuser:appgroup .next
|
||||
RUN addgroup -S appgroup \
|
||||
&& adduser -S appuser -G appgroup \
|
||||
&& mkdir .next \
|
||||
&& chown appuser:appgroup .next
|
||||
|
||||
COPY --from=builder --chown=appuser:appgroup /build/.next/standalone ./
|
||||
COPY --from=builder --chown=appuser:appgroup /build/.next/static ./.next/static
|
||||
COPY --from=builder --chown=appuser:appgroup /build/public ./public
|
||||
COPY --from=builder --chown=appuser:appgroup /build/package.json ./package.json
|
||||
|
||||
USER appuser
|
||||
|
||||
# expose port
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
|
||||
# FROM node:22-alpine AS base
|
||||
|
||||
# RUN apk add --no-cache tzdata && \
|
||||
# cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime && \
|
||||
# echo "Asia/Tehran" > /etc/timezone
|
||||
|
||||
# WORKDIR /app
|
||||
|
||||
# FROM base AS deps
|
||||
# RUN apk add --no-cache libc6-compat git
|
||||
# COPY package*.json ./
|
||||
# RUN npm ci --only=production --ignore-scripts
|
||||
|
||||
# FROM base AS builder
|
||||
# WORKDIR /build
|
||||
# COPY --from=deps /app/node_modules ./node_modules
|
||||
# COPY . .
|
||||
# RUN npm run build && npm ci --only=production --ignore-scripts
|
||||
|
||||
# FROM base AS runner
|
||||
# WORKDIR /app
|
||||
|
||||
# ENV NODE_ENV=production
|
||||
# ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||
# RUN mkdir .next && chown appuser:appgroup .next
|
||||
|
||||
# COPY --from=builder --chown=appuser:appgroup /build/.next/standalone ./
|
||||
# COPY --from=builder --chown=appuser:appgroup /build/.next/static ./.next/static
|
||||
# COPY --from=builder --chown=appuser:appgroup /build/public ./public
|
||||
# COPY --from=builder --chown=appuser:appgroup /build/package.json ./package.json
|
||||
|
||||
# USER appuser
|
||||
|
||||
# EXPOSE 3000
|
||||
# ENV PORT=3000
|
||||
# ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
# CMD ["node", "server.js"]
|
||||
@@ -273,6 +273,11 @@ const ShippingMethodSelector = ({
|
||||
ارزانترین
|
||||
</span>
|
||||
)}
|
||||
{shipper.payDeliveryFeeOnDelivery && (
|
||||
<span className="text-xs bg-amber-100 text-amber-800 px-1.5 py-0.5 rounded flex-shrink-0">
|
||||
پرداخت در محل
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
زمان ارسال: {shipper.shippingDaysRange?.join(' تا ') || 'نامشخص'}
|
||||
@@ -282,14 +287,24 @@ const ShippingMethodSelector = ({
|
||||
|
||||
<div className="flex items-center justify-end sm:justify-start">
|
||||
<div className="font-medium text-gray-900 relative group text-right">
|
||||
{shipper.totalShippingCost.toLocaleString('fa-IR')} تومان
|
||||
{priceDifference > 0 && (
|
||||
<div className="text-xs text-orange-600 mt-1">
|
||||
+{priceDifference.toLocaleString('fa-IR')} تومان
|
||||
</div>
|
||||
{shipper.payDeliveryFeeOnDelivery ? (
|
||||
<span className="text-amber-700">
|
||||
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
{shipper.totalShippingCost.toLocaleString('fa-IR')} تومان
|
||||
{priceDifference > 0 && (
|
||||
<div className="text-xs text-orange-600 mt-1">
|
||||
+{priceDifference.toLocaleString('fa-IR')} تومان
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="absolute bottom-full right-0 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap z-10">
|
||||
هزینه ارسال کل این فروشگاه
|
||||
{shipper.payDeliveryFeeOnDelivery
|
||||
? 'هزینه ارسال هنگام تحویل پرداخت میشود'
|
||||
: 'هزینه ارسال کل این فروشگاه'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import TitleBack from '../components/TitleBack'
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||
import DiscountCard from '../components/DiscountCard'
|
||||
import { usePaymentMethods, useGetCart, useShippingCost, useCheckoutCart } from '../hooks/useCartData'
|
||||
import { ShopShippingType } from '../types/Types'
|
||||
import { useState } from 'react'
|
||||
import { toast } from '@/components/Toast'
|
||||
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||
@@ -50,12 +51,24 @@ const CartPayment: NextPage = () => {
|
||||
const savedShipments = typeof window !== 'undefined' ? localStorage.getItem('selectedShipments') : null
|
||||
const selectedShipments = savedShipments ? JSON.parse(savedShipments) : {}
|
||||
|
||||
// محاسبه هزینه ارسال بر اساس انتخابهای کاربر
|
||||
const totalShippingCost = shippingData?.results?.shipping?.reduce((total, shop) => {
|
||||
const getSelectedShipper = (shop: ShopShippingType) => {
|
||||
const selectedShipperId = selectedShipments[shop.shopId]
|
||||
const selectedShipper = shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId)
|
||||
return total + (selectedShipper?.totalShippingCost || shop.shippers?.[0]?.totalShippingCost || 0)
|
||||
}, 0) || 0
|
||||
return shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId) || shop.shippers?.[0]
|
||||
}
|
||||
|
||||
const shippingTotals = shippingData?.results?.shipping?.reduce((totals, shop) => {
|
||||
const shipper = getSelectedShipper(shop)
|
||||
if (!shipper) return totals
|
||||
if (shipper.payDeliveryFeeOnDelivery) {
|
||||
totals.onDelivery += shipper.totalShippingCost
|
||||
} else {
|
||||
totals.online += shipper.totalShippingCost
|
||||
}
|
||||
return totals
|
||||
}, { online: 0, onDelivery: 0 }) || { online: 0, onDelivery: 0 }
|
||||
|
||||
const totalShippingCost = shippingTotals.online
|
||||
const shippingCostOnDelivery = shippingTotals.onDelivery
|
||||
|
||||
// محاسبه قیمت نهایی
|
||||
const finalTotal = (cart?.payable_price || 0) + totalShippingCost
|
||||
@@ -132,6 +145,7 @@ const CartPayment: NextPage = () => {
|
||||
itemsPrice={cart?.retail_price || 0}
|
||||
discount={cart.total_discount}
|
||||
shippingCost={totalShippingCost}
|
||||
shippingCostOnDelivery={shippingCostOnDelivery}
|
||||
total={finalTotal}
|
||||
onConfirm={handlePayment}
|
||||
confirmLabel="پرداخت"
|
||||
|
||||
@@ -7,6 +7,7 @@ import TitleBack from '../components/TitleBack'
|
||||
import ShippingMethodSelector from '../components/ShippingMethodSelector'
|
||||
import { Location } from 'iconsax-react'
|
||||
import { useGetCart, useSaveShipmentCart, useShippingCost } from '../hooks/useCartData'
|
||||
import { ShopShippingType } from '../types/Types'
|
||||
// import { CartItemType } from '../types/Types'
|
||||
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||
import Link from 'next/link'
|
||||
@@ -143,11 +144,24 @@ const CartShipping: NextPage = () => {
|
||||
}
|
||||
|
||||
// محاسبه هزینه ارسال بر اساس انتخابهای کاربر
|
||||
const totalShippingCost = shippingData?.results?.shipping?.reduce((total, shop) => {
|
||||
const getSelectedShipper = (shop: ShopShippingType) => {
|
||||
const selectedShipperId = selectedShipments[shop.shopId]
|
||||
const selectedShipper = shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId)
|
||||
return total + (selectedShipper?.totalShippingCost || shop.shippers?.[0]?.totalShippingCost || 0)
|
||||
}, 0) || 0
|
||||
return shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId) || shop.shippers?.[0]
|
||||
}
|
||||
|
||||
const shippingTotals = shippingData?.results?.shipping?.reduce((totals, shop) => {
|
||||
const shipper = getSelectedShipper(shop)
|
||||
if (!shipper) return totals
|
||||
if (shipper.payDeliveryFeeOnDelivery) {
|
||||
totals.onDelivery += shipper.totalShippingCost
|
||||
} else {
|
||||
totals.online += shipper.totalShippingCost
|
||||
}
|
||||
return totals
|
||||
}, { online: 0, onDelivery: 0 }) || { online: 0, onDelivery: 0 }
|
||||
|
||||
const totalShippingCost = shippingTotals.online
|
||||
const shippingCostOnDelivery = shippingTotals.onDelivery
|
||||
|
||||
// محاسبه قیمت نهایی
|
||||
const finalTotal = (cart?.payable_price || 0) + totalShippingCost
|
||||
@@ -205,6 +219,7 @@ const CartShipping: NextPage = () => {
|
||||
itemsPrice={cart?.retail_price || 0}
|
||||
discount={cart?.total_discount || 0}
|
||||
shippingCost={totalShippingCost}
|
||||
shippingCostOnDelivery={shippingCostOnDelivery}
|
||||
total={finalTotal}
|
||||
onConfirm={handleProceedToPayment}
|
||||
confirmLabel="پرداخت"
|
||||
|
||||
@@ -60,6 +60,7 @@ export type CartItemType = {
|
||||
description: string;
|
||||
deliveryTime: number;
|
||||
deliveryType: string;
|
||||
payDeliveryFeeOnDelivery?: boolean;
|
||||
}>;
|
||||
warranty: {
|
||||
_id: number;
|
||||
@@ -224,6 +225,7 @@ export type ShipperType = {
|
||||
shipperName: string;
|
||||
shippingDaysRange: string[];
|
||||
totalShippingCost: number;
|
||||
payDeliveryFeeOnDelivery: boolean;
|
||||
items: ShippingItemType[];
|
||||
};
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ const OrderInvoicePage = () => {
|
||||
const totalDiscount = order.payment.priceDetails.total_discount
|
||||
const couponDiscount = order.payment.priceDetails.coupon_discount
|
||||
const shippingCost = order.payment.priceDetails.shipping_cost
|
||||
const shippingCostOnDelivery = order.payment.priceDetails.shipping_cost_on_delivery || 0
|
||||
const totalPrice = order.payment.totalPrice
|
||||
|
||||
return (
|
||||
@@ -212,6 +213,12 @@ const OrderInvoicePage = () => {
|
||||
<span>هزینه ارسال:</span>
|
||||
<span>{NumberFormat(shippingCost)} تومان</span>
|
||||
</div>
|
||||
{shippingCostOnDelivery > 0 && (
|
||||
<div className="flex justify-between text-gray-700">
|
||||
<span>هزینه ارسال (پرداخت در محل):</span>
|
||||
{/* <span>{NumberFormat(shippingCostOnDelivery)} تومان</span> */}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between text-xl font-bold border-t pt-3">
|
||||
<span>جمع کل:</span>
|
||||
<span className="text-primary">
|
||||
|
||||
@@ -94,6 +94,7 @@ export interface PaymentMethod {
|
||||
|
||||
export interface PriceDetails {
|
||||
shipping_cost: number;
|
||||
shipping_cost_on_delivery?: number;
|
||||
process_cost: number;
|
||||
total_retail_price: number;
|
||||
total_payable_price: number;
|
||||
|
||||
@@ -10,6 +10,7 @@ type CartSummaryProps = {
|
||||
itemsPrice: number;
|
||||
discount: number;
|
||||
shippingCost?: number;
|
||||
shippingCostOnDelivery?: number;
|
||||
total: number;
|
||||
onConfirm?: () => void;
|
||||
confirmHref?: string;
|
||||
@@ -34,7 +35,7 @@ const Row: FC<{ label: string; value: string | number; emphasize?: boolean }> =
|
||||
};
|
||||
|
||||
const CartSummary: FC<CartSummaryProps> = (props) => {
|
||||
const { itemsCount, itemsPrice, discount, shippingCost, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel, confirmDisabled, isLoading } = props;
|
||||
const { itemsCount, itemsPrice, discount, shippingCost, shippingCostOnDelivery, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel, confirmDisabled, isLoading } = props;
|
||||
|
||||
return (
|
||||
<div className={"rounded-xl sm:rounded-2xl border border-border p-4 sm:p-6 h-fit bg-[#FAFAFA] " + (className ?? "")}>
|
||||
@@ -49,6 +50,9 @@ const CartSummary: FC<CartSummaryProps> = (props) => {
|
||||
{shippingCost !== undefined && shippingCost > 0 && (
|
||||
<Row label="هزینه ارسال" value={shippingCost} />
|
||||
)}
|
||||
{shippingCostOnDelivery !== undefined && shippingCostOnDelivery > 0 && (
|
||||
<Row label="هزینه ارسال (پرداخت در محل)" value={shippingCostOnDelivery} />
|
||||
)}
|
||||
<Row label="جمع فاکتور" value={total} emphasize />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -68,11 +68,17 @@ const Input: FC<Props> = (props: Props) => {
|
||||
}
|
||||
}, [props.value])
|
||||
|
||||
useEffect(() => {
|
||||
if (props.variant === 'search') {
|
||||
setSearch(String(props.value ?? ''))
|
||||
}
|
||||
}, [props.value, props.variant])
|
||||
|
||||
useEffect(() => {
|
||||
if (props.variant === 'search' && props.onChangeSearchFinal) {
|
||||
const timeout = setTimeout(() => {
|
||||
props.onChangeSearchFinal?.(search)
|
||||
}, 1000)
|
||||
}, 500)
|
||||
return () => clearTimeout(timeout)
|
||||
}
|
||||
}, [search, props])
|
||||
|
||||
+4
-4
@@ -7,8 +7,8 @@ export const PRIMARY_COLOR = "#DA2129";
|
||||
|
||||
// تنظیمات فروشگاه
|
||||
export const SHOP_CONFIG = {
|
||||
name: "تابلو مرکزی", // نام فروشگاه - میتوان آن را تغییر داد
|
||||
fullName: "فجر تابلو مرکزی", // نام کامل فروشگاه
|
||||
englishName: "Fajr Tablo Center", // نام کامل فروشگاه به زبان انگلیسی
|
||||
description: "فروشگاه آنلاین فجر تابلو مرکزی", // توضیحات فروشگاه
|
||||
name: "فجر مرکزی", // نام فروشگاه - میتوان آن را تغییر داد
|
||||
fullName: "تابلو فجر مرکزی", // نام کامل فروشگاه
|
||||
englishName: "Tablo Fajr Center", // نام کامل فروشگاه به زبان انگلیسی
|
||||
description: "فروشگاه آنلاین تابلو فجر مرکزی", // توضیحات فروشگاه
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import Image from 'next/image'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
const DesktopSearch: FC = () => {
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [mounted, setMounted] = useState(false)
|
||||
@@ -16,13 +17,14 @@ const DesktopSearch: FC = () => {
|
||||
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 })
|
||||
|
||||
const { data: searchResults, isLoading } = useProductSearch(searchQuery)
|
||||
const isSearching = inputValue.length > 0 && (isLoading || inputValue !== searchQuery)
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearchQuery(value)
|
||||
const handleInputChange = (value: string) => {
|
||||
setInputValue(value)
|
||||
setIsOpen(value.length > 0)
|
||||
|
||||
if (value.length > 0 && inputRef.current) {
|
||||
@@ -37,6 +39,7 @@ const DesktopSearch: FC = () => {
|
||||
|
||||
const handleProductClick = (url: string) => {
|
||||
router.push(url)
|
||||
setInputValue('')
|
||||
setSearchQuery('')
|
||||
setIsOpen(false)
|
||||
}
|
||||
@@ -79,12 +82,13 @@ const DesktopSearch: FC = () => {
|
||||
className='w-full'
|
||||
variant='search'
|
||||
placeholder='جستجو'
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
value={inputValue}
|
||||
onChange={(e) => handleInputChange(e.target.value)}
|
||||
onChangeSearchFinal={setSearchQuery}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{mounted && isOpen && searchQuery.length > 0 && createPortal(
|
||||
{mounted && isOpen && inputValue.length > 0 && createPortal(
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className='fixed bg-white rounded-xl shadow-lg border border-gray-200 max-h-[400px] overflow-y-auto z-[99999]'
|
||||
@@ -94,7 +98,7 @@ const DesktopSearch: FC = () => {
|
||||
width: `${dropdownPosition.width}px`
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
{isSearching ? (
|
||||
<div className='p-4 text-center text-gray-500'>
|
||||
در حال جستجو...
|
||||
</div>
|
||||
|
||||
@@ -8,17 +8,16 @@ import Image from 'next/image'
|
||||
|
||||
const MobileSearch: FC = () => {
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false)
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const router = useRouter()
|
||||
|
||||
const { data: searchResults, isLoading } = useProductSearch(searchQuery)
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearchQuery(value)
|
||||
}
|
||||
const isSearching = inputValue.length > 0 && (isLoading || inputValue !== searchQuery)
|
||||
|
||||
const handleClose = () => {
|
||||
setIsSearchOpen(false)
|
||||
setInputValue('')
|
||||
setSearchQuery('')
|
||||
}
|
||||
|
||||
@@ -36,8 +35,9 @@ const MobileSearch: FC = () => {
|
||||
variant='search'
|
||||
placeholder='جستجو'
|
||||
autoFocus
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onChangeSearchFinal={setSearchQuery}
|
||||
/>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
@@ -48,9 +48,9 @@ const MobileSearch: FC = () => {
|
||||
</div>
|
||||
|
||||
<div className='flex-1 overflow-y-auto'>
|
||||
{searchQuery.length > 0 && (
|
||||
{inputValue.length > 0 && (
|
||||
<>
|
||||
{isLoading ? (
|
||||
{isSearching ? (
|
||||
<div className='p-4 text-center text-gray-500'>
|
||||
در حال جستجو...
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user