diff --git a/src/app/admin/contract-management/components/contractEditor.tsx b/src/app/admin/contract-management/components/contractEditor.tsx index 21ef233..543e394 100644 --- a/src/app/admin/contract-management/components/contractEditor.tsx +++ b/src/app/admin/contract-management/components/contractEditor.tsx @@ -132,29 +132,29 @@ export default function ContractEditor({ onSaved }: Props) { }; return ( -
-
-
+
+
+
setName(e.target.value)} - className="w-full sm:w-1/2 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50" + className="w-full sm:w-1/2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50 shadow" />
{isPreview && ( @@ -163,18 +163,18 @@ export default function ContractEditor({ onSaved }: Props) {
{/* New metadata inputs */} -
+
setType(e.target.value)} - className="w-full sm:w-1/3 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50" + className="w-full sm:w-1/3 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50 shadow" /> setQ(e.target.value)} - className="w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50" + className="w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50 shadow" />
-
    +
    {filtered.map((c) => ( -
  • -
    -
    -

    {c.name}

    - -
    -

    Version {c.version}{c.updatedAt ? ` • Updated ${new Date(c.updatedAt).toLocaleString()}` : ''}

    +
    +
    +

    {c.name}

    +
    -
    - - - - + + +
    -
  • +
    ))} {!filtered.length && ( -
  • No contracts found.
  • +
    No contracts found.
    )} -
+
); } diff --git a/src/app/admin/contract-management/components/contractUploadCompanyStamp.tsx b/src/app/admin/contract-management/components/contractUploadCompanyStamp.tsx index 0e7b021..afba3ce 100644 --- a/src/app/admin/contract-management/components/contractUploadCompanyStamp.tsx +++ b/src/app/admin/contract-management/components/contractUploadCompanyStamp.tsx @@ -212,48 +212,48 @@ export default function ContractUploadCompanyStamp({ onUploaded }: Props) { }; return ( -
+
{/* Header with Add New Stamp modal trigger */}

Manage your company stamps. One active at a time.

{/* Emphasized Active stamp */} {activeStamp && ( -
-
+
+
{activeStamp.base64 ? ( Active stamp ) : ( -
+
no image
)} - + Active
-

{activeStamp.label || activeStamp.id}

-

This stamp is auto-applied to documents where applicable.

+

{activeStamp.label || activeStamp.id}

+

Auto-applied to documents where applicable.

@@ -266,33 +266,33 @@ export default function ContractUploadCompanyStamp({ onUploaded }: Props) { {!!stamps.length && (

Your Company Stamps

-
    +
      {stamps.map((s) => { const src = toImgSrc(s); const activeCls = s.active - ? 'border-green-300 bg-green-50' - : 'border-gray-200 hover:border-gray-300 transition-colors'; + ? 'border-green-300 bg-green-50 shadow' + : 'border-gray-200 hover:border-indigo-300 transition-colors'; return (
    • {s.base64 ? ( Stamp ) : ( -
      +
      no image
      )}
      {s.label || s.id} {s.active && ( - + Active )} @@ -302,21 +302,21 @@ export default function ContractUploadCompanyStamp({ onUploaded }: Props) { {s.active ? ( ) : ( )} @@ -333,14 +333,14 @@ export default function ContractUploadCompanyStamp({ onUploaded }: Props) {
      -
      -
      -

      Add New Stamp

      -

      +

      +
      +

      Add New Stamp

      +

      Accepted types: PNG, JPEG, WebP, SVG. Max size: 5MB.

      -
      +
      e.preventDefault()} onDrop={onDrop} - className="rounded-xl border-2 border-dashed border-gray-300 hover:border-indigo-400 transition-colors p-4 bg-gray-50" + className="rounded-xl border-2 border-dashed border-indigo-300 hover:border-indigo-400 transition-colors p-4 bg-indigo-50" >
      {modalPreviewUrl ? ( @@ -365,13 +365,13 @@ export default function ContractUploadCompanyStamp({ onUploaded }: Props) { className="h-20 w-20 object-contain rounded-lg ring-1 ring-gray-200 bg-white" /> ) : ( -
      +
      No image
      )}

      Drag and drop your stamp here

      -

      or click to browse

      +

      or click to browse

      -
      +
      @@ -405,7 +405,7 @@ export default function ContractUploadCompanyStamp({ onUploaded }: Props) { type="button" onClick={confirmUpload} disabled={modalUploading || !modalFile} - className="text-sm px-3 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white disabled:opacity-60" + className="text-sm px-4 py-2 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white disabled:opacity-60 transition" > {modalUploading ? 'Uploading…' : 'Upload'} diff --git a/src/app/admin/contract-management/page.tsx b/src/app/admin/contract-management/page.tsx index a8349d0..14290ac 100644 --- a/src/app/admin/contract-management/page.tsx +++ b/src/app/admin/contract-management/page.tsx @@ -8,15 +8,20 @@ import ContractTemplateList from './components/contractTemplateList'; import useAuthStore from '../../store/authStore'; import { useRouter } from 'next/navigation'; +const NAV = [ + { key: 'stamp', label: 'Company Stamp', icon: }, + { key: 'templates', label: 'Templates', icon: }, + { key: 'editor', label: 'Create Template', icon: }, +]; + 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'); - useEffect(() => { - setMounted(true); - }, []); + useEffect(() => { setMounted(true); }, []); // Only allow admin const isAdmin = @@ -30,7 +35,7 @@ export default function ContractManagementPage() { useEffect(() => { if (mounted && !isAdmin) { - router.replace('/'); // or show a 403 page + router.replace('/'); } }, [mounted, isAdmin, router]); @@ -41,33 +46,63 @@ export default function ContractManagementPage() { return ( - {/* Force white background for this page */} -
      -
      -
      -

      Contract Management

      -

      - Create contract templates in HTML, upload the company stamp, and manage existing contracts. -

      -
      +
      +
      + {/* Sidebar Navigation */} + - {/* Reordered vertical layout */} -
      -
      -

      Company Stamp

      - -
      + {/* Main Content */} +
      +
      +

      Contract Management

      +

      + Manage contract templates, company stamp, and create new templates. +

      +
      -
      -

      Contracts

      - -
      - -
      -

      Create Template

      - -
      -
      + {/* Section Panels */} + {section === 'stamp' && ( +
      +

      + + Company Stamp +

      + +
      + )} + {section === 'templates' && ( +
      +

      + + Templates +

      + +
      + )} + {section === 'editor' && ( +
      +

      + + Create Template +

      + +
      + )} +
      diff --git a/src/app/admin/matrix-management/detail/page.tsx b/src/app/admin/matrix-management/detail/page.tsx index e852783..7fcc5bf 100644 --- a/src/app/admin/matrix-management/detail/page.tsx +++ b/src/app/admin/matrix-management/detail/page.tsx @@ -266,35 +266,34 @@ export default function MatrixDetailPage() { {refreshing && (
      - + Refreshing…
      )} - {/* Centered page container to avoid full-width stretch */} -
      -
      - {/* Modern header card with action */} -
      -
      -
      +
      +
      + {/* Header card */} +
      +
      +
      -

      {matrixName}

      -

      - Top node: {topNodeEmail} +

      {matrixName}

      +

      + Top node: {topNodeEmail}

      - + Children/node: 5 - + Max depth: {(!serverMaxDepth || serverMaxDepth <= 0) ? 'Unlimited' : serverMaxDepth}
      @@ -302,90 +301,89 @@ export default function MatrixDetailPage() {
      -
      +
      - {/* banner for unlimited */} + {/* Banner for unlimited */} {isUnlimited && (
      Large structure. Results are paginated by depth and count.
      )} - {/* sticky depth controls */} -
      + {/* Sticky depth controls */} +
      - setDepthA(Math.max(0, Number(e.target.value) || 0))} className="w-16 rounded border border-gray-300 px-2 py-1 text-xs" /> + setDepthA(Math.max(0, Number(e.target.value) || 0))} className="w-16 rounded-lg border border-gray-300 px-2 py-1 text-xs" /> - setDepthB(Math.max(depthA, Number(e.target.value) || depthA))} className="w-16 rounded border border-gray-300 px-2 py-1 text-xs" /> + setDepthB(Math.max(depthA, Number(e.target.value) || depthA))} className="w-16 rounded-lg border border-gray-300 px-2 py-1 text-xs" />
      - {/* NEW: include root toggle */}
      Showing levels {depthA}–{depthB} of {isUnlimited ? 'Unlimited' : serverMaxDepth}
      - +
      - {/* small stats */} -
      -
      -
      Users (current slice)
      -
      {usersInSlice.length}
      + {/* Small stats */} +
      +
      +
      Users (current slice)
      +
      {usersInSlice.length}
      -
      -
      Total descendants
      -
      {isUnlimited ? 'All descendants so far' : totalDescendants}
      +
      +
      Total descendants
      +
      {isUnlimited ? 'All descendants so far' : totalDescendants}
      -
      -
      Active levels loaded
      -
      {depthA}–{depthB}
      +
      +
      Active levels loaded
      +
      {depthA}–{depthB}
      - {/* dynamic levels */} -
      + {/* Dynamic levels */} +
      {Array .from(byLevel.keys()) .filter(l => l >= depthA && l <= depthB) - .filter(l => includeRoot || l !== 0) // NEW: hide level 0 when unchecked + .filter(l => includeRoot || l !== 0) .sort((a,b)=>a-b) .map(l => ( ))}
      - {/* jump to level */} -
      + {/* Jump to level */} +
      - { const lv = Number(e.target.value) || 0 setDepthA(lv); setDepthB(Math.max(lv, lv + 5)) }}> @@ -394,10 +392,10 @@ export default function MatrixDetailPage() {
      {/* Collapsible All users list by level */} -
      -
      -

      All users in this matrix

      -

      Grouped by levels (power of five structure).

      +
      +
      +

      All users in this matrix

      +

      Grouped by levels (power of five structure).

      {[0,1,2,3,4,5].map(lvl => { @@ -414,15 +412,14 @@ export default function MatrixDetailPage() { return globalMatch && levelMatch }) return ( -
      +
      - -
      + {/* Error banner for stats */} {statsError && ( @@ -277,17 +279,17 @@ export default function MatrixManagementPage() { )} {/* Stats */} -
      +
      - +
      {/* Filters */} -
      +
      - setPolicyFilter(e.target.value as any)} className="rounded-lg border border-gray-300 px-3 py-2 text-sm bg-white shadow"> @@ -295,13 +297,13 @@ export default function MatrixManagementPage() {
      - setSortByUsers(e.target.value as any)} className="rounded-lg border border-gray-300 px-3 py-2 text-sm bg-white shadow">
      - ℹ️ Tooltip: Users count respects each matrix’s max depth policy. + ℹ️ Users count respects each matrix’s max depth policy.
      @@ -313,12 +315,11 @@ export default function MatrixManagementPage() { )} {/* Matrix cards */} -
      +
      {statsLoading ? ( - // Simple skeleton Array.from({ length: 3 }).map((_, i) => ( -
      -
      +
      +
      @@ -330,14 +331,14 @@ export default function MatrixManagementPage() {
      No matrices found.
      ) : ( matricesView.map(m => ( -
      -
      +
      +
      -

      {m.name}

      +

      {m.name}

      - + Max depth: {(!m.policyMaxDepth || m.policyMaxDepth <= 0) ? 'Unlimited' : m.policyMaxDepth}
      @@ -361,18 +362,16 @@ export default function MatrixManagementPage() {
      - {/* Quick default A–B editor */}
      - Default depth slice: + Default depth slice: localStorage.setItem(`matrixDepthA:${m.id}`, String(Math.max(0, Number(e.target.value) || 0)))} - className="w-16 rounded border border-gray-300 px-2 py-1 text-[11px]" + className="w-16 rounded-lg border border-gray-300 px-2 py-1 text-xs" /> - to + to localStorage.setItem(`matrixDepthB:${m.id}`, String(Math.max(0, Number(e.target.value) || 0)))} - className="w-16 rounded border border-gray-300 px-2 py-1 text-[11px]" + className="w-16 rounded-lg border border-gray-300 px-2 py-1 text-xs" />
      @@ -420,9 +418,9 @@ export default function MatrixManagementPage() {
      { setCreateOpen(false); resetForm() }} />
      -
      -
      -

      Create New Matrix

      +
      +
      +

      Create Matrix

      -
      + {/* Success banner */} {createSuccess && (
      @@ -451,7 +449,7 @@ export default function MatrixManagementPage() { type="button" onClick={confirmForce} disabled={createLoading} - className="rounded-md bg-amber-600 hover:bg-amber-500 text-white px-3 py-1.5 text-xs font-medium disabled:opacity-50" + className="rounded-lg bg-amber-600 hover:bg-amber-500 text-white px-4 py-2 text-xs font-medium disabled:opacity-50" > Replace (force) @@ -459,7 +457,7 @@ export default function MatrixManagementPage() { type="button" onClick={() => setForcePrompt(null)} disabled={createLoading} - className="rounded-md border border-amber-300 px-3 py-1.5 text-xs font-medium text-amber-800 hover:bg-amber-100 disabled:opacity-50" + className="rounded-lg border border-amber-300 px-4 py-2 text-xs font-medium text-amber-800 hover:bg-amber-100 disabled:opacity-50" > Cancel @@ -469,24 +467,24 @@ export default function MatrixManagementPage() { {/* Form fields */}
      - + setCreateName(e.target.value)} disabled={createLoading} - className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent disabled:bg-gray-100" + className="w-full rounded-lg border border-gray-300 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-900 focus:border-transparent disabled:bg-gray-100" placeholder="e.g., Platinum Matrix" />
      - + setCreateEmail(e.target.value)} disabled={createLoading} - className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent disabled:bg-gray-100" + className="w-full rounded-lg border border-gray-300 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-900 focus:border-transparent disabled:bg-gray-100" placeholder="owner@example.com" />
      @@ -497,19 +495,19 @@ export default function MatrixManagementPage() {
      )} -
      +
      {/* Server Status & Logs */} -
      -
      -
      - +
      +
      +
      +
      -

      +

      Server Status & Logs

      -

      +

      System health, resource usage & recent error insights.

      -
      +
      {/* Metrics */}
      -

      +

      Server Status:{' '} {serverStats.status === 'Online' ? 'Server Online' : 'Offline'}

      -
      +

      Uptime: {serverStats.uptime}

      CPU Usage: {serverStats.cpu}

      Memory Usage: {serverStats.memory} GB

      -
      +
      Autoscaled environment (mock)
      @@ -229,11 +259,11 @@ export default function AdminDashboardPage() { {/* Logs */}
      -

      +

      Recent Error Logs

      {serverStats.recentErrors.length === 0 && ( -

      +

      No recent logs.

      )} @@ -242,12 +272,12 @@ export default function AdminDashboardPage() {
      diff --git a/src/app/admin/subscriptions/createSubscription/page.tsx b/src/app/admin/subscriptions/createSubscription/page.tsx index f6efe61..89068c5 100644 --- a/src/app/admin/subscriptions/createSubscription/page.tsx +++ b/src/app/admin/subscriptions/createSubscription/page.tsx @@ -55,51 +55,61 @@ export default function CreateSubscriptionPage() { return ( -
      -
      -
      -
      -

      Create Subscription

      -

      Add a new product or subscription plan.

      +
      +
      + {/* Header */} +
      +
      +
      +

      Create Subscription

      +

      Add a new product or subscription plan.

      +
      + + + Back to list +
      - - Back to list - -
      + -
      +
      + {/* Title */}
      - - setTitle(e.target.value)} /> + + setTitle(e.target.value)} />
      + {/* Quantity */}
      - - setQuantity(Number(e.target.value))} /> + + setQuantity(Number(e.target.value))} />
      - + {/* Price */}
      - - setPrice(Number(e.target.value))} /> + + setPrice(Number(e.target.value))} />
      + {/* Currency */}
      - - setCurrency(e.target.value.toUpperCase().slice(0,3))} /> + + setCurrency(e.target.value.toUpperCase().slice(0,3))} />
      - + {/* Tax Rate */}
      - - setTaxRate(e.target.value === '' ? undefined : Number(e.target.value))} /> + + setTaxRate(e.target.value === '' ? undefined : Number(e.target.value))} />
      + {/* Featured */}
      - setIsFeatured(e.target.checked)} /> - + setIsFeatured(e.target.checked)} /> +
      - + {/* Billing Interval */}
      - - { const v = e.target.value as any; setBillingInterval(v); if (v) { @@ -115,57 +125,77 @@ export default function CreateSubscriptionPage() {
      + {/* Interval Count */}
      - - setIntervalCount(e.target.value === '' ? undefined : Number(e.target.value))} disabled={!billingInterval} /> + + setIntervalCount(e.target.value === '' ? undefined : Number(e.target.value))} disabled={!billingInterval} />
      - + {/* SKU */}
      - - setSku(e.target.value)} /> + + setSku(e.target.value)} />
      + {/* Slug */}
      - - setSlug(e.target.value)} /> + + setSlug(e.target.value)} />
      - + {/* Availability */}
      - - setState(e.target.value as any)}>
      + {/* Description */}
      - -