From 4979d4120cc052f16bc4454a99cbca4a39755e36 Mon Sep 17 00:00:00 2001 From: parsa aghaei Date: Sun, 26 Jul 2026 20:11:26 +0330 Subject: [PATCH] fix(checkout,orders,admin): resolve guest checkout address bug, persist refill and charity in DB, and fix admin media thumbnail cropping --- .ai_agency/memory/state.json | 10 ++++---- backend/prisma/schema.prisma | 2 ++ backend/src/orders/dto/create-order.dto.ts | 19 +++++++++++++++ backend/src/orders/orders.service.ts | 24 ++++++++++++++++++- .../src/components/ui/MediaSelector.tsx | 4 ++-- frontend/admin-panel/src/pages/Media.tsx | 4 ++-- frontend/admin-panel/src/pages/Products.tsx | 16 ++++++++----- .../application/components/CheckoutPage.tsx | 12 ++++++---- .../application/lib/services/orderService.ts | 6 +++++ 9 files changed, 76 insertions(+), 21 deletions(-) diff --git a/.ai_agency/memory/state.json b/.ai_agency/memory/state.json index 32b6231..d433497 100644 --- a/.ai_agency/memory/state.json +++ b/.ai_agency/memory/state.json @@ -7,12 +7,12 @@ "status": "COMPLETE", "checkpoint": { "active_agent": "01_auditor", - "current_ticket_id": "EPIC-05-TASK-01", + "current_ticket_id": "EPIC-06-TASK-05", "sub_step": { - "index": 3, - "total": 3, - "name": "verify_all_builds", - "description": "All 11 epic tasks fully completed and verified with production builds" + "index": 5, + "total": 5, + "name": "epic_06_verification", + "description": "All EPIC-06 tasks (Guest Checkout, Auto-Refill, Charity Round-Up, Dynamic AI Depletion & Admin Media Gallery) fully resolved and verified" } }, "review_phase": { diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index bdfa524..1670507 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -288,6 +288,8 @@ model Order { couponId String? @map("coupon_id") @db.Uuid totalAmount Decimal @map("total_amount") @db.Decimal(15, 2) charityDonation Decimal @default(0.00) @map("charity_donation") @db.Decimal(15, 2) + isRefill Boolean @default(false) @map("is_refill") + refillIntervalDays Int? @map("refill_interval_days") status String @default("processing") @db.VarChar(30) // processing, shipped, delivered trackingNumber String? @unique @map("tracking_number") @db.VarChar(100) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz() diff --git a/backend/src/orders/dto/create-order.dto.ts b/backend/src/orders/dto/create-order.dto.ts index e917ec7..becfa34 100644 --- a/backend/src/orders/dto/create-order.dto.ts +++ b/backend/src/orders/dto/create-order.dto.ts @@ -30,6 +30,25 @@ export class CreateOrderDto { @IsString() prescriptionUrl?: string; + @ApiPropertyOptional({ description: 'مبلغ کمک به پناهگاه حیوانات (ردپای مهربانی)' }) + @IsOptional() + @IsNumber() + charityDonation?: number; + + @ApiPropertyOptional({ description: 'فعالسازی تمدید خودکار سفارش' }) + @IsOptional() + isRefill?: boolean; + + @ApiPropertyOptional({ description: 'دوره زمانی تمدید خودکار (روز)' }) + @IsOptional() + @IsNumber() + refillIntervalDays?: number; + + @ApiPropertyOptional({ description: 'آدرس تحویل گیرنده' }) + @IsOptional() + @IsString() + shippingAddress?: string; + @ApiProperty({ description: 'لیست اقلام سفارش', type: [OrderItemDto] }) @IsArray() @ValidateNested({ each: true }) diff --git a/backend/src/orders/orders.service.ts b/backend/src/orders/orders.service.ts index e6feff4..ba936ee 100644 --- a/backend/src/orders/orders.service.ts +++ b/backend/src/orders/orders.service.ts @@ -106,14 +106,36 @@ export class OrdersService { } } - const finalAmount = Math.max(totalAmount - discountAmount, 0); + // 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 trackingNumber = this.generateTrackingNumber(); + // Increment user charity total if charity donation was added + if (charityAmount > 0) { + try { + await this.prisma.user.update({ + where: { id: userId }, + data: { charityDonationTotal: { increment: charityAmount } } + }); + } catch { + // Ignore if user not found or guest + } + } + return this.prisma.order.create({ data: { userId, couponId, totalAmount: finalAmount, + charityDonation: charityAmount, + isRefill: Boolean(createOrderDto.isRefill), + refillIntervalDays: createOrderDto.refillIntervalDays || 60, trackingNumber, status: 'processing', orderItems: { diff --git a/frontend/admin-panel/src/components/ui/MediaSelector.tsx b/frontend/admin-panel/src/components/ui/MediaSelector.tsx index 2cb7cb6..272501e 100644 --- a/frontend/admin-panel/src/components/ui/MediaSelector.tsx +++ b/frontend/admin-panel/src/components/ui/MediaSelector.tsx @@ -197,11 +197,11 @@ export default function MediaSelector({ isOpen, onClose, onSelect, multiple = fa }} className={`group relative bg-gray-100 rounded-xl overflow-hidden border-2 cursor-pointer transition-all hover:shadow-lg hover:shadow-purple-100 ${isSelected ? 'border-purple-600 ring-2 ring-purple-300' : 'border-transparent hover:border-purple-500'}`} > -
+
{media.filename}