veza/apps/web/src/components/commerce/modals/PromoCodeModal.tsx
senke fa3503ef87 aesthetic-improvements: reduce decorative cyan in marketplace, gamification, commerce, and education (80/20 rule, batch 10)
- Marketplace: LicenceDetailsModal decorative icon and price text, ProductDetailView decorative author text, ReviewProductModal decorative icon, LicenceCard decorative price text (4 instances)
- Gamification: AchievementCard decorative XP reward text, LeaderboardView loading spinner and decorative XP text, ProfileXPView loading spinner and decorative icons, AchievementsView loading spinner (5 instances)
- Commerce: WishlistView decorative price text, PromoCodeModal decorative icon, CartItem decorative license tag icon, OrderSummary decorative total price text (4 instances)
- Education: MyCoursesView decorative icon (1 instance)
- Total: ~14 files, ~14 instances replaced
- Preserved: Functional links (LicenceDetailsModal legal contract link), active/selected states (CourseLearningView active lesson, QuizModal selected answer - already preserved), primary actions, design system variants
- Action 11.3.1.3 in progress (tenth batch: marketplace, gamification, commerce, and education components)
2026-01-16 11:23:25 +01:00

84 lines
2.6 KiB
TypeScript

import React, { useState } from 'react';
import { Button } from '../../ui/button';
import { Input } from '../../ui/input';
import { X, Tag, Check, AlertCircle } from 'lucide-react';
interface PromoCodeModalProps {
onClose: () => void;
onApply: (discountPercent: number, code: string) => void;
}
export const PromoCodeModal: React.FC<PromoCodeModalProps> = ({
onClose,
onApply,
}) => {
const [code, setCode] = useState('');
const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
const handleApply = () => {
// Mock validation
if (code.toUpperCase() === 'VEZA20') {
setStatus('success');
setTimeout(() => {
onApply(20, 'VEZA20');
onClose();
}, 1000);
} else {
setStatus('error');
}
};
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
<div
className="absolute inset-0 bg-kodo-void/90 backdrop-blur-sm"
onClick={onClose}
></div>
<div className="relative w-full max-w-sm bg-kodo-graphite border border-kodo-steel rounded-xl shadow-2xl animate-scaleIn overflow-hidden">
<div className="p-4 border-b border-kodo-steel bg-kodo-ink flex justify-between items-center">
<h3 className="font-bold text-white flex items-center gap-2">
<Tag className="w-4 h-4 text-kodo-steel" /> Add Promo Code
</h3>
<button onClick={onClose}>
<X className="w-5 h-5 text-kodo-content-dim hover:text-white" />
</button>
</div>
<div className="p-6 space-y-4">
<Input
placeholder="Enter code (e.g. VEZA20)"
value={code}
onChange={(e) => {
setCode(e.target.value);
setStatus('idle');
}}
className={
status === 'error' ? 'border-kodo-red focus:border-kodo-red' : ''
}
/>
{status === 'error' && (
<div className="flex items-center gap-2 text-xs text-kodo-red animate-shake">
<AlertCircle className="w-3 h-3" /> Invalid promo code
</div>
)}
{status === 'success' && (
<div className="flex items-center gap-2 text-xs text-kodo-lime animate-fadeIn">
<Check className="w-3 h-3" /> Code applied! 20% Off
</div>
)}
<Button
variant="primary"
className="w-full"
onClick={handleApply}
disabled={!code}
>
Apply Discount
</Button>
</div>
</div>
</div>
);
};