9.5 KiB
Task Fix: Quotation Template JSON Viewer Overflow & Sheet Refactor
Status
Planned Fix
Objective
Fix the Quotation Template settings page layout overflow caused by rendering a full PDFME JSON schema inline.
The page should no longer render large JSON content directly in the main content area. Instead, each template version should expose a compact View JSON action that opens a scrollable Sheet viewer.
This fix improves:
- page readability
- button visibility
- layout stability
- JSON review experience
- future maintainability for large PDFME templates
Problem
Current page:
/dashboard/crm/settings/templates
renders full JSON schema inline.
Observed issue:
- full JSON expands vertically and horizontally
- content overflows the visible page
- buttons/actions become hard to find
- page is difficult to operate
- JSON block dominates the whole settings page
Current pattern is likely similar to:
<pre>{JSON.stringify(schemaJson, null, 2)}</pre>
or:
<code>{JSON.stringify(version.schemaJson, null, 2)}</code>
This should be removed from the main page layout.
Screenshot Context
Current UI shows:
Quotation Templates
Template Configuration Center
ALLA Quotation Standard
Version 1.0
Large inline JSON block
The large inline JSON causes the page to become visually noisy and may hide or push important actions away from the viewport.
Mandatory Review
Review before implementation:
src/app/dashboard/crm/settings/templates/**
src/features/crm/document-templates/**
src/features/crm/quotations/**
src/components/ui/sheet.tsx
src/components/ui/button.tsx
Also search for inline JSON rendering:
rg "JSON.stringify|schemaJson|schema_json|<pre|<code" src/app/dashboard/crm/settings/templates src/features/crm/document-templates src/features/crm/quotations
Required UX Change
Replace inline JSON rendering with a Sheet-based viewer.
Main page should display only compact information.
Each template version card/row should show:
Version
File path
Mapping count
Table column count
Active status
View JSON button
When user clicks:
View JSON
Open a right-side Sheet.
Sheet should show:
Title: Template Schema JSON
Description: version + file path
Actions: Copy JSON
Content: formatted JSON in a scrollable code block
Scope 1: Remove Inline JSON From Main Page
Find the current inline JSON block and remove it from normal page flow.
Do not render full JSON directly inside:
Card
Accordion
Tabs
Main page content
Replace it with a compact action button.
Incorrect:
<pre className="...">
{JSON.stringify(schemaJson, null, 2)}
</pre>
Correct:
<TemplateJsonSheet
version={version.version}
filePath={version.filePath}
schemaJson={version.schemaJson}
/>
Scope 2: Create Template JSON Sheet Component
Create component:
src/features/crm/document-templates/components/template-json-sheet.tsx
If the feature folder differs, place it near the current template settings components.
Component props:
type TemplateJsonSheetProps = {
version: string;
filePath?: string | null;
schemaJson: unknown;
triggerLabel?: string;
};
Required behavior:
- client component
- uses shadcn
Sheet - trigger button opens Sheet
- formats JSON with
JSON.stringify(schemaJson ?? {}, null, 2) - displays JSON inside scrollable area
- includes copy-to-clipboard button
- handles invalid or empty JSON safely
- does not crash if schemaJson is null
Scope 3: Sheet Layout Rules
Use:
<SheetContent side="right" className="w-full sm:max-w-4xl">
or if project layout requires:
<SheetContent side="right" className="w-full max-w-[90vw] sm:max-w-5xl">
JSON block should use:
max-h-[calc(100vh-180px)]
overflow-auto
rounded-lg
border
bg-muted
p-4
font-mono
text-xs
whitespace-pre
Important:
- JSON scrolls inside Sheet
- main page does not grow because of JSON
- horizontal JSON overflow is contained inside Sheet
- Sheet remains usable on smaller screens
Scope 4: Copy JSON Action
Add:
Copy JSON
Behavior:
- copy formatted JSON to clipboard
- show success toast if toast utility exists
- if no toast utility exists, copy silently or use existing notification pattern
Recommended:
await navigator.clipboard.writeText(formattedJson);
toast.success('Copied JSON');
Rules:
- do not import a new toast library if project already has one
- use existing toast/sonner convention
Scope 5: Compact Version Card UI
Refactor version display.
Instead of showing JSON inline, display a compact card section like:
Version 1.0
src/pdfme_template/ALLA_template_pdfme_fainal3.json
Mappings: 12
Table Columns: 24
Status: Active
[View JSON]
If mapping counts are already available, use them.
If not available, show only safe existing metadata.
Do not introduce extra server queries unless necessary.
Scope 6: Empty / Missing JSON State
If schemaJson is missing:
Sheet should show:
No schema JSON available.
or:
{}
Do not throw runtime error.
Scope 7: Optional JSON Summary
If easy, add small metadata before the button:
Pages: n
Schemas: n
PDFME Version: x
Possible calculation:
const pageCount = Array.isArray(schemaJson?.schemas)
? schemaJson.schemas.length
: 0;
This is optional and should not block the fix.
Scope 8: Accessibility
Requirements:
- button label must be clear
- Sheet title must exist
- code block should be selectable
- copy button should have visible text, not icon only
- no hidden overflow on the whole page
Scope 9: Do Not Change Template Data Model
This is UI-only.
Do not change:
crm_document_templates
crm_document_template_versions
crm_document_template_mappings
crm_document_template_table_columns
PDFME schema format
template seed behavior
template upload behavior
quotation PDF generation
Suggested Component Example
'use client';
import { useMemo } from 'react';
import { Copy, Eye } from 'lucide-react';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger
} from '@/components/ui/sheet';
type TemplateJsonSheetProps = {
version: string;
filePath?: string | null;
schemaJson: unknown;
triggerLabel?: string;
};
export function TemplateJsonSheet({
version,
filePath,
schemaJson,
triggerLabel = 'View JSON'
}: TemplateJsonSheetProps) {
const formattedJson = useMemo(() => {
try {
return JSON.stringify(schemaJson ?? {}, null, 2);
} catch {
return '{}';
}
}, [schemaJson]);
const handleCopy = async () => {
await navigator.clipboard.writeText(formattedJson);
toast.success('Copied JSON');
};
return (
<Sheet>
<SheetTrigger asChild>
<Button variant='outline' size='sm'>
<Eye className='mr-2 h-4 w-4' />
{triggerLabel}
</Button>
</SheetTrigger>
<SheetContent side='right' className='w-full sm:max-w-4xl'>
<SheetHeader>
<SheetTitle>Template Schema JSON</SheetTitle>
<SheetDescription>
Version {version}
{filePath ? ` · ${filePath}` : ''}
</SheetDescription>
</SheetHeader>
<div className='mt-4 flex items-center justify-end'>
<Button variant='outline' size='sm' onClick={handleCopy}>
<Copy className='mr-2 h-4 w-4' />
Copy JSON
</Button>
</div>
<pre className='mt-4 max-h-[calc(100vh-180px)] overflow-auto rounded-lg border bg-muted p-4 font-mono text-xs whitespace-pre'>
<code>{formattedJson}</code>
</pre>
</SheetContent>
</Sheet>
);
}
Adjust import paths and formatting to match the project style.
Scope 10: Page-Level Overflow Guard
Review parent containers.
Ensure the templates page does not use layout classes that create viewport overflow unexpectedly.
Recommended:
space-y-6
overflow-hidden only when intentional
min-w-0 on flex/grid children
If cards contain long paths, use:
truncate
break-all
For file paths:
<p className="break-all text-sm text-muted-foreground">
{filePath}
</p>
Verification
Run:
npm exec tsc --noEmit
npm run build
Manual verification:
Open /dashboard/crm/settings/templates
Page no longer shows full JSON inline
Page does not overflow because of JSON
Template version card remains compact
View JSON button is visible
Click View JSON
Sheet opens from right side
JSON displays formatted
JSON scrolls inside Sheet
Copy JSON works
Close Sheet
No regression in quotation PDF preview/download
Search verification:
rg "JSON.stringify|<pre|schemaJson|schema_json" src/app/dashboard/crm/settings/templates src/features/crm/document-templates src/features/crm/quotations
Allowed:
TemplateJsonSheet
intentional JSON formatting inside Sheet
server-side schema serialization
Not allowed:
inline full JSON rendering on main settings page
Definition of Done
Task is complete when:
- Quotation Template settings page no longer renders full JSON inline
- JSON schema opens only through Sheet
- Sheet has independent scroll area
- Copy JSON action works
- Page actions remain visible
- Layout overflow is fixed
- TypeScript passes
- Build passes
Result:
Quotation Template JSON Viewer = Refactored
Template Settings Layout = Stable
Admin UX = Improved