import { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { FileText, Database, Bell, Loader2, Plus, Trash2, Save } from 'lucide-react';
import {
getForm16Config,
putForm16Config,
type Form16AdminConfig as Form16ConfigType,
type Form16NotificationItem,
type Form16Notification26AsItem,
} from '@/services/adminApi';
import { toast } from 'sonner';
function isValidEmail(s: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s.trim());
}
interface ViewerTableProps {
emails: string[];
onAdd: (email: string) => void;
onRemove: (email: string) => void;
placeholder?: string;
}
function ViewerTable({ emails, onAdd, onRemove, placeholder }: ViewerTableProps) {
const [input, setInput] = useState('');
const add = () => {
const e = input.trim().toLowerCase();
if (!e) return;
if (!isValidEmail(e)) {
toast.error('Please enter a valid email address');
return;
}
if (emails.includes(e)) {
toast.error('This email is already in the list');
return;
}
onAdd(e);
setInput('');
};
return (
{emails.length > 0 ? (
Email
Actions
{emails.map((email) => (
{email}
onRemove(email)}
className="text-destructive hover:text-destructive"
>
))}
) : (
No viewers added. Add emails above or leave empty to allow all RE users with access.
)}
);
}
function defaultNotif(enabled: boolean, template: string): Form16NotificationItem {
return { enabled, template };
}
const default26AsNotif = (): Form16Notification26AsItem => ({
enabled: true,
templateRe: '26AS data has been added. Please review and use for matching dealer Form 16 submissions.',
templateDealers: 'New 26AS data has been uploaded. You can now submit your Form 16 for the relevant quarter if you haven’t already.',
});
export function Form16AdminConfig() {
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [submissionViewerEmails, setSubmissionViewerEmails] = useState([]);
const [twentySixAsViewerEmails, setTwentySixAsViewerEmails] = useState([]);
const [reminderEnabled, setReminderEnabled] = useState(true);
const [reminderDays, setReminderDays] = useState(7);
const [notification26AsDataAdded, setNotification26AsDataAdded] = useState(default26AsNotif());
const [notificationForm16SuccessCreditNote, setNotificationForm16SuccessCreditNote] = useState(defaultNotif(true, 'Form 16 submitted successfully. Credit note: [CreditNoteRef].'));
const [notificationForm16Unsuccessful, setNotificationForm16Unsuccessful] = useState(defaultNotif(true, 'Form 16 submission was unsuccessful. Issue: [Issue]. Please review.'));
const [alertSubmitForm16Enabled, setAlertSubmitForm16Enabled] = useState(true);
const [alertSubmitForm16FrequencyDays, setAlertSubmitForm16FrequencyDays] = useState(0);
const [alertSubmitForm16FrequencyHours, setAlertSubmitForm16FrequencyHours] = useState(24);
const [alertSubmitForm16RunAtTime, setAlertSubmitForm16RunAtTime] = useState('09:00');
const [alertSubmitForm16Template, setAlertSubmitForm16Template] = useState('Please submit your Form 16 at your earliest. [Name], due date: [DueDate].');
const [reminderNotificationEnabled, setReminderNotificationEnabled] = useState(true);
const [reminderFrequencyDays, setReminderFrequencyDays] = useState(0);
const [reminderFrequencyHours, setReminderFrequencyHours] = useState(12);
const [reminderRunAtTime, setReminderRunAtTime] = useState('10:00');
const [reminderNotificationTemplate, setReminderNotificationTemplate] = useState('Reminder: Form 16 submission is pending. [Name], [Request ID]. Please review.');
useEffect(() => {
let mounted = true;
getForm16Config()
.then((config: Form16ConfigType) => {
if (!mounted) return;
setSubmissionViewerEmails(config.submissionViewerEmails ?? []);
setTwentySixAsViewerEmails(config.twentySixAsViewerEmails ?? []);
setReminderEnabled(config.reminderEnabled ?? true);
setReminderDays(typeof config.reminderDays === 'number' ? config.reminderDays : 7);
if (config.notification26AsDataAdded) {
const n = config.notification26AsDataAdded as Form16Notification26AsItem & { template?: string };
setNotification26AsDataAdded({
enabled: n.enabled ?? true,
templateRe: n.templateRe ?? n.template ?? default26AsNotif().templateRe,
templateDealers: n.templateDealers ?? default26AsNotif().templateDealers,
});
}
if (config.notificationForm16SuccessCreditNote) setNotificationForm16SuccessCreditNote(config.notificationForm16SuccessCreditNote);
if (config.notificationForm16Unsuccessful) setNotificationForm16Unsuccessful(config.notificationForm16Unsuccessful);
setAlertSubmitForm16Enabled(config.alertSubmitForm16Enabled ?? true);
setAlertSubmitForm16FrequencyDays(config.alertSubmitForm16FrequencyDays ?? 0);
setAlertSubmitForm16FrequencyHours(config.alertSubmitForm16FrequencyHours ?? 24);
setAlertSubmitForm16RunAtTime(config.alertSubmitForm16RunAtTime !== undefined && config.alertSubmitForm16RunAtTime !== null ? config.alertSubmitForm16RunAtTime : '09:00');
setAlertSubmitForm16Template(config.alertSubmitForm16Template ?? 'Please submit your Form 16 at your earliest. [Name], due date: [DueDate].');
setReminderNotificationEnabled(config.reminderNotificationEnabled ?? true);
setReminderFrequencyDays(config.reminderFrequencyDays ?? 0);
setReminderFrequencyHours(config.reminderFrequencyHours ?? 12);
setReminderRunAtTime(config.reminderRunAtTime !== undefined && config.reminderRunAtTime !== null ? config.reminderRunAtTime : '10:00');
setReminderNotificationTemplate(config.reminderNotificationTemplate ?? 'Reminder: Form 16 submission is pending. [Name], [Request ID]. Please review.');
})
.catch(() => {
if (mounted) toast.error('Failed to load Form 16 configuration');
})
.finally(() => {
if (mounted) setLoading(false);
});
return () => {
mounted = false;
};
}, []);
const handleSave = async () => {
setSaving(true);
try {
await putForm16Config({
submissionViewerEmails,
twentySixAsViewerEmails,
reminderEnabled,
reminderDays: Math.max(1, Math.min(365, reminderDays)) || 7,
notification26AsDataAdded,
notificationForm16SuccessCreditNote,
notificationForm16Unsuccessful,
alertSubmitForm16Enabled,
alertSubmitForm16FrequencyDays: Math.max(0, Math.min(365, alertSubmitForm16FrequencyDays)),
alertSubmitForm16FrequencyHours: Math.max(0, Math.min(168, alertSubmitForm16FrequencyHours)),
alertSubmitForm16RunAtTime: alertSubmitForm16RunAtTime ?? '',
alertSubmitForm16Template,
reminderNotificationEnabled,
reminderFrequencyDays: Math.max(0, Math.min(365, reminderFrequencyDays)),
reminderFrequencyHours: Math.max(0, Math.min(168, reminderFrequencyHours)),
reminderRunAtTime: reminderRunAtTime ?? '',
reminderNotificationTemplate,
});
toast.success('Form 16 configuration saved');
} catch {
toast.error('Failed to save Form 16 configuration');
} finally {
setSaving(false);
}
};
if (loading) {
return (
);
}
return (
{/* Page header */}
Form 16 Administration
Configure Form 16 access, who can view submission data and 26AS, and notification settings.
{/* Summary cards */}
Submission data viewers (RE)
{submissionViewerEmails.length}
Who can see Form 16 submissions
26AS viewers (RE)
{twentySixAsViewerEmails.length}
Who can see 26AS page
Reminders to dealers
{reminderEnabled ? 'On' : 'Off'}
Pending Form 16 reminder schedule
Email / in-app notifications
{[
notification26AsDataAdded?.enabled,
notificationForm16SuccessCreditNote?.enabled,
notificationForm16Unsuccessful?.enabled,
alertSubmitForm16Enabled,
reminderNotificationEnabled,
].filter(Boolean).length}{' '}
/ 5 enabled
To dealers and RE as per rules below
{/* Submission data viewers */}
Submission data – who can see
Users with these email addresses can see Form 16 submission data (and the Form 16 menu in the sidebar). Use the exact login email of each user (the same email they use to sign in). Leave the list empty to allow all RE users with Form 16 access.
setSubmissionViewerEmails((prev) => [...prev, email].sort())}
onRemove={(email) => setSubmissionViewerEmails((prev) => prev.filter((e) => e !== email))}
placeholder="e.g., user@royalenfield.com"
/>
{/* 26AS viewers */}
26AS page and button – who can see
Users with these email addresses can see the 26AS page and 26AS menu item. Use the exact login email of each user. Leave empty to allow all RE users.
setTwentySixAsViewerEmails((prev) => [...prev, email].sort())}
onRemove={(email) => setTwentySixAsViewerEmails((prev) => prev.filter((e) => e !== email))}
placeholder="e.g., user@royalenfield.com"
/>
{/* Notifications and reminders (simple toggles) */}
Reminder schedule (for dealers)
When reminders are enabled, dealers with pending Form 16 for a quarter are reminded at this interval. Set how often (in days) the system may send them a reminder to submit Form 16.
Enable reminders to dealers
Remind dealers every (days)
setReminderDays(parseInt(e.target.value, 10) || 7)}
/>
{/* Notification Configuration – Form 16 events */}
Email and in-app notifications
Configure who receives each notification, what triggers it, and when it is sent. Templates support placeholders such as [Name], [Request ID], [DueDate].
Form 16 notifications – recipient and trigger
{/* 26AS data added – separate message for RE users and for dealers */}
26AS data added
Sent to: RE users who can view 26AS, and separately to all dealers. When: As soon as new 26AS data is uploaded.
Message to RE users
Message to dealers
Placeholders: [Name], [Request ID]
setNotification26AsDataAdded((prev) => ({ ...prev, enabled }))}
/>
{/* Successful Form 16 with credit note */}
Form 16 success – credit note issued
Sent to: The dealer who submitted the Form 16. When: Immediately after their submission is matched with 26AS and a credit note is generated.
setNotificationForm16SuccessCreditNote((prev) => ({ ...prev, enabled }))}
/>
{/* Unsuccessful Form 16 */}
Form 16 unsuccessful (mismatch or issue)
Sent to: The dealer who submitted. When: When their submission fails (e.g. value mismatch with 26AS, duplicate, or validation error) so they can correct and resubmit.
setNotificationForm16Unsuccessful((prev) => ({ ...prev, enabled }))}
/>
{/* Alert to submit Form 16 (auto, configurable) */}
Alert – submit Form 16 (to dealers who haven’t submitted)
Sent to: Dealers who have not yet submitted Form 16 for the current FY. When: Daily at the time below (server timezone). All settings are API-driven from this config.
{/* Reminder notification (pending Form 16) */}
Reminder – pending Form 16 (to dealers)
Sent to: Dealers who have at least one open Form 16 submission without a credit note. When: Daily at the time below (server timezone). All settings are API-driven from this config.
{saving ? : }
Save Form 16 configuration
);
}