Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d12fe6c4d | |||
| ce10d3ea57 | |||
| 0e2ce1210d | |||
| bba527c83f | |||
| 8bc5930bd6 | |||
| 38ffe6cd43 |
@@ -1,13 +0,0 @@
|
|||||||
node_modules
|
|
||||||
.next
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
*.md
|
|
||||||
.env*
|
|
||||||
npm-debug.log*
|
|
||||||
.DS_Store
|
|
||||||
coverage
|
|
||||||
.cursor
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
*.log
|
|
||||||
@@ -3,7 +3,7 @@ name: deploy to danak
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- shinan
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_deploy:
|
build_and_deploy:
|
||||||
@@ -11,10 +11,8 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
DANAK_SERVER: "https://captain.dev.danakcorp.com"
|
DANAK_SERVER: "https://captain.dev.danakcorp.com"
|
||||||
APP_TOKEN: 230cbe758f711c2b12099fce48ef8d4f2212ac6dc89cb300e92a9b8a45133669
|
APP_TOKEN: 9d1dd69b08c1d77b36092597316c44288126c57f217d87308130c98d764bf891
|
||||||
APP_NAME: shop
|
APP_NAME: shinan
|
||||||
# APP_TOKEN: 111ac60c637716478be8ab8b888fd20baba808a10b0fc0c18bf788766883ec9e
|
|
||||||
# APP_NAME: e-commerce-front
|
|
||||||
GITHUB_TOKEN: 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
|
GITHUB_TOKEN: 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+26
-39
@@ -1,72 +1,59 @@
|
|||||||
# syntax=docker/dockerfile:1
|
|
||||||
|
|
||||||
# ------------------------
|
# ------------------------
|
||||||
# Dependencies
|
# Base image
|
||||||
# ------------------------
|
# ------------------------
|
||||||
FROM node:22-alpine AS deps
|
FROM node:22-alpine AS base
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
# ------------------------
|
||||||
|
# Dependencies stage
|
||||||
|
# ------------------------
|
||||||
|
FROM base AS deps
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.npm \
|
RUN apk add --no-cache libc6-compat git
|
||||||
npm ci
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
# ------------------------
|
# ------------------------
|
||||||
# Builder
|
# Builder stage
|
||||||
# ------------------------
|
# ------------------------
|
||||||
FROM node:22-alpine AS builder
|
FROM base 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
|
WORKDIR /build
|
||||||
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
RUN NEXT_TELEMETRY_DISABLED=1 npm run build -- --no-lint || true
|
||||||
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# ------------------------
|
# ------------------------
|
||||||
# Runner (production)
|
# Runner stage (production)
|
||||||
# ------------------------
|
# ------------------------
|
||||||
FROM node:22-alpine AS runner
|
FROM base 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
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production
|
||||||
NEXT_TELEMETRY_DISABLED=1 \
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
PORT=3000 \
|
ENV PORT=3000
|
||||||
HOSTNAME=0.0.0.0 \
|
ENV HOSTNAME="0.0.0.0"
|
||||||
TZ=Asia/Tehran
|
|
||||||
|
|
||||||
RUN addgroup -S appgroup \
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||||
&& adduser -S appuser -G appgroup \
|
|
||||||
&& mkdir .next \
|
RUN mkdir .next && chown appuser:appgroup .next
|
||||||
&& chown appuser:appgroup .next
|
|
||||||
|
|
||||||
COPY --from=builder --chown=appuser:appgroup /build/.next/standalone ./
|
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/.next/static ./.next/static
|
||||||
COPY --from=builder --chown=appuser:appgroup /build/public ./public
|
COPY --from=builder --chown=appuser:appgroup /build/public ./public
|
||||||
|
COPY --from=builder --chown=appuser:appgroup /build/package.json ./package.json
|
||||||
|
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
|
# expose port
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 550 KiB |
@@ -273,11 +273,6 @@ const ShippingMethodSelector = ({
|
|||||||
ارزانترین
|
ارزانترین
|
||||||
</span>
|
</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>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
زمان ارسال: {shipper.shippingDaysRange?.join(' تا ') || 'نامشخص'}
|
زمان ارسال: {shipper.shippingDaysRange?.join(' تا ') || 'نامشخص'}
|
||||||
@@ -287,24 +282,14 @@ const ShippingMethodSelector = ({
|
|||||||
|
|
||||||
<div className="flex items-center justify-end sm:justify-start">
|
<div className="flex items-center justify-end sm:justify-start">
|
||||||
<div className="font-medium text-gray-900 relative group text-right">
|
<div className="font-medium text-gray-900 relative group text-right">
|
||||||
{shipper.payDeliveryFeeOnDelivery ? (
|
{shipper.totalShippingCost.toLocaleString('fa-IR')} تومان
|
||||||
<span className="text-amber-700">
|
{priceDifference > 0 && (
|
||||||
|
<div className="text-xs text-orange-600 mt-1">
|
||||||
</span>
|
+{priceDifference.toLocaleString('fa-IR')} تومان
|
||||||
) : (
|
</div>
|
||||||
<>
|
|
||||||
{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">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const InvoicePage = () => {
|
|||||||
<div className="flex items-center justify-between border-b pb-4 mb-6">
|
<div className="flex items-center justify-between border-b pb-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<Image
|
<Image
|
||||||
src="/images/logo.png"
|
src="/images/logo.svg"
|
||||||
alt="لوگو"
|
alt="لوگو"
|
||||||
width={120}
|
width={120}
|
||||||
height={40}
|
height={40}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import TitleBack from '../components/TitleBack'
|
|||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||||
import DiscountCard from '../components/DiscountCard'
|
import DiscountCard from '../components/DiscountCard'
|
||||||
import { usePaymentMethods, useGetCart, useShippingCost, useCheckoutCart } from '../hooks/useCartData'
|
import { usePaymentMethods, useGetCart, useShippingCost, useCheckoutCart } from '../hooks/useCartData'
|
||||||
import { ShopShippingType } from '../types/Types'
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { toast } from '@/components/Toast'
|
import { toast } from '@/components/Toast'
|
||||||
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||||
@@ -51,24 +50,12 @@ const CartPayment: NextPage = () => {
|
|||||||
const savedShipments = typeof window !== 'undefined' ? localStorage.getItem('selectedShipments') : null
|
const savedShipments = typeof window !== 'undefined' ? localStorage.getItem('selectedShipments') : null
|
||||||
const selectedShipments = savedShipments ? JSON.parse(savedShipments) : {}
|
const selectedShipments = savedShipments ? JSON.parse(savedShipments) : {}
|
||||||
|
|
||||||
const getSelectedShipper = (shop: ShopShippingType) => {
|
// محاسبه هزینه ارسال بر اساس انتخابهای کاربر
|
||||||
|
const totalShippingCost = shippingData?.results?.shipping?.reduce((total, shop) => {
|
||||||
const selectedShipperId = selectedShipments[shop.shopId]
|
const selectedShipperId = selectedShipments[shop.shopId]
|
||||||
return shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId) || shop.shippers?.[0]
|
const selectedShipper = shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId)
|
||||||
}
|
return total + (selectedShipper?.totalShippingCost || shop.shippers?.[0]?.totalShippingCost || 0)
|
||||||
|
}, 0) || 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
|
const finalTotal = (cart?.payable_price || 0) + totalShippingCost
|
||||||
@@ -145,7 +132,6 @@ const CartPayment: NextPage = () => {
|
|||||||
itemsPrice={cart?.retail_price || 0}
|
itemsPrice={cart?.retail_price || 0}
|
||||||
discount={cart.total_discount}
|
discount={cart.total_discount}
|
||||||
shippingCost={totalShippingCost}
|
shippingCost={totalShippingCost}
|
||||||
shippingCostOnDelivery={shippingCostOnDelivery}
|
|
||||||
total={finalTotal}
|
total={finalTotal}
|
||||||
onConfirm={handlePayment}
|
onConfirm={handlePayment}
|
||||||
confirmLabel="پرداخت"
|
confirmLabel="پرداخت"
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import TitleBack from '../components/TitleBack'
|
|||||||
import ShippingMethodSelector from '../components/ShippingMethodSelector'
|
import ShippingMethodSelector from '../components/ShippingMethodSelector'
|
||||||
import { Location } from 'iconsax-react'
|
import { Location } from 'iconsax-react'
|
||||||
import { useGetCart, useSaveShipmentCart, useShippingCost } from '../hooks/useCartData'
|
import { useGetCart, useSaveShipmentCart, useShippingCost } from '../hooks/useCartData'
|
||||||
import { ShopShippingType } from '../types/Types'
|
|
||||||
// import { CartItemType } from '../types/Types'
|
// import { CartItemType } from '../types/Types'
|
||||||
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@@ -144,24 +143,11 @@ const CartShipping: NextPage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// محاسبه هزینه ارسال بر اساس انتخابهای کاربر
|
// محاسبه هزینه ارسال بر اساس انتخابهای کاربر
|
||||||
const getSelectedShipper = (shop: ShopShippingType) => {
|
const totalShippingCost = shippingData?.results?.shipping?.reduce((total, shop) => {
|
||||||
const selectedShipperId = selectedShipments[shop.shopId]
|
const selectedShipperId = selectedShipments[shop.shopId]
|
||||||
return shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId) || shop.shippers?.[0]
|
const selectedShipper = shop.shippers?.find(shipper => shipper.shipperId === selectedShipperId)
|
||||||
}
|
return total + (selectedShipper?.totalShippingCost || shop.shippers?.[0]?.totalShippingCost || 0)
|
||||||
|
}, 0) || 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
|
const finalTotal = (cart?.payable_price || 0) + totalShippingCost
|
||||||
@@ -219,7 +205,6 @@ const CartShipping: NextPage = () => {
|
|||||||
itemsPrice={cart?.retail_price || 0}
|
itemsPrice={cart?.retail_price || 0}
|
||||||
discount={cart?.total_discount || 0}
|
discount={cart?.total_discount || 0}
|
||||||
shippingCost={totalShippingCost}
|
shippingCost={totalShippingCost}
|
||||||
shippingCostOnDelivery={shippingCostOnDelivery}
|
|
||||||
total={finalTotal}
|
total={finalTotal}
|
||||||
onConfirm={handleProceedToPayment}
|
onConfirm={handleProceedToPayment}
|
||||||
confirmLabel="پرداخت"
|
confirmLabel="پرداخت"
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ export type CartItemType = {
|
|||||||
description: string;
|
description: string;
|
||||||
deliveryTime: number;
|
deliveryTime: number;
|
||||||
deliveryType: string;
|
deliveryType: string;
|
||||||
payDeliveryFeeOnDelivery?: boolean;
|
|
||||||
}>;
|
}>;
|
||||||
warranty: {
|
warranty: {
|
||||||
_id: number;
|
_id: number;
|
||||||
@@ -225,7 +224,6 @@ export type ShipperType = {
|
|||||||
shipperName: string;
|
shipperName: string;
|
||||||
shippingDaysRange: string[];
|
shippingDaysRange: string[];
|
||||||
totalShippingCost: number;
|
totalShippingCost: number;
|
||||||
payDeliveryFeeOnDelivery: boolean;
|
|
||||||
items: ShippingItemType[];
|
items: ShippingItemType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -173,7 +173,7 @@ body {
|
|||||||
}
|
}
|
||||||
@theme {
|
@theme {
|
||||||
--color-border: #e5e5e5;
|
--color-border: #e5e5e5;
|
||||||
--color-primary: #da2129;
|
--color-primary: #a62186;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ const OrderInvoicePage = () => {
|
|||||||
const totalDiscount = order.payment.priceDetails.total_discount
|
const totalDiscount = order.payment.priceDetails.total_discount
|
||||||
const couponDiscount = order.payment.priceDetails.coupon_discount
|
const couponDiscount = order.payment.priceDetails.coupon_discount
|
||||||
const shippingCost = order.payment.priceDetails.shipping_cost
|
const shippingCost = order.payment.priceDetails.shipping_cost
|
||||||
const shippingCostOnDelivery = order.payment.priceDetails.shipping_cost_on_delivery || 0
|
|
||||||
const totalPrice = order.payment.totalPrice
|
const totalPrice = order.payment.totalPrice
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -67,7 +66,7 @@ const OrderInvoicePage = () => {
|
|||||||
<div className="flex items-center justify-between border-b pb-4 mb-6">
|
<div className="flex items-center justify-between border-b pb-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<Image
|
<Image
|
||||||
src="/images/logo.png"
|
src="/images/logo.svg"
|
||||||
alt="لوگو"
|
alt="لوگو"
|
||||||
width={120}
|
width={120}
|
||||||
height={40}
|
height={40}
|
||||||
@@ -213,12 +212,6 @@ const OrderInvoicePage = () => {
|
|||||||
<span>هزینه ارسال:</span>
|
<span>هزینه ارسال:</span>
|
||||||
<span>{NumberFormat(shippingCost)} تومان</span>
|
<span>{NumberFormat(shippingCost)} تومان</span>
|
||||||
</div>
|
</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">
|
<div className="flex justify-between text-xl font-bold border-t pt-3">
|
||||||
<span>جمع کل:</span>
|
<span>جمع کل:</span>
|
||||||
<span className="text-primary">
|
<span className="text-primary">
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ export interface PaymentMethod {
|
|||||||
|
|
||||||
export interface PriceDetails {
|
export interface PriceDetails {
|
||||||
shipping_cost: number;
|
shipping_cost: number;
|
||||||
shipping_cost_on_delivery?: number;
|
|
||||||
process_cost: number;
|
process_cost: number;
|
||||||
total_retail_price: number;
|
total_retail_price: number;
|
||||||
total_payable_price: number;
|
total_payable_price: number;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ type CartSummaryProps = {
|
|||||||
itemsPrice: number;
|
itemsPrice: number;
|
||||||
discount: number;
|
discount: number;
|
||||||
shippingCost?: number;
|
shippingCost?: number;
|
||||||
shippingCostOnDelivery?: number;
|
|
||||||
total: number;
|
total: number;
|
||||||
onConfirm?: () => void;
|
onConfirm?: () => void;
|
||||||
confirmHref?: string;
|
confirmHref?: string;
|
||||||
@@ -35,7 +34,7 @@ const Row: FC<{ label: string; value: string | number; emphasize?: boolean }> =
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CartSummary: FC<CartSummaryProps> = (props) => {
|
const CartSummary: FC<CartSummaryProps> = (props) => {
|
||||||
const { itemsCount, itemsPrice, discount, shippingCost, shippingCostOnDelivery, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel, confirmDisabled, isLoading } = props;
|
const { itemsCount, itemsPrice, discount, shippingCost, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel, confirmDisabled, isLoading } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"rounded-xl sm:rounded-2xl border border-border p-4 sm:p-6 h-fit bg-[#FAFAFA] " + (className ?? "")}>
|
<div className={"rounded-xl sm:rounded-2xl border border-border p-4 sm:p-6 h-fit bg-[#FAFAFA] " + (className ?? "")}>
|
||||||
@@ -50,9 +49,6 @@ const CartSummary: FC<CartSummaryProps> = (props) => {
|
|||||||
{shippingCost !== undefined && shippingCost > 0 && (
|
{shippingCost !== undefined && shippingCost > 0 && (
|
||||||
<Row label="هزینه ارسال" value={shippingCost} />
|
<Row label="هزینه ارسال" value={shippingCost} />
|
||||||
)}
|
)}
|
||||||
{shippingCostOnDelivery !== undefined && shippingCostOnDelivery > 0 && (
|
|
||||||
<Row label="هزینه ارسال (پرداخت در محل)" value={shippingCostOnDelivery} />
|
|
||||||
)}
|
|
||||||
<Row label="جمع فاکتور" value={total} emphasize />
|
<Row label="جمع فاکتور" value={total} emphasize />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -68,17 +68,11 @@ const Input: FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
}, [props.value])
|
}, [props.value])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.variant === 'search') {
|
|
||||||
setSearch(String(props.value ?? ''))
|
|
||||||
}
|
|
||||||
}, [props.value, props.variant])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.variant === 'search' && props.onChangeSearchFinal) {
|
if (props.variant === 'search' && props.onChangeSearchFinal) {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
props.onChangeSearchFinal?.(search)
|
props.onChangeSearchFinal?.(search)
|
||||||
}, 500)
|
}, 1000)
|
||||||
return () => clearTimeout(timeout)
|
return () => clearTimeout(timeout)
|
||||||
}
|
}
|
||||||
}, [search, props])
|
}, [search, props])
|
||||||
|
|||||||
+8
-8
@@ -1,14 +1,14 @@
|
|||||||
export const TOKEN_NAME = "sh_token";
|
export const TOKEN_NAME = "shinan_token";
|
||||||
export const REFRESH_TOKEN_NAME = "sh_refresh_token";
|
export const REFRESH_TOKEN_NAME = "shinan_refresh_token";
|
||||||
export const BASE_URL = "https://api.fajrtabloshop.com";
|
export const BASE_URL = "https://shinan-api.danakcorp.com";
|
||||||
// export const BASE_URL = "http://192.168.99.235:4000";
|
// export const BASE_URL = "http://192.168.99.235:4000";
|
||||||
|
|
||||||
export const PRIMARY_COLOR = "#DA2129";
|
export const PRIMARY_COLOR = "#a62186";
|
||||||
|
|
||||||
// تنظیمات فروشگاه
|
// تنظیمات فروشگاه
|
||||||
export const SHOP_CONFIG = {
|
export const SHOP_CONFIG = {
|
||||||
name: "فجر مرکزی", // نام فروشگاه - میتوان آن را تغییر داد
|
name: "شینان", // نام فروشگاه - میتوان آن را تغییر داد
|
||||||
fullName: "تابلو فجر مرکزی", // نام کامل فروشگاه
|
fullName: "شینان", // نام کامل فروشگاه
|
||||||
englishName: "Tablo Fajr Center", // نام کامل فروشگاه به زبان انگلیسی
|
englishName: "Shinan", // نام کامل فروشگاه به زبان انگلیسی
|
||||||
description: "فروشگاه آنلاین تابلو فجر مرکزی", // توضیحات فروشگاه
|
description: "فروشگاه آنلاین شینان", // توضیحات فروشگاه
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const Footer: FC = () => {
|
|||||||
width={100}
|
width={100}
|
||||||
height={100}
|
height={100}
|
||||||
alt='logo'
|
alt='logo'
|
||||||
src={siteSetting?.results?.siteSetting?.siteLogo || '/images/logo.png'}
|
src={siteSetting?.results?.siteSetting?.siteLogo || '/images/logo.svg'}
|
||||||
className='w-full h-auto max-h-20 max-w-20 object-contain'
|
className='w-full h-auto max-h-20 max-w-20 object-contain'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const Header: FC = () => {
|
|||||||
<div className='md:hidden flex items-center justify-between relative'>
|
<div className='md:hidden flex items-center justify-between relative'>
|
||||||
<Link href='/' className='px-4 py-4'>
|
<Link href='/' className='px-4 py-4'>
|
||||||
<Image
|
<Image
|
||||||
src='/images/logo.png'
|
src='/images/logo.svg'
|
||||||
alt='logo'
|
alt='logo'
|
||||||
width={120}
|
width={120}
|
||||||
height={50}
|
height={50}
|
||||||
@@ -49,7 +49,7 @@ const Header: FC = () => {
|
|||||||
<div className='flex gap-[30px] items-center'>
|
<div className='flex gap-[30px] items-center'>
|
||||||
<Link href='/'>
|
<Link href='/'>
|
||||||
<Image
|
<Image
|
||||||
src='/images/logo.png'
|
src='/images/logo.svg'
|
||||||
alt='logo'
|
alt='logo'
|
||||||
width={100}
|
width={100}
|
||||||
height={100}
|
height={100}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ const CartItemCard = ({
|
|||||||
|
|
||||||
const productImage = isOnline
|
const productImage = isOnline
|
||||||
? (item as CartItemType).product.imagesUrl.cover
|
? (item as CartItemType).product.imagesUrl.cover
|
||||||
: (item as EnrichedOfflineCartItem).product?.imagesUrl?.cover || '/images/logo.png'
|
: (item as EnrichedOfflineCartItem).product?.imagesUrl?.cover || '/images/logo.svg'
|
||||||
|
|
||||||
const productCategory = isOnline
|
const productCategory = isOnline
|
||||||
? ((item as CartItemType).product.category?._id || 'دستهبندی')
|
? ((item as CartItemType).product.category?._id || 'دستهبندی')
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import Image from 'next/image'
|
|||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
const DesktopSearch: FC = () => {
|
const DesktopSearch: FC = () => {
|
||||||
const [inputValue, setInputValue] = useState('')
|
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [mounted, setMounted] = useState(false)
|
const [mounted, setMounted] = useState(false)
|
||||||
@@ -17,14 +16,13 @@ const DesktopSearch: FC = () => {
|
|||||||
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 })
|
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 })
|
||||||
|
|
||||||
const { data: searchResults, isLoading } = useProductSearch(searchQuery)
|
const { data: searchResults, isLoading } = useProductSearch(searchQuery)
|
||||||
const isSearching = inputValue.length > 0 && (isLoading || inputValue !== searchQuery)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true)
|
setMounted(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleInputChange = (value: string) => {
|
const handleSearch = (value: string) => {
|
||||||
setInputValue(value)
|
setSearchQuery(value)
|
||||||
setIsOpen(value.length > 0)
|
setIsOpen(value.length > 0)
|
||||||
|
|
||||||
if (value.length > 0 && inputRef.current) {
|
if (value.length > 0 && inputRef.current) {
|
||||||
@@ -39,7 +37,6 @@ const DesktopSearch: FC = () => {
|
|||||||
|
|
||||||
const handleProductClick = (url: string) => {
|
const handleProductClick = (url: string) => {
|
||||||
router.push(url)
|
router.push(url)
|
||||||
setInputValue('')
|
|
||||||
setSearchQuery('')
|
setSearchQuery('')
|
||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
}
|
}
|
||||||
@@ -82,13 +79,12 @@ const DesktopSearch: FC = () => {
|
|||||||
className='w-full'
|
className='w-full'
|
||||||
variant='search'
|
variant='search'
|
||||||
placeholder='جستجو'
|
placeholder='جستجو'
|
||||||
value={inputValue}
|
value={searchQuery}
|
||||||
onChange={(e) => handleInputChange(e.target.value)}
|
onChange={(e) => handleSearch(e.target.value)}
|
||||||
onChangeSearchFinal={setSearchQuery}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{mounted && isOpen && inputValue.length > 0 && createPortal(
|
{mounted && isOpen && searchQuery.length > 0 && createPortal(
|
||||||
<div
|
<div
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
className='fixed bg-white rounded-xl shadow-lg border border-gray-200 max-h-[400px] overflow-y-auto z-[99999]'
|
className='fixed bg-white rounded-xl shadow-lg border border-gray-200 max-h-[400px] overflow-y-auto z-[99999]'
|
||||||
@@ -98,7 +94,7 @@ const DesktopSearch: FC = () => {
|
|||||||
width: `${dropdownPosition.width}px`
|
width: `${dropdownPosition.width}px`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSearching ? (
|
{isLoading ? (
|
||||||
<div className='p-4 text-center text-gray-500'>
|
<div className='p-4 text-center text-gray-500'>
|
||||||
در حال جستجو...
|
در حال جستجو...
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,16 +8,17 @@ import Image from 'next/image'
|
|||||||
|
|
||||||
const MobileSearch: FC = () => {
|
const MobileSearch: FC = () => {
|
||||||
const [isSearchOpen, setIsSearchOpen] = useState(false)
|
const [isSearchOpen, setIsSearchOpen] = useState(false)
|
||||||
const [inputValue, setInputValue] = useState('')
|
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const { data: searchResults, isLoading } = useProductSearch(searchQuery)
|
const { data: searchResults, isLoading } = useProductSearch(searchQuery)
|
||||||
const isSearching = inputValue.length > 0 && (isLoading || inputValue !== searchQuery)
|
|
||||||
|
const handleSearch = (value: string) => {
|
||||||
|
setSearchQuery(value)
|
||||||
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setIsSearchOpen(false)
|
setIsSearchOpen(false)
|
||||||
setInputValue('')
|
|
||||||
setSearchQuery('')
|
setSearchQuery('')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +36,8 @@ const MobileSearch: FC = () => {
|
|||||||
variant='search'
|
variant='search'
|
||||||
placeholder='جستجو'
|
placeholder='جستجو'
|
||||||
autoFocus
|
autoFocus
|
||||||
value={inputValue}
|
value={searchQuery}
|
||||||
onChange={(e) => setInputValue(e.target.value)}
|
onChange={(e) => handleSearch(e.target.value)}
|
||||||
onChangeSearchFinal={setSearchQuery}
|
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
@@ -48,9 +48,9 @@ const MobileSearch: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex-1 overflow-y-auto'>
|
<div className='flex-1 overflow-y-auto'>
|
||||||
{inputValue.length > 0 && (
|
{searchQuery.length > 0 && (
|
||||||
<>
|
<>
|
||||||
{isSearching ? (
|
{isLoading ? (
|
||||||
<div className='p-4 text-center text-gray-500'>
|
<div className='p-4 text-center text-gray-500'>
|
||||||
در حال جستجو...
|
در حال جستجو...
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user