From 7a3aa951260cc9a6220da2c4e8029c1fe5e69a10 Mon Sep 17 00:00:00 2001 From: AI Bot Date: Fri, 9 Oct 2026 08:45:28 -0500 Subject: [PATCH] feat: sistema de cupones de descuento configurables por caducidad y limite de usos en admin, checkout y app android --- dripzone-app/src/app/admin/cupones/page.tsx | 74 ++++++++++++++++--- .../src/app/api/coupons/validate/route.ts | 19 ++++- dripzone-app/src/app/checkout/page.tsx | 56 +++++++++++--- dripzone-app/src/lib/data-service.ts | 16 ++++ dripzone-app/src/lib/evolution-api.ts | 8 +- dripzone-app/src/lib/server-appwrite.ts | 2 +- dripzone-app/src/types/index.ts | 2 +- scripts/deploy_coupons_and_send_apk.py | 10 ++- 8 files changed, 156 insertions(+), 31 deletions(-) diff --git a/dripzone-app/src/app/admin/cupones/page.tsx b/dripzone-app/src/app/admin/cupones/page.tsx index 9fbc5ff..b4e2afa 100644 --- a/dripzone-app/src/app/admin/cupones/page.tsx +++ b/dripzone-app/src/app/admin/cupones/page.tsx @@ -46,7 +46,7 @@ export default function AdminCuponesPage() { // Form Fields const [code, setCode] = useState(''); const [description, setDescription] = useState(''); - const [discountType, setDiscountType] = useState<'percentage' | 'fixed'>('percentage'); + const [discountType, setDiscountType] = useState<'percentage' | 'fixed' | 'per_item'>('percentage'); const [discountValue, setDiscountValue] = useState(10); const [minSpend, setMinSpend] = useState(0); const [maxDiscount, setMaxDiscount] = useState(0); @@ -526,20 +526,24 @@ export default function AdminCuponesPage() { {/* Discount Value Badge */}
-
- {coupon.discountType === 'percentage' ? '%' : '$'} +
+ {coupon.discountType === 'percentage' ? '%' : coupon.discountType === 'per_item' ? '📦' : '$'}

{coupon.discountType === 'percentage' ? `${coupon.discountValue}% de Descuento` + : coupon.discountType === 'per_item' + ? `${formatCOP(coupon.discountValue)} / cada producto` : `${formatCOP(coupon.discountValue)} de Descuento`}

- {coupon.minSpend && coupon.minSpend > 0 + {coupon.discountType === 'per_item' + ? `Descuenta ${formatCOP(coupon.discountValue)} por cada producto al saldo contraentrega` + : coupon.minSpend && coupon.minSpend > 0 ? `Compra mín: ${formatCOP(coupon.minSpend)}` : 'Sin monto mínimo'} - {coupon.discountType === 'percentage' && coupon.maxDiscount && coupon.maxDiscount > 0 + {coupon.maxDiscount && coupon.maxDiscount > 0 ? ` • Tope: ${formatCOP(coupon.maxDiscount)}` : ''}

@@ -733,20 +737,20 @@ export default function AdminCuponesPage() { {/* Tipo de Descuento */}
-
+
@@ -756,22 +760,57 @@ export default function AdminCuponesPage() { setDiscountType('fixed'); if (discountValue <= 100) setDiscountValue(20000); }} - className={`py-2.5 px-3 rounded-xl font-bold flex items-center justify-center gap-2 border transition-all cursor-pointer ${ + className={`py-2.5 px-3 rounded-xl font-bold flex items-center justify-center gap-1.5 border transition-all cursor-pointer text-xs ${ discountType === 'fixed' ? 'bg-purple-600 text-white border-purple-400 shadow-md' : 'bg-neutral-950 text-neutral-400 border-neutral-800 hover:text-white' }`} > - - Monto Fijo (COP $) + + Monto Fijo Global + + +
+ + {discountType === 'per_item' && ( +
+

+ ⚡ Descuento Progresivo por Unidades: +

+

+ Si el cliente compra 1 par descuenta {formatCOP(discountValue || 10000)}, si compra 2 pares descuenta {formatCOP((discountValue || 10000) * 2)}, si compra 3 pares {formatCOP((discountValue || 10000) * 3)} y así sucesivamente. +

+

+ 🛡️ El descuento se aplica 100% sobre el saldo pendiente por pagar (contraentrega), protegiendo intacto el anticipo de separación. +

+
+ )}
{/* Valor del Descuento y Atajos */}
)) + : discountType === 'per_item' + ? [5000, 10000, 15000, 20000, 25000, 30000].map((v) => ( + + )) : [10000, 15000, 20000, 30000, 50000].map((v) => (