This commit is contained in:
Hamed
2026-08-03 12:25:44 +04:00
parent ee456ff4bb
commit 61e29cc149
4 changed files with 114 additions and 98 deletions
+13 -5
View File
@@ -2,10 +2,10 @@
<project version="4"> <project version="4">
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update"> <list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update">
<change afterPath="$PROJECT_DIR$/lib/axios.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.env.local.example" beforeDir="false" afterPath="$PROJECT_DIR$/.env.local.example" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/page.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/app/page.jsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/components/CartDrawer.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/CartDrawer.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/components/ChatWidget.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/ChatWidget.jsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/components/TicketForm.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/TicketForm.jsx" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -88,7 +88,7 @@
<workItem from="1785583517424" duration="145000" /> <workItem from="1785583517424" duration="145000" />
<workItem from="1785645096071" duration="26000" /> <workItem from="1785645096071" duration="26000" />
<workItem from="1785646314677" duration="16621000" /> <workItem from="1785646314677" duration="16621000" />
<workItem from="1785730130350" duration="8979000" /> <workItem from="1785730130350" duration="9270000" />
</task> </task>
<task id="LOCAL-00001" summary="update"> <task id="LOCAL-00001" summary="update">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -178,7 +178,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1785744250045</updated> <updated>1785744250045</updated>
</task> </task>
<option name="localTasksCounter" value="12" /> <task id="LOCAL-00012" summary="update">
<option name="closed" value="true" />
<created>1785745317460</created>
<option name="number" value="00012" />
<option name="presentableId" value="LOCAL-00012" />
<option name="project" value="LOCAL" />
<updated>1785745317460</updated>
</task>
<option name="localTasksCounter" value="13" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
+96 -93
View File
@@ -2,110 +2,113 @@
// کشوی (Drawer) سبد خرید: نمایش آیتم‌های انتخابی، تغییر تعداد، جمع کل، و دکمه پرداخت (placeholder). // کشوی (Drawer) سبد خرید: نمایش آیتم‌های انتخابی، تغییر تعداد، جمع کل، و دکمه پرداخت (placeholder).
import { Drawer, Button, Empty, InputNumber, message } from 'antd'; import {Drawer, Button, Empty, InputNumber, message} from 'antd';
import { DeleteOutlined } from '@ant-design/icons'; import {DeleteOutlined} from '@ant-design/icons';
import { useSelector, useDispatch } from 'react-redux'; import {useSelector, useDispatch} from 'react-redux';
import { import {
selectCartItems, selectCartItems,
selectCartTotal, selectCartTotal,
selectIsDrawerOpen, selectIsDrawerOpen,
closeDrawer, closeDrawer,
removeFromCart, removeFromCart,
incrementQty, incrementQty,
decrementQty, decrementQty,
} from '@/store/cartSlice'; } from '@/store/cartSlice';
import { useLanguage } from '@/app/context/LanguageContext'; import {useLanguage} from '@/app/context/LanguageContext';
import api from "@/lib/axios";
function toLocaleNumber(n, locale) { function toLocaleNumber(n, locale) {
return n.toLocaleString(locale === 'fa' ? 'fa-IR' : 'en-US'); return n.toLocaleString(locale === 'fa' ? 'fa-IR' : 'en-US');
} }
export default function CartDrawer() { export default function CartDrawer() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const isOpen = useSelector(selectIsDrawerOpen); const isOpen = useSelector(selectIsDrawerOpen);
const items = useSelector(selectCartItems); const items = useSelector(selectCartItems);
const total = useSelector(selectCartTotal); const total = useSelector(selectCartTotal);
const { t, locale, dir } = useLanguage(); const {t, locale, dir} = useLanguage();
const currencySuffix = locale === 'fa' ? ' ریال' : ' Rials'; const currencySuffix = locale === 'fa' ? ' ریال' : ' Rials';
const handleCheckout = async () => { const handleCheckout = async () => {
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// TODO: Connect to payment gateway API. // TODO: Connect to payment gateway API.
//
// import axios from 'axios';
//
// try {
// const { data } = await axios.post('/api/payment/create', {
// items: items.map((i) => ({ id: i.id, qty: i.qty })),
// totalAmount: total,
// });
// window.location.href = data.paymentRedirectUrl;
// } catch (err) {
// message.error('...');
// }
// -------------------------------------------------------------------
message.info(t.cart.checkoutToast);
};
return ( // try {
<Drawer // const { data } = await axios.post('/api/payment/create', {
title={t.cart.title} // items: items.map((i) => ({ id: i.id, qty: i.qty })),
placement={dir === 'rtl' ? 'right' : 'left'} // totalAmount: total,
onClose={() => dispatch(closeDrawer())} // });
open={isOpen} // window.location.href = data.paymentRedirectUrl;
width={400} // } catch (err) {
> // message.error('...');
{items.length === 0 ? ( // }
<Empty description={<span className="text-secondary">{t.cart.empty}</span>} className="mt-16" />
) : (
<div className="flex flex-col gap-4">
{items.map((item) => (
<div key={item.id} className="flex items-center gap-3 bg-surface border border-subtle rounded-xl p-3">
<img src={item.image} alt={item.name} className="w-14 h-20 object-contain shrink-0" />
<div className="flex-1 min-w-0">
<p className="text-sm font-semibold text-primary truncate">{item.name}</p>
<p className="text-xs text-brand-400 mt-1">
{toLocaleNumber(item.priceNumeric, locale)}
{currencySuffix}
</p>
<div className="flex items-center gap-2 mt-2">
<InputNumber
size="small"
min={1}
value={item.qty}
onChange={(val) => {
if (val > item.qty) dispatch(incrementQty(item.id));
else if (val < item.qty) dispatch(decrementQty(item.id));
}}
className="w-16"
/>
<Button
type="text"
danger
size="small"
icon={<DeleteOutlined />}
onClick={() => dispatch(removeFromCart(item.id))}
/>
</div>
</div>
</div>
))}
<div className="border-t border-subtle pt-4 mt-2"> // api() <===> todo : use this func to fetch ...
<div className="flex items-center justify-between text-sm text-secondary mb-4">
<span>{t.cart.total}</span> // -------------------------------------------------------------------
<span className="text-lg font-bold text-primary"> message.info(t.cart.checkoutToast);
};
return (
<Drawer
title={t.cart.title}
placement={dir === 'rtl' ? 'right' : 'left'}
onClose={() => dispatch(closeDrawer())}
open={isOpen}
width={400}
>
{items.length === 0 ? (
<Empty description={<span className="text-secondary">{t.cart.empty}</span>} className="mt-16"/>
) : (
<div className="flex flex-col gap-4">
{items.map((item) => (
<div key={item.id}
className="flex items-center gap-3 bg-surface border border-subtle rounded-xl p-3">
<img src={item.image} alt={item.name} className="w-14 h-20 object-contain shrink-0"/>
<div className="flex-1 min-w-0">
<p className="text-sm font-semibold text-primary truncate">{item.name}</p>
<p className="text-xs text-brand-400 mt-1">
{toLocaleNumber(item.priceNumeric, locale)}
{currencySuffix}
</p>
<div className="flex items-center gap-2 mt-2">
<InputNumber
size="small"
min={1}
value={item.qty}
onChange={(val) => {
if (val > item.qty) dispatch(incrementQty(item.id));
else if (val < item.qty) dispatch(decrementQty(item.id));
}}
className="w-16"
/>
<Button
type="text"
danger
size="small"
icon={<DeleteOutlined/>}
onClick={() => dispatch(removeFromCart(item.id))}
/>
</div>
</div>
</div>
))}
<div className="border-t border-subtle pt-4 mt-2">
<div className="flex items-center justify-between text-sm text-secondary mb-4">
<span>{t.cart.total}</span>
<span className="text-lg font-bold text-primary">
{toLocaleNumber(total, locale)} {toLocaleNumber(total, locale)}
{currencySuffix} {currencySuffix}
</span> </span>
</div> </div>
<Button type="primary" block size="large" onClick={handleCheckout}> <Button type="primary" block size="large" onClick={handleCheckout}>
{t.cart.checkout} {t.cart.checkout}
</Button> </Button>
</div> </div>
</div> </div>
)} )}
</Drawer> </Drawer>
); );
} }
+2
View File
@@ -38,6 +38,8 @@ export default function ChatWidget() {
// import axios from 'axios'; // import axios from 'axios';
// const { data } = await axios.post('/api/chat', { message: text }); // const { data } = await axios.post('/api/chat', { message: text });
// setMessages((prev) => [...prev, { from: 'bot', text: data.reply }]); // setMessages((prev) => [...prev, { from: 'bot', text: data.reply }]);
// api() <===> todo : use this func to fetch ...
// ----------------------------------------------------------------- // -----------------------------------------------------------------
setTimeout(() => { setTimeout(() => {
setMessages((prev) => [...prev, { from: 'bot', text: t.chat.fallbackReply }]); setMessages((prev) => [...prev, { from: 'bot', text: t.chat.fallbackReply }]);
+3
View File
@@ -23,6 +23,9 @@ export default function TicketForm() {
// message.error('...'); // message.error('...');
// return; // return;
// } // }
// api() <===> todo : use this func to fetch ...
// ------------------------------------------------------------------- // -------------------------------------------------------------------
console.log('Ticket submitted (placeholder):', values); console.log('Ticket submitted (placeholder):', values);
message.success(t.ticket.successMsg); message.success(t.ticket.successMsg);