"use client"; import { useSession } from "next-auth/react"; import { useEffect, useState, useCallback } from "react"; import { useRouter, useParams } from "next/navigation"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Building2, Users, Database, Settings, ArrowLeft, Save, Trash2, UserPlus, Mail, Shield, Activity, Calendar } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; interface User { id: string; name: string; email: string; role: string; createdAt: string; invitedBy: string | null; invitedAt: string | null; } interface Company { id: string; name: string; email: string; status: string; maxUsers: number; createdAt: string; updatedAt: string; users: User[]; _count: { sessions: number; imports: number; }; } export default function CompanyManagement() { const { data: session, status } = useSession(); const router = useRouter(); const params = useParams(); const { toast } = useToast(); const [company, setCompany] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [editData, setEditData] = useState>({}); const [originalData, setOriginalData] = useState>({}); const [showInviteUser, setShowInviteUser] = useState(false); const [inviteData, setInviteData] = useState({ name: "", email: "", role: "USER" }); const [showUnsavedChangesDialog, setShowUnsavedChangesDialog] = useState(false); const [pendingNavigation, setPendingNavigation] = useState(null); // Function to check if data has been modified const hasUnsavedChanges = useCallback(() => { // Normalize data for comparison (handle null/undefined/empty string equivalence) const normalizeValue = (value: any) => { if (value === null || value === undefined || value === "") { return ""; } return value; }; const normalizedEditData = { name: normalizeValue(editData.name), email: normalizeValue(editData.email), status: normalizeValue(editData.status), maxUsers: editData.maxUsers || 0, }; const normalizedOriginalData = { name: normalizeValue(originalData.name), email: normalizeValue(originalData.email), status: normalizeValue(originalData.status), maxUsers: originalData.maxUsers || 0, }; return JSON.stringify(normalizedEditData) !== JSON.stringify(normalizedOriginalData); }, [editData, originalData]); // Handle navigation protection - must be at top level const handleNavigation = useCallback((url: string) => { // Allow navigation within the same company (different tabs, etc.) if (url.includes(`/platform/companies/${params.id}`)) { router.push(url); return; } // If there are unsaved changes, show confirmation dialog if (hasUnsavedChanges()) { setPendingNavigation(url); setShowUnsavedChangesDialog(true); } else { router.push(url); } }, [router, params.id, hasUnsavedChanges]); useEffect(() => { if (status === "loading") return; if (!session?.user?.isPlatformUser) { router.push("/platform/login"); return; } fetchCompany(); }, [session, status, router, params.id]); const fetchCompany = async () => { try { const response = await fetch(`/api/platform/companies/${params.id}`); if (response.ok) { const data = await response.json(); setCompany(data); const companyData = { name: data.name, email: data.email, status: data.status, maxUsers: data.maxUsers, }; setEditData(companyData); setOriginalData(companyData); } else { toast({ title: "Error", description: "Failed to load company data", variant: "destructive", }); } } catch (error) { console.error("Failed to fetch company:", error); toast({ title: "Error", description: "Failed to load company data", variant: "destructive", }); } finally { setIsLoading(false); } }; const handleSave = async () => { setIsSaving(true); try { const response = await fetch(`/api/platform/companies/${params.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(editData), }); if (response.ok) { const updatedCompany = await response.json(); setCompany(updatedCompany); const companyData = { name: updatedCompany.name, email: updatedCompany.email, status: updatedCompany.status, maxUsers: updatedCompany.maxUsers, }; setOriginalData(companyData); toast({ title: "Success", description: "Company updated successfully", }); } else { throw new Error("Failed to update company"); } } catch (error) { toast({ title: "Error", description: "Failed to update company", variant: "destructive", }); } finally { setIsSaving(false); } }; const handleStatusChange = async (newStatus: string) => { const statusAction = newStatus === "SUSPENDED" ? "suspend" : "activate"; try { const response = await fetch(`/api/platform/companies/${params.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: newStatus }), }); if (response.ok) { setCompany(prev => prev ? { ...prev, status: newStatus } : null); setEditData(prev => ({ ...prev, status: newStatus })); toast({ title: "Success", description: `Company ${statusAction}d successfully`, }); } else { throw new Error(`Failed to ${statusAction} company`); } } catch (error) { toast({ title: "Error", description: `Failed to ${statusAction} company`, variant: "destructive", }); } }; const confirmNavigation = () => { if (pendingNavigation) { router.push(pendingNavigation); setPendingNavigation(null); } setShowUnsavedChangesDialog(false); }; const cancelNavigation = () => { setPendingNavigation(null); setShowUnsavedChangesDialog(false); }; // Protect against browser back/forward and other navigation useEffect(() => { const handleBeforeUnload = (e: BeforeUnloadEvent) => { if (hasUnsavedChanges()) { e.preventDefault(); e.returnValue = ''; } }; const handlePopState = (e: PopStateEvent) => { if (hasUnsavedChanges()) { const confirmLeave = window.confirm( 'You have unsaved changes. Are you sure you want to leave this page?' ); if (!confirmLeave) { // Push the current state back to prevent navigation window.history.pushState(null, '', window.location.href); e.preventDefault(); } } }; window.addEventListener('beforeunload', handleBeforeUnload); window.addEventListener('popstate', handlePopState); return () => { window.removeEventListener('beforeunload', handleBeforeUnload); window.removeEventListener('popstate', handlePopState); }; }, [hasUnsavedChanges]); const handleInviteUser = async () => { try { const response = await fetch(`/api/platform/companies/${params.id}/users`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(inviteData), }); if (response.ok) { setShowInviteUser(false); setInviteData({ name: "", email: "", role: "USER" }); fetchCompany(); // Refresh company data toast({ title: "Success", description: "User invited successfully", }); } else { throw new Error("Failed to invite user"); } } catch (error) { toast({ title: "Error", description: "Failed to invite user", variant: "destructive", }); } }; const getStatusBadgeVariant = (status: string) => { switch (status) { case "ACTIVE": return "default"; case "TRIAL": return "secondary"; case "SUSPENDED": return "destructive"; case "ARCHIVED": return "outline"; default: return "default"; } }; if (status === "loading" || isLoading) { return (
Loading company details...
); } if (!session?.user?.isPlatformUser || !company) { return null; } const canEdit = session.user.platformRole === "SUPER_ADMIN"; return (

{company.name}

{company.status}

Company Management

{canEdit && ( )}
Overview Users Settings Analytics {/* Stats Overview */}
Total Users
{company.users.length}

of {company.maxUsers} maximum

Total Sessions
{company._count.sessions}
Data Imports
{company._count.imports}
Created
{new Date(company.createdAt).toLocaleDateString()}
{/* Company Info */} Company Information
setEditData(prev => ({ ...prev, name: e.target.value }))} disabled={!canEdit} />
setEditData(prev => ({ ...prev, email: e.target.value }))} disabled={!canEdit} />
setEditData(prev => ({ ...prev, maxUsers: parseInt(e.target.value) }))} disabled={!canEdit} />
{canEdit && hasUnsavedChanges() && (
)}
Users ({company.users.length}) {canEdit && ( )}
{company.users.map((user) => (
{user.name?.charAt(0) || user.email.charAt(0).toUpperCase()}
{user.name || "No name"}
{user.email}
{user.role}
Joined {new Date(user.createdAt).toLocaleDateString()}
))} {company.users.length === 0 && (
No users found. Invite the first user to get started.
)}
Danger Zone {canEdit && ( <>

Suspend Company

Temporarily disable access to this company

Suspend Company Are you sure you want to suspend this company? This will disable access for all users. Cancel handleStatusChange("SUSPENDED")}> Suspend
{company.status === "SUSPENDED" && (

Reactivate Company

Restore access to this company

)} )}
Analytics
Analytics dashboard coming soon...
{/* Invite User Dialog */} {showInviteUser && (
Invite User
setInviteData(prev => ({ ...prev, name: e.target.value }))} placeholder="User's full name" />
setInviteData(prev => ({ ...prev, email: e.target.value }))} placeholder="user@example.com" />
)} {/* Unsaved Changes Dialog */} Unsaved Changes You have unsaved changes that will be lost if you leave this page. Are you sure you want to continue? Stay on Page Leave Without Saving
); }