'use strict'; 'use client'; import React, { useState, useEffect } from 'react'; import Link from 'next/link'; import { useRouter, useParams } from 'next/navigation'; import styles from './page.module.css'; import Header from '@/components/Header'; import { useLocale } from '@/lib/i18n/LocaleContext'; interface Rule { id?: number; tempId?: string; type: 'TIER' | 'BONUS'; minAchievement: number; maxAchievement: number; rate: number; payoutAmount: number; } interface Plan { id: number; name: string; code: string; type: string; status: string; version: number; rules: Rule[]; } export default function RulesPage() { const { t } = useLocale(); const router = useRouter(); const params = useParams(); const planId = params?.id ? parseInt(params.id as string) : null; const [plan, setPlan] = useState(null); const [rules, setRules] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); const [role, setRole] = useState(null); const fetchPlan = async () => { if (!planId) return; try { const res = await fetch(`/api/plans/${planId}`); if (res.ok) { const data = await res.json(); setPlan(data.plan); if (data.plan.rules && data.plan.rules.length > 0) { // Format rules from server (Decimal fields come as strings/numbers) const formatted = data.plan.rules.map((r: any) => ({ id: r.id, tempId: Math.random().toString(36).substr(2, 9), type: r.type as 'TIER' | 'BONUS', minAchievement: parseFloat(r.minAchievement), maxAchievement: parseFloat(r.maxAchievement), rate: parseFloat(r.rate), payoutAmount: parseFloat(r.payoutAmount) })); setRules(formatted); } else { // Default initial empty row setRules([ { tempId: Math.random().toString(36).substr(2, 9), type: 'TIER', minAchievement: 0.0, maxAchievement: 1.0, rate: 0.0, payoutAmount: 0.0 } ]); } } else { setError(t('plans.errorFind')); } } catch (err) { setError(t('plans.errorLoad')); } finally { setIsLoading(false); } }; useEffect(() => { fetch('/api/auth/me') .then(res => res.ok ? res.json() : null) .then(data => { if (data?.user) setRole(data.user.role); }) .catch(err => console.error('Failed to fetch user role', err)); fetchPlan(); }, [planId]); const handleAddRow = () => { // Generate a default values row, potentially continuing from previous max const lastRule = rules[rules.length - 1]; const newMin = lastRule ? lastRule.maxAchievement : 0.0; setRules([ ...rules, { tempId: Math.random().toString(36).substr(2, 9), type: 'TIER', minAchievement: newMin, maxAchievement: newMin + 0.1 > 1.0 ? 1.0 : newMin + 0.1, rate: 0.0, payoutAmount: 0.0 } ]); }; const handleDeleteRow = (tempId: string) => { if (rules.length === 1) { setError(t('rules.errorAtLeastOne')); return; } setRules(rules.filter(r => r.tempId !== tempId)); }; const handleUpdateRow = (tempId: string, field: keyof Rule, value: any) => { setError(null); setSuccess(null); setRules( rules.map(r => { if (r.tempId === tempId) { let parsedValue = value; if (field === 'minAchievement' || field === 'maxAchievement' || field === 'rate') { parsedValue = parseFloat(value) || 0.0; } else if (field === 'payoutAmount') { parsedValue = parseFloat(value) || 0.0; } return { ...r, [field]: parsedValue }; } return r; }) ); }; const handleSaveRules = async (e: React.FormEvent) => { e.preventDefault(); setError(null); setSuccess(null); // Validate boundaries for (let i = 0; i < rules.length; i++) { const r = rules[i]; if (r.minAchievement >= r.maxAchievement) { setError(t('rules.errorBoundary').replace('{row}', String(i + 1)).replace('{min}', String(r.minAchievement)).replace('{max}', String(r.maxAchievement))); return; } if (r.minAchievement < 0 || r.maxAchievement < 0 || r.rate < 0 || r.payoutAmount < 0) { setError(t('rules.errorNegative').replace('{row}', String(i + 1))); return; } } try { const res = await fetch(`/api/plans/${planId}/rules`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rules: rules.map(r => ({ type: r.type, minAchievement: r.minAchievement, maxAchievement: r.maxAchievement, rate: r.rate, payoutAmount: r.payoutAmount })) }) }); const data = await res.json(); if (!res.ok) { setError(data.error || t('rules.errorSave')); return; } setSuccess(t('rules.successSave')); // Refresh current plan fetchPlan(); } catch (err) { setError(t('rules.errorNetwork')); } }; if (isLoading) { return (
{t('rules.loading')}
); } return (
{/* Shared Dashboard Header */}
← {t('rules.back')}

{t('rules.title')}

{t('rules.subtitle').replace('{name}', plan?.name || '').replace('{code}', plan?.code || '').replace('{version}', String(plan?.version || '')).replace('{status}', t('status.' + plan?.status) || '')}

{error &&
{error}
} {success &&
{success}
}
{t('rules.thType')} {t('rules.thMin')} {t('rules.thMax')} {t('rules.thRate')} {t('rules.thPayout')}
{rules.map((rule, idx) => (
handleUpdateRow(rule.tempId!, 'minAchievement', e.target.value)} id={`rule-min-${idx}`} required disabled={!(role === 'admin' || role === 'director')} /> handleUpdateRow(rule.tempId!, 'maxAchievement', e.target.value)} id={`rule-max-${idx}`} required disabled={!(role === 'admin' || role === 'director')} /> handleUpdateRow(rule.tempId!, 'rate', e.target.value)} id={`rule-rate-${idx}`} required disabled={!(role === 'admin' || role === 'director')} /> handleUpdateRow(rule.tempId!, 'payoutAmount', e.target.value)} id={`rule-payout-${idx}`} required disabled={!(role === 'admin' || role === 'director')} /> {(role === 'admin' || role === 'director') && ( )}
))}
{(role === 'admin' || role === 'director') && ( )}
{ (role === 'admin' || role === 'director') ? t('rules.cancel') : t('rules.backBtn') } {(role === 'admin' || role === 'director') && ( )}
); }