160 lines
7.5 KiB
TypeScript
160 lines
7.5 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import PageLayout from '../../components/PageLayout';
|
|
import ContractEditor from './components/contractEditor';
|
|
import ContractUploadCompanyStamp from './components/contractUploadCompanyStamp';
|
|
import CompanySettingsPanel from './components/companySettingsPanel';
|
|
import ContractTemplateList from './components/contractTemplateList';
|
|
import useAuthStore from '../../store/authStore';
|
|
import { useRouter } from 'next/navigation';
|
|
|
|
const NAV = [
|
|
{ key: 'stamp', label: 'Company Stamp', icon: <svg className="w-5 h-5" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 8v4l3 3"/></svg> },
|
|
{ key: 'templates', label: 'Templates', icon: <svg className="w-5 h-5" fill="none" stroke="currentColor"><path d="M4 6h16M4 12h16M4 18h16"/></svg> },
|
|
{ key: 'editor', label: 'Create Template', icon: <svg className="w-5 h-5" fill="none" stroke="currentColor"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 113 3L7 19l-4 1 1-4 12.5-12.5z"/></svg> },
|
|
];
|
|
|
|
export default function ContractManagementPage() {
|
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
const user = useAuthStore((s) => s.user);
|
|
const [mounted, setMounted] = useState(false);
|
|
const router = useRouter();
|
|
const [section, setSection] = useState('templates');
|
|
const [editingTemplateId, setEditingTemplateId] = useState<string | null>(null);
|
|
const [editorKey, setEditorKey] = useState(0);
|
|
|
|
useEffect(() => { setMounted(true); }, []);
|
|
|
|
// Only allow admin
|
|
const isAdmin =
|
|
!!user &&
|
|
(
|
|
(user as any)?.role === 'admin' ||
|
|
(user as any)?.userType === 'admin' ||
|
|
(user as any)?.isAdmin === true ||
|
|
((user as any)?.roles?.includes?.('admin'))
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (mounted && !isAdmin) {
|
|
router.replace('/');
|
|
}
|
|
}, [mounted, isAdmin, router]);
|
|
|
|
if (!mounted) return null;
|
|
if (!isAdmin) return null;
|
|
|
|
const bumpRefresh = () => setRefreshKey((k) => k + 1);
|
|
|
|
return (
|
|
<PageLayout>
|
|
<div className="bg-gradient-to-tr from-blue-50 via-white to-blue-100 min-h-screen">
|
|
{/* tighter horizontal padding on mobile */}
|
|
<div className="flex flex-col md:flex-row max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 md:py-8 gap-6 md:gap-8">
|
|
{/* Sidebar Navigation (mobile = horizontal scroll tabs, desktop = vertical) */}
|
|
<nav className="md:w-56 w-full md:self-start md:sticky md:top-6">
|
|
<div className="flex md:flex-col flex-row gap-2 md:gap-3 overflow-x-auto md:overflow-visible -mx-4 px-4 md:mx-0 md:px-0 pb-2 md:pb-0">
|
|
{NAV.map((item) => (
|
|
<button
|
|
key={item.key}
|
|
onClick={() => {
|
|
if (section === 'editor' && item.key !== 'editor') {
|
|
setEditingTemplateId(null);
|
|
setEditorKey((k) => k + 1);
|
|
}
|
|
if (item.key === 'editor') {
|
|
setEditingTemplateId(null);
|
|
setEditorKey((k) => k + 1);
|
|
}
|
|
setSection(item.key);
|
|
}}
|
|
className={`flex flex-shrink-0 items-center gap-2 px-4 py-2 rounded-lg font-medium transition whitespace-nowrap text-sm md:text-base
|
|
${section === item.key
|
|
? 'bg-blue-900 text-blue-50 shadow'
|
|
: 'bg-white text-blue-900 hover:bg-blue-50 hover:text-blue-900 border border-blue-200'}`}
|
|
>
|
|
{item.icon}
|
|
<span>{item.label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Main Content */}
|
|
<main className="flex-1 space-y-6 md:space-y-8">
|
|
{/* sticky only on md+; smaller padding/title on mobile */}
|
|
<header className="md:sticky md:top-0 z-10 bg-white/90 backdrop-blur border-b border-blue-100 py-5 px-4 md:py-10 md:px-8 rounded-2xl shadow-lg flex flex-col gap-3 md:gap-4 mb-2 md:mb-4">
|
|
<h1 className="text-2xl md:text-4xl font-extrabold text-blue-900 tracking-tight">Contract Management</h1>
|
|
<p className="text-sm md:text-lg text-blue-700">
|
|
Manage contract templates, company stamp, and create new templates.
|
|
</p>
|
|
</header>
|
|
|
|
{/* Section Panels (compact padding on mobile) */}
|
|
{section === 'stamp' && (
|
|
<section className="rounded-2xl bg-white shadow-lg border border-gray-100 p-4 md:p-6">
|
|
<h2 className="text-xl font-semibold text-blue-900 mb-4 flex items-center gap-2">
|
|
<svg className="w-6 h-6" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 8v4l3 3"/></svg>
|
|
Company Stamp
|
|
</h2>
|
|
<ContractUploadCompanyStamp onUploaded={bumpRefresh} />
|
|
|
|
<div className="mt-8 pt-6 border-t border-gray-200">
|
|
<h3 className="text-lg font-semibold text-blue-900 mb-3 flex items-center gap-2">
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg>
|
|
Company Information
|
|
</h3>
|
|
<p className="text-sm text-gray-500 mb-4">Address details used on invoices.</p>
|
|
<CompanySettingsPanel />
|
|
</div>
|
|
</section>
|
|
)}
|
|
{section === 'templates' && (
|
|
<section className="rounded-2xl bg-white shadow-lg border border-gray-100 p-4 md:p-6">
|
|
<h2 className="text-xl font-semibold text-blue-900 mb-4 flex items-center gap-2">
|
|
<svg className="w-6 h-6" fill="none" stroke="currentColor"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
|
|
Templates
|
|
</h2>
|
|
<ContractTemplateList
|
|
refreshKey={refreshKey}
|
|
onEdit={(id) => {
|
|
setEditingTemplateId(id);
|
|
setEditorKey((k) => k + 1);
|
|
setSection('editor');
|
|
}}
|
|
/>
|
|
</section>
|
|
)}
|
|
{section === 'editor' && (
|
|
<section className="rounded-2xl bg-white shadow-lg border border-gray-100 p-4 md:p-6">
|
|
<h2 className="text-xl font-semibold text-blue-900 mb-4 flex items-center gap-2">
|
|
<svg className="w-6 h-6" fill="none" stroke="currentColor"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 113 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>
|
|
Create Template
|
|
</h2>
|
|
<ContractEditor
|
|
key={`${editorKey}-${editingTemplateId ?? 'new'}`}
|
|
editingTemplateId={editingTemplateId}
|
|
onCancelEdit={() => {
|
|
setEditingTemplateId(null);
|
|
setEditorKey((k) => k + 1);
|
|
setSection('templates');
|
|
}}
|
|
onSaved={(info) => {
|
|
bumpRefresh();
|
|
if (info?.action === 'revised') {
|
|
setEditingTemplateId(null);
|
|
setEditorKey((k) => k + 1);
|
|
setSection('templates');
|
|
}
|
|
}}
|
|
/>
|
|
</section>
|
|
)}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</PageLayout>
|
|
);
|
|
}
|