import { useState, useEffect } from 'react' import { Save, Plus, Edit2, Trash2, X, Eye, Gift, Ticket } from 'lucide-react' import { api } from '../utils/api' export default function AdminCouponsPage() { const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [success, setSuccess] = useState('') const [coupons, setCoupons] = useState([]) const [editingId, setEditingId] = useState(null) const [showAddForm, setShowAddForm] = useState(false) const [viewingRedemptions, setViewingRedemptions] = useState(null) const [redemptions, setRedemptions] = useState([]) const [formData, setFormData] = useState({ code: '', type: 'single_use', valid_from: '', valid_until: '', grants_tier: 'premium', duration_days: 30, max_redemptions: 1, notes: '', active: true }) useEffect(() => { loadCoupons() }, []) async function loadCoupons() { try { setLoading(true) const data = await api.listCoupons() setCoupons(data) setError('') } catch (e) { setError(e.message) } finally { setLoading(false) } } async function loadRedemptions(couponId) { try { const data = await api.getCouponRedemptions(couponId) setRedemptions(data) setViewingRedemptions(couponId) } catch (e) { setError(e.message) } } function resetForm() { setFormData({ code: '', type: 'single_use', valid_from: '', valid_until: '', grants_tier: 'premium', duration_days: 30, max_redemptions: 1, notes: '', active: true }) setEditingId(null) setShowAddForm(false) } function startEdit(coupon) { setFormData({ code: coupon.code, type: coupon.type, valid_from: coupon.valid_from ? coupon.valid_from.split('T')[0] : '', valid_until: coupon.valid_until ? coupon.valid_until.split('T')[0] : '', grants_tier: coupon.grants_tier, duration_days: coupon.duration_days || 30, max_redemptions: coupon.max_redemptions || 1, notes: coupon.notes || '', active: coupon.active }) setEditingId(coupon.id) setShowAddForm(false) } function startAdd() { // Generate random code const randomCode = `${formData.type === 'gift' ? 'GIFT' : 'PROMO'}-${Math.random().toString(36).substr(2, 8).toUpperCase()}` setFormData({ ...formData, code: randomCode }) setShowAddForm(true) setEditingId(null) } async function handleSave() { try { setError('') setSuccess('') if (!formData.code.trim()) { setError('Code erforderlich') return } const payload = { code: formData.code.trim().toUpperCase(), type: formData.type, valid_from: formData.valid_from || null, valid_until: formData.valid_until || null, grants_tier: formData.grants_tier, duration_days: parseInt(formData.duration_days) || null, max_redemptions: formData.type === 'single_use' ? 1 : (parseInt(formData.max_redemptions) || null), notes: formData.notes.trim(), active: formData.active } if (editingId) { await api.updateCoupon(editingId, payload) setSuccess('Coupon aktualisiert') } else { await api.createCoupon(payload) setSuccess('Coupon erstellt') } await loadCoupons() resetForm() } catch (e) { setError(e.message) } } async function handleDelete(couponId) { if (!confirm('Coupon wirklich löschen?')) return try { setError('') await api.deleteCoupon(couponId) setSuccess('Coupon gelöscht') await loadCoupons() } catch (e) { setError(e.message) } } function formatDate(dateStr) { if (!dateStr) return '-' return new Date(dateStr).toLocaleDateString('de-DE') } if (loading) return (
) const couponTypes = [ { value: 'single_use', label: 'Single-Use (einmalig)', icon: '🎟️' }, { value: 'multi_use_period', label: 'Multi-Use Period (Zeitraum)', icon: '🔄' }, { value: 'gift', label: 'Geschenk-Coupon', icon: '🎁' } ] const tierOptions = [ { value: 'free', label: 'Free' }, { value: 'basic', label: 'Basic' }, { value: 'premium', label: 'Premium' } ] return (
{/* Header */}
Coupon-Verwaltung
Trial-Codes, Wellpass-Integration, Geschenk-Coupons
{!showAddForm && !editingId && ( )}
{/* Messages */} {error && (
{error}
)} {success && (
{success}
)} {/* Add/Edit Form */} {(showAddForm || editingId) && (
{editingId ? 'Coupon bearbeiten' : 'Neuer Coupon'}
{/* Code */}
setFormData({ ...formData, code: e.target.value })} placeholder="Z.B. PROMO-2026" />
Wird automatisch in Großbuchstaben konvertiert
{/* Type */}
{formData.type === 'single_use' && 'Kann nur einmal pro User eingelöst werden'} {formData.type === 'multi_use_period' && 'Unbegrenzte Einlösungen im Gültigkeitszeitraum (z.B. Wellpass)'} {formData.type === 'gift' && 'Geschenk-Coupon für Bonus-System'}
{/* Tier */}
{/* Duration (only for single_use and gift) */} {(formData.type === 'single_use' || formData.type === 'gift') && (
setFormData({ ...formData, duration_days: e.target.value })} />
Wie lange ist der gewährte Zugriff gültig?
)} {/* Valid From/Until (for multi_use_period) */} {formData.type === 'multi_use_period' && (
setFormData({ ...formData, valid_from: e.target.value })} />
setFormData({ ...formData, valid_until: e.target.value })} />
)} {/* Max Redemptions (not for single_use) */} {formData.type !== 'single_use' && (
setFormData({ ...formData, max_redemptions: e.target.value })} placeholder="Leer = unbegrenzt" />
)} {/* Notes */}
setFormData({ ...formData, notes: e.target.value })} placeholder="Z.B. 'Für Marketing-Kampagne März 2026'" />
{/* Active */}
{/* Actions */}
)} {/* Redemptions Modal */} {viewingRedemptions && (
setViewingRedemptions(null)}>
e.stopPropagation()}>
Einlösungen
{redemptions.length === 0 ? (
Noch keine Einlösungen
) : (
{redemptions.map(r => (
{r.profile_name || `User #${r.profile_id}`}
Eingelöst am {formatDate(r.redeemed_at)}
))}
)}
)} {/* Coupons List */}
{coupons.length === 0 && (
Keine Coupons vorhanden
)} {coupons.map(coupon => (
{coupon.code}
{couponTypes.find(t => t.value === coupon.type)?.icon} {couponTypes.find(t => t.value === coupon.type)?.label} {!coupon.active && ( INAKTIV )}
Gewährt: {coupon.grants_tier} {coupon.duration_days && ` für ${coupon.duration_days} Tage`}
{coupon.notes && (
📝 {coupon.notes}
)}
{coupon.valid_from && (
Gültig ab: {formatDate(coupon.valid_from)}
)} {coupon.valid_until && (
Gültig bis: {formatDate(coupon.valid_until)}
)}
Einlösungen: {coupon.current_redemptions || 0} {coupon.max_redemptions ? ` / ${coupon.max_redemptions}` : ' / ∞'}
))}
) }