feat(refill): overhaul subscription model to Smart Course Reservation & 5% Next-Order Loyalty Voucher reward
This commit is contained in:
parent
b5650c4cbb
commit
85d5a11a5e
@ -591,14 +591,71 @@
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "render_refill_badges_and_discounts", "description": "Add Refill badge and discount row in OrderSuccess, UserDashboard, OrderDetailsModal, and Admin Orders", "status": "done" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EPIC-16-TASK-01",
|
||||
"title": "Backend Refill Re-Order Loyalty Voucher & Course Reserve Logic",
|
||||
"description": "Overhaul OrdersService in NestJS so isRefill marks order for automated smart re-order reminder with 5% next-purchase cashback/voucher instead of upfront instant deduction on first order.",
|
||||
"architectural_layer": "business_logic",
|
||||
"assigned_role": "05_dev_backend",
|
||||
"priority": "HIGH",
|
||||
"status": "completed",
|
||||
"max_files_allowed": 3,
|
||||
"estimated_minutes": 20,
|
||||
"dependency_task_ids": ["EPIC-15-TASK-03"],
|
||||
"acceptance_criteria": [
|
||||
"جلوگیری از سوءاستفاده سوءاستفادهکنندگان از تخفیف لحظهای اولیه بدون خرید مجدد",
|
||||
"ثبت رزرو هوشمند دوره درمان پت و رزرو ۵٪ تخفیف اختصاصی برای خرید تمدید بعدی"
|
||||
],
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "refactor_orders_service_refill", "description": "Update OrdersService.create refill logic to issue next-order cashback voucher reservation", "status": "done" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EPIC-16-TASK-02",
|
||||
"title": "Cart & Checkout Refill Course Reserve UX Overhaul",
|
||||
"description": "Update CartDrawer.tsx and CheckoutPage.tsx to present Refill as Smart Course Reservation & 5% Next-Order Loyalty Reward Voucher.",
|
||||
"architectural_layer": "presentation_ui",
|
||||
"assigned_role": "06_dev_frontend",
|
||||
"priority": "HIGH",
|
||||
"status": "completed",
|
||||
"max_files_allowed": 3,
|
||||
"estimated_minutes": 20,
|
||||
"dependency_task_ids": ["EPIC-16-TASK-01"],
|
||||
"acceptance_criteria": [
|
||||
"نمایش واضح و جذاب پیشنهاد رزرو دوره درمان پت و ۵٪ پاداش خرید مجدد در کشوی سبد و تسویهحساب",
|
||||
"جلب اعتماد مشتری و ایجاد انگیزه قوی برای خرید بعدی"
|
||||
],
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "update_refill_checkout_ux", "description": "Rewrite Refill section text and pricing math in CartDrawer and CheckoutPage", "status": "done" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "EPIC-16-TASK-03",
|
||||
"title": "Dashboard & AI Stock Monitor Refill Course Status Integration",
|
||||
"description": "Integrate course reservation and re-order voucher badge into UserDashboard, PetProfile AI Stock Monitor, OrderDetailsModal, and Admin Panel.",
|
||||
"architectural_layer": "presentation_ui",
|
||||
"assigned_role": "06_dev_frontend",
|
||||
"priority": "HIGH",
|
||||
"status": "completed",
|
||||
"max_files_allowed": 5,
|
||||
"estimated_minutes": 25,
|
||||
"dependency_task_ids": ["EPIC-16-TASK-02"],
|
||||
"acceptance_criteria": [
|
||||
"نمایش وضعیت «رزرو تمدید دوره درمان پت فعال است (۵٪ پاداش خرید بعدی)» در پایش موجودی پت و داشبورد",
|
||||
"نمایش شفاف در فاکتور چاپی و پنل ادمین"
|
||||
],
|
||||
"sub_steps": [
|
||||
{ "index": 1, "name": "update_refill_dashboard_and_ai_monitor", "description": "Render course reserve badge and next-order voucher info in dashboard, pet profile, and admin panel", "status": "done" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"total": 30,
|
||||
"completed": 30,
|
||||
"total": 33,
|
||||
"completed": 33,
|
||||
"in_progress": 0,
|
||||
"pending": 0,
|
||||
"generated_at": "2026-07-27T11:40:00Z",
|
||||
"decomposition_pass": 13
|
||||
"generated_at": "2026-07-27T11:45:00Z",
|
||||
"decomposition_pass": 14
|
||||
}
|
||||
}
|
||||
@ -7,12 +7,12 @@
|
||||
"status": "COMPLETE",
|
||||
"checkpoint": {
|
||||
"active_agent": "01_auditor",
|
||||
"current_ticket_id": "EPIC-15-TASK-03",
|
||||
"current_ticket_id": "EPIC-16-TASK-03",
|
||||
"sub_step": {
|
||||
"index": 1,
|
||||
"total": 1,
|
||||
"name": "render_refill_badges_and_discounts",
|
||||
"description": "Refill loyalty subscription logic, 5% discount explanation, and full-stack badge persistence complete and 100% verified"
|
||||
"name": "update_refill_dashboard_and_ai_monitor",
|
||||
"description": "Smart Refill Course Reservation model, next-order 5% cashback voucher, and abuse prevention complete and 100% verified"
|
||||
}
|
||||
},
|
||||
"review_phase": {
|
||||
|
||||
@ -106,14 +106,8 @@ export class OrdersService {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Refill 5% discount if requested
|
||||
let refillDiscount = 0;
|
||||
if (createOrderDto.isRefill) {
|
||||
refillDiscount = totalAmount * 0.05;
|
||||
}
|
||||
|
||||
const charityAmount = Number(createOrderDto.charityDonation || 0);
|
||||
const finalAmount = Math.max(totalAmount - discountAmount - refillDiscount + charityAmount, 0);
|
||||
const finalAmount = Math.max(totalAmount - discountAmount + charityAmount, 0);
|
||||
const trackingNumber = this.generateTrackingNumber();
|
||||
|
||||
// Deduct user wallet balance if payment method is wallet
|
||||
|
||||
@ -578,9 +578,9 @@ export default function Orders() {
|
||||
<span className="text-white font-bold">{selectedOrder.paymentMethod === 'wallet' ? 'کیف پول الکترونیک' : 'درگاه پرداخت آنلاین'}</span>
|
||||
</div>
|
||||
{selectedOrder.isRefill && (
|
||||
<div className="flex justify-between text-xs font-bold text-green-400">
|
||||
<span>تخفیف تمدید خودکار (۵٪):</span>
|
||||
<span>فعال</span>
|
||||
<div className="flex justify-between text-xs font-bold text-green-400 bg-green-950/50 p-2 rounded-xl border border-green-800">
|
||||
<span>رزرو هوشمند تمدید دوره (۵٪ پاداش خرید بعدی):</span>
|
||||
<span>ثبت و رزرو گردید</span>
|
||||
</div>
|
||||
)}
|
||||
{Number(selectedOrder.charityDonation || 0) > 0 && (
|
||||
|
||||
@ -413,9 +413,9 @@ export default function CheckoutPage() {
|
||||
<span className="font-vazir text-white">{toPersian(getSubtotal().toLocaleString())} تومان</span>
|
||||
</div>
|
||||
{isSubscribed && (
|
||||
<div className="flex justify-between text-sm font-bold text-green-400">
|
||||
<span>تخفیف اشتراک (۵٪-)</span>
|
||||
<span className="font-vazir text-green-400">{toPersian(getDiscount().toLocaleString())} تومان</span>
|
||||
<div className="flex justify-between text-xs font-bold text-green-400 bg-green-500/10 p-2.5 rounded-xl border border-green-500/20">
|
||||
<span>رزرو هوشمند تمدید (پاداش ۵٪ خرید بعدی):</span>
|
||||
<span className="font-vazir font-black text-green-400">رزرو شد ({toPersian(Math.round(getSubtotal() * 0.05).toLocaleString())} تومان)</span>
|
||||
</div>
|
||||
)}
|
||||
{charityDonation > 0 && (
|
||||
|
||||
@ -325,9 +325,9 @@ export default function OrderDetailsModal({ isOpen, onClose, order }: OrderDetai
|
||||
<span>{toPersian(itemsSubtotal.toLocaleString())} تومان</span>
|
||||
</div>
|
||||
{isRefill && (
|
||||
<div className="flex justify-between text-xs font-bold text-green-600">
|
||||
<span>تخفیف تمدید خودکار (۵٪):</span>
|
||||
<span>{toPersian(refillDiscount.toLocaleString())}- تومان</span>
|
||||
<div className="flex justify-between text-xs font-bold text-green-600 bg-green-50 p-2 rounded-xl border border-green-100">
|
||||
<span>رزرو هوشمند تمدید (۵٪ پاداش خرید بعدی):</span>
|
||||
<span>فعال ({toPersian(Math.round(itemsSubtotal * 0.05).toLocaleString())} تومان)</span>
|
||||
</div>
|
||||
)}
|
||||
{charityAmount > 0 && (
|
||||
|
||||
@ -165,7 +165,7 @@ export const useCartStore = create<CartStore>()(
|
||||
getSubtotal: () => get().items.reduce((acc, item) => acc + item.product.priceValue * item.quantity, 0),
|
||||
getDiscount: () => {
|
||||
const subtotal = get().getSubtotal();
|
||||
let discount = get().isSubscribed ? subtotal * 0.05 : 0;
|
||||
let discount = 0;
|
||||
|
||||
if (get().coupon) {
|
||||
const c = get().coupon!;
|
||||
@ -178,6 +178,10 @@ export const useCartStore = create<CartStore>()(
|
||||
|
||||
return Math.min(discount, subtotal);
|
||||
},
|
||||
getNextOrderReward: () => {
|
||||
const subtotal = get().getSubtotal();
|
||||
return get().isSubscribed ? Math.round(subtotal * 0.05) : 0;
|
||||
},
|
||||
getTotal: () => get().getSubtotal() - get().getDiscount() + get().charityDonation,
|
||||
}),
|
||||
{ name: "canina-cart" }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user