'use client';
import { useEffect, useMemo, useState, type ReactNode } from 'react';
import {
Settings,
Save,
Loader2,
User,
Shield,
FileText,
Building,
RefreshCw,
Upload,
Image as ImageIcon,
KeyRound,
LayoutDashboard,
Search,
Copy,
Check,
Lock,
} from 'lucide-react';
import { parseJwt, getAccessToken } from '@/services/api/client';
import { userService, UserResponse } from '@/services/api/userService';
import { toast } from 'sonner';
import { format } from 'date-fns';
type SettingsBlade = 'overview' | 'profile' | 'security' | 'branding';
function PropertyRow({ label, children }: { label: string; children: ReactNode }) {
return (
{label}
{typeof children === 'string' ? (
{children}
) : (
children
)}
);
}
export default function SettingsPage() {
const [userRole, setUserRole] = useState('');
const [blade, setBlade] = useState('overview');
const [navSearch, setNavSearch] = useState('');
const [copiedId, setCopiedId] = useState(false);
const [profile, setProfile] = useState(null);
const [loadingProfile, setLoadingProfile] = useState(true);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [userName, setUserName] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const [savingProfile, setSavingProfile] = useState(false);
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [savingPassword, setSavingPassword] = useState(false);
const [invoiceLogoUrl, setInvoiceLogoUrl] = useState('https://ifixkart.com/logo.png');
const [invoiceCompanyName, setInvoiceCompanyName] = useState('iFixKart Solutions Platform');
const [invoiceGstin, setInvoiceGstin] = useState('33AAAAA0000A1Z5');
const [invoiceStoreAddress, setInvoiceStoreAddress] = useState(
'Offline Main Store Counter, Chennai | +91 9876543210'
);
const [invoiceGstRate, setInvoiceGstRate] = useState('18.0');
const [uploadingLogo, setUploadingLogo] = useState(false);
const isSuperAdmin =
userRole === 'super admin' || userRole === 'super_admin' || userRole === 'superadmin';
const fetchBrandingSettings = async () => {
if (typeof window === 'undefined') return;
const savedBranding = localStorage.getItem('ifixkart_invoice_branding');
if (savedBranding) {
try {
const parsed = JSON.parse(savedBranding);
if (parsed.logoUrl) setInvoiceLogoUrl(parsed.logoUrl);
if (parsed.companyName) setInvoiceCompanyName(parsed.companyName);
if (parsed.gstin) setInvoiceGstin(parsed.gstin);
if (parsed.storeAddress) setInvoiceStoreAddress(parsed.storeAddress);
if (parsed.gstRate !== undefined) setInvoiceGstRate(String(parsed.gstRate));
} catch {
/* ignore corrupt local branding */
}
}
try {
const token = getAccessToken();
const backendUrl = '';
const res = await fetch(`${backendUrl}/api/v1/settings/key/invoice_branding`, {
headers: { Authorization: `Bearer ${token}` },
});
if (res.ok) {
const data = await res.json();
const val = data.setting_value;
if (val && typeof val === 'object') {
if (val.logoUrl) setInvoiceLogoUrl(val.logoUrl);
if (val.companyName) setInvoiceCompanyName(val.companyName);
if (val.gstin) setInvoiceGstin(val.gstin);
if (val.storeAddress) setInvoiceStoreAddress(val.storeAddress);
if (val.gstRate !== undefined) setInvoiceGstRate(String(val.gstRate));
}
}
} catch {
/* branding endpoint optional */
}
};
const fetchUserProfile = async () => {
setLoadingProfile(true);
try {
const token = getAccessToken();
if (!token) return;
const payload = parseJwt(token);
const userId = payload?.sub as string;
if (!userId) return;
const user = await userService.getProfile(userId);
setProfile(user);
setFirstName(user.first_name || '');
setLastName(user.last_name || '');
setPhone(user.phone || '');
setEmail(user.email || '');
setUserName(user.display_name || user.email.split('@')[0]);
} catch {
toast.error('Failed to load user profile');
} finally {
setLoadingProfile(false);
}
};
useEffect(() => {
const token = getAccessToken();
if (token) {
const payload = parseJwt(token);
if (payload) {
setUserRole(((payload.role as string) || '').toLowerCase());
}
}
fetchBrandingSettings();
fetchUserProfile();
}, []);
useEffect(() => {
if (blade === 'branding' && !isSuperAdmin) setBlade('overview');
}, [blade, isSuperAdmin]);
const handleSaveProfile = async (e: React.FormEvent) => {
e.preventDefault();
if (!profile) return;
setSavingProfile(true);
try {
await userService.update(profile.user_id, {
first_name: firstName,
last_name: lastName,
phone: phone,
display_name: userName,
});
toast.success('Profile saved');
fetchUserProfile();
} catch (err: unknown) {
const message = err instanceof Error ? err.message : 'Failed to update profile';
toast.error(message);
} finally {
setSavingProfile(false);
}
};
const handleDiscardProfile = () => {
if (!profile) return;
setFirstName(profile.first_name || '');
setLastName(profile.last_name || '');
setPhone(profile.phone || '');
setEmail(profile.email || '');
setUserName(profile.display_name || profile.email.split('@')[0]);
};
const handleUpdatePassword = async (e: React.FormEvent) => {
e.preventDefault();
if (!newPassword || !confirmPassword) {
toast.error('Enter a new password');
return;
}
if (newPassword !== confirmPassword) {
toast.error('New password and confirmation do not match');
return;
}
setSavingPassword(true);
try {
if (!profile) return;
await userService.update(profile.user_id, {
password: newPassword,
});
toast.success('Password updated');
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
} catch (err: unknown) {
const message = err instanceof Error ? err.message : 'Password update failed';
toast.error(message);
} finally {
setSavingPassword(false);
}
};
const handleLogoFileUpload = async (e: React.ChangeEvent) => {
const file = e.target.files?.[0];
if (!file) return;
setUploadingLogo(true);
try {
const formData = new FormData();
formData.append('file', file);
formData.append('entity_type', 'invoice_branding');
formData.append('entity_id', 'logo');
const token = getAccessToken();
const backendUrl = '';
const res = await fetch(`${backendUrl}/api/v1/files/upload`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: formData,
});
if (!res.ok) {
const reader = new FileReader();
reader.onload = (evt) => {
if (evt.target?.result) {
setInvoiceLogoUrl(evt.target.result as string);
toast.success('Invoice logo uploaded');
}
};
reader.readAsDataURL(file);
return;
}
const data = await res.json();
const relativePath =
data.webp_path || data.storage_path || data.thumbnail_path || data.raw_path || data.url || data.file_path || '';
const uploadedUrl = relativePath.startsWith('http')
? relativePath
: `${backendUrl}${relativePath.startsWith('/') ? '' : '/'}${relativePath}`;
setInvoiceLogoUrl(uploadedUrl);
toast.success('Invoice logo uploaded');
} catch {
const reader = new FileReader();
reader.onload = (evt) => {
if (evt.target?.result) {
setInvoiceLogoUrl(evt.target.result as string);
toast.success('Invoice logo loaded');
}
};
reader.readAsDataURL(file);
} finally {
setUploadingLogo(false);
}
};
const handleSaveInvoiceBranding = async () => {
const brandingData = {
logoUrl: invoiceLogoUrl,
companyName: invoiceCompanyName,
gstin: invoiceGstin,
storeAddress: invoiceStoreAddress,
gstRate: Number(invoiceGstRate) || 18.0,
};
localStorage.setItem('ifixkart_invoice_branding', JSON.stringify(brandingData));
try {
const token = getAccessToken();
const backendUrl = '';
await fetch(`${backendUrl}/api/v1/settings/save/invoice_branding`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({
setting_value: brandingData,
description: 'Invoice logo and store branding configurations',
is_public: true,
}),
});
} catch {
/* save still persisted locally */
}
toast.success('Invoice logo and branding saved');
};
const copyUserId = async () => {
if (!profile?.user_id) return;
try {
await navigator.clipboard.writeText(profile.user_id);
setCopiedId(true);
toast.success('User ID copied to clipboard');
setTimeout(() => setCopiedId(false), 1500);
} catch {
toast.error('Could not copy the user ID');
}
};
const reloadAll = () => {
fetchUserProfile();
fetchBrandingSettings();
};
const navItems = useMemo(
() =>
[
{ id: 'overview' as const, label: 'Overview', hint: 'Resource essentials', icon: LayoutDashboard },
{ id: 'profile' as const, label: 'Identity', hint: 'Name, phone, username', icon: User },
{ id: 'security' as const, label: 'Access', hint: 'Password credentials', icon: KeyRound },
...(isSuperAdmin
? [{ id: 'branding' as const, label: 'Invoice branding', hint: 'Logo, GSTIN, store', icon: FileText }]
: []),
].filter((item) => {
const q = navSearch.trim().toLowerCase();
if (!q) return true;
return item.label.toLowerCase().includes(q) || item.hint.toLowerCase().includes(q);
}),
[isSuperAdmin, navSearch]
);
const dataCardShell =
'crm-radius-section border border-border bg-card shadow-[0_1px_3px_rgba(16,24,40,0.06)] overflow-visible min-w-0';
const labelClass = 'block text-[11px] font-semibold text-muted-foreground uppercase mb-1.5';
const inputClass =
'w-full h-9 px-3 crm-radius-control border border-border bg-card text-[13px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors';
const primaryButton =
'inline-flex items-center justify-center gap-2 h-9 min-h-9 px-4 crm-radius-control bg-primary hover:bg-primary/90 text-white text-[13px] font-semibold cursor-pointer disabled:opacity-50 whitespace-nowrap';
const secondaryButton =
'inline-flex items-center justify-center gap-2 h-9 min-h-9 px-4 crm-radius-control border border-border bg-card text-foreground text-[13px] font-medium hover:bg-muted cursor-pointer disabled:opacity-50 whitespace-nowrap';
const fullName = [firstName, lastName].filter(Boolean).join(' ') || profile?.email || 'Administrator';
const roleLabel = userRole ? userRole.replace(/_/g, ' ') : 'Admin';
const bladeMeta: Record = {
overview: {
title: 'Overview',
subtitle: 'Account resource essentials and current identity status.',
},
profile: {
title: 'Identity',
subtitle: 'Update the display credentials used across the admin console.',
},
security: {
title: 'Access credentials',
subtitle: 'Rotate the password used to sign in to this administrator account.',
},
branding: {
title: 'Invoice branding',
subtitle: 'Logo, legal name, GSTIN and store contact printed on invoices.',
},
};
return (
System Settings
{roleLabel}
{fullName}
{email || profile?.email || 'Administrator account'} · iFixKart console
{profile?.is_active !== false ? 'Active' : 'Inactive'}
{roleLabel}
{profile?.email_verified && (
Email verified
)}
{bladeMeta[blade].title}
{bladeMeta[blade].subtitle}
{blade === 'profile' && (
)}
{blade === 'security' && (
)}
{blade === 'branding' && isSuperAdmin && (
)}
{loadingProfile && blade !== 'branding' ? (
) : blade === 'overview' ? (
Essentials
{fullName}
{userName || '—'}
{email || '—'}
{phone || '—'}
{roleLabel}
Directory
{profile?.user_id || '—'}
{profile?.user_id && (
)}
{profile?.employee_code || '—'}
{profile?.last_login ? format(new Date(profile.last_login), 'dd MMM yyyy, hh:mm a') : '—'}
{profile?.created_at ? format(new Date(profile.created_at), 'dd MMM yyyy') : '—'}
{profile?.is_active !== false ? 'Active' : 'Inactive'}
) : blade === 'profile' ? (
) : blade === 'security' ? (
) : (
)}
);
}