Document page: two-row layout - details|summary+notes top, OCR|PDF bottom
This commit is contained in:
parent
6adfefff7a
commit
1b49dac87f
|
|
@ -49,135 +49,122 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 1: Details (left) | Summary + Notes (right) — equal height -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<!-- Left Column: Details -->
|
||||
<div class="space-y-6">
|
||||
<!-- Metadata Card -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Details</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<!-- Left column -->
|
||||
<dl class="space-y-4">
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Category</dt>
|
||||
<dd class="mt-1">
|
||||
<select onchange="inlineUpdateCategory(this.value)"
|
||||
class="font-medium text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-700 rounded-lg px-2 py-1 border-0 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600 text-sm">
|
||||
{{range .Categories}}
|
||||
<option value="{{.}}" {{if eq . $.Document.Category}}selected{{end}}>{{categoryIcon .}} {{title .}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</dd>
|
||||
</div>
|
||||
{{if .Document.Vendor}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Vendor</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{.Document.Vendor}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Document.Amount}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Amount</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{.Document.Amount}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
</dl>
|
||||
<!-- Right column -->
|
||||
<dl class="space-y-4">
|
||||
{{if .Document.Date}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Date</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{formatDate .Document.Date}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Document.ProcessedAt}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Processed</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{formatDateTime .Document.ProcessedAt}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Document.OriginalFile}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Original File</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white truncate">{{.Document.OriginalFile}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
</dl>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden flex flex-col">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Details</h2>
|
||||
</div>
|
||||
<div class="p-6 flex-1">
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<dl class="space-y-4">
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Category</dt>
|
||||
<dd class="mt-1">
|
||||
<select onchange="inlineUpdateCategory(this.value)"
|
||||
class="font-medium text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-700 rounded-lg px-2 py-1 border-0 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600 text-sm">
|
||||
{{range .Categories}}
|
||||
<option value="{{.}}" {{if eq . $.Document.Category}}selected{{end}}>{{categoryIcon .}} {{title .}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</dd>
|
||||
</div>
|
||||
{{if .Document.Vendor}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Vendor</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{.Document.Vendor}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Document.Amount}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Amount</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{.Document.Amount}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
</dl>
|
||||
<dl class="space-y-4">
|
||||
{{if .Document.Date}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Date</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{formatDate .Document.Date}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Document.ProcessedAt}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Processed</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white">{{formatDateTime .Document.ProcessedAt}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Document.OriginalFile}}
|
||||
<div>
|
||||
<dt class="text-sm text-gray-500 dark:text-gray-400">Original File</dt>
|
||||
<dd class="mt-1 font-medium text-gray-900 dark:text-white truncate">{{.Document.OriginalFile}}</dd>
|
||||
</div>
|
||||
{{end}}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
{{if .Document.Summary}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Summary</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{.Document.Summary}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Notes -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Notes</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
{{if .Document.Notes}}
|
||||
<p class="text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{.Document.Notes}}</p>
|
||||
{{else}}
|
||||
<p class="text-gray-400 dark:text-gray-500 italic">No notes yet. Click Edit to add notes.</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Full Text -->
|
||||
{{if .Document.FullText}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">OCR Text</h2>
|
||||
<button onclick="copyText()" class="text-sm text-brand-600 dark:text-brand-400 hover:underline">Copy</button>
|
||||
</div>
|
||||
<div class="p-6 max-h-96 overflow-auto">
|
||||
<div id="ocr-text" class="text-sm text-gray-700 dark:text-gray-300 prose dark:prose-invert max-w-none">{{.Document.FullText | safe}}</div>
|
||||
<script>
|
||||
// Simple markdown rendering
|
||||
(function() {
|
||||
const el = document.getElementById('ocr-text');
|
||||
let md = el.textContent;
|
||||
// Headers
|
||||
md = md.replace(/^### (.+)$/gm, '<h4 class="font-semibold mt-4 mb-2">$1</h4>');
|
||||
md = md.replace(/^## (.+)$/gm, '<h3 class="font-semibold text-lg mt-4 mb-2">$1</h3>');
|
||||
// Bold
|
||||
md = md.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||
// Tables (simple)
|
||||
md = md.replace(/\|(.+)\|/g, function(match) {
|
||||
const cells = match.split('|').filter(c => c.trim());
|
||||
return '<tr>' + cells.map(c => '<td class="border px-2 py-1">' + c.trim() + '</td>').join('') + '</tr>';
|
||||
});
|
||||
md = md.replace(/(<tr>.*<\/tr>\n?)+/g, '<table class="border-collapse border my-2">$&</table>');
|
||||
// Bullets
|
||||
md = md.replace(/^- (.+)$/gm, '<li>$1</li>');
|
||||
md = md.replace(/(<li>.*<\/li>\n?)+/g, '<ul class="list-disc ml-4 my-2">$&</ul>');
|
||||
// Line breaks
|
||||
md = md.replace(/\n/g, '<br>');
|
||||
el.innerHTML = md;
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<!-- Right Column: PDF Viewer -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden flex flex-col">
|
||||
{{if .Document.Summary}}
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Summary</h2>
|
||||
</div>
|
||||
<div class="p-6 border-b border-gray-100 dark:border-gray-700">
|
||||
<p class="text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{.Document.Summary}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Notes</h2>
|
||||
</div>
|
||||
<div class="p-6 flex-1">
|
||||
{{if .Document.Notes}}
|
||||
<p class="text-gray-700 dark:text-gray-300 whitespace-pre-wrap">{{.Document.Notes}}</p>
|
||||
{{else}}
|
||||
<p class="text-gray-400 dark:text-gray-500 italic">No notes yet. Click Edit to add notes.</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: OCR Text (left) | PDF Viewer (right) -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{{if .Document.FullText}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">OCR Text</h2>
|
||||
<button onclick="copyText()" class="text-sm text-brand-600 dark:text-brand-400 hover:underline">Copy</button>
|
||||
</div>
|
||||
<div class="p-6 max-h-[700px] overflow-auto">
|
||||
<div id="ocr-text" class="text-sm text-gray-700 dark:text-gray-300 prose dark:prose-invert max-w-none">{{.Document.FullText | safe}}</div>
|
||||
<script>
|
||||
(function() {
|
||||
const el = document.getElementById('ocr-text');
|
||||
let md = el.textContent;
|
||||
md = md.replace(/^### (.+)$/gm, '<h4 class="font-semibold mt-4 mb-2">$1</h4>');
|
||||
md = md.replace(/^## (.+)$/gm, '<h3 class="font-semibold text-lg mt-4 mb-2">$1</h3>');
|
||||
md = md.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||
md = md.replace(/\|(.+)\|/g, function(match) {
|
||||
const cells = match.split('|').filter(c => c.trim());
|
||||
return '<tr>' + cells.map(c => '<td class="border px-2 py-1">' + c.trim() + '</td>').join('') + '</tr>';
|
||||
});
|
||||
md = md.replace(/(<tr>.*<\/tr>\n?)+/g, '<table class="border-collapse border my-2">$&</table>');
|
||||
md = md.replace(/^- (.+)$/gm, '<li>$1</li>');
|
||||
md = md.replace(/(<li>.*<\/li>\n?)+/g, '<ul class="list-disc ml-4 my-2">$&</ul>');
|
||||
md = md.replace(/\n/g, '<br>');
|
||||
el.innerHTML = md;
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div></div>
|
||||
{{end}}
|
||||
|
||||
<div>
|
||||
{{if .Document.PDFPath}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden sticky top-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 class="font-semibold text-gray-900 dark:text-white">Document Preview</h2>
|
||||
<div class="flex items-center space-x-2">
|
||||
|
|
|
|||
Loading…
Reference in New Issue