feat(frontend): convert ExportPreviewModal to editable textarea with reset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-02-13 12:59:44 -05:00
parent 0a67a846de
commit 808c8d49d1

View File

@@ -1,5 +1,5 @@
import { useState } from 'react' import { useState, useEffect } from 'react'
import { Copy, Download, Check } from 'lucide-react' import { Copy, Download, Check, RotateCcw } from 'lucide-react'
import { Modal } from '@/components/common/Modal' import { Modal } from '@/components/common/Modal'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
@@ -9,7 +9,9 @@ interface ExportPreviewModalProps {
content: string content: string
filename: string filename: string
format: 'markdown' | 'text' | 'html' | 'psa' format: 'markdown' | 'text' | 'html' | 'psa'
onDownload: () => void onDownload: (content: string) => void
includeSummary?: boolean
onToggleSummary?: (include: boolean) => void
} }
export function ExportPreviewModal({ export function ExportPreviewModal({
@@ -19,12 +21,20 @@ export function ExportPreviewModal({
filename, filename,
format, format,
onDownload, onDownload,
includeSummary = false,
onToggleSummary,
}: ExportPreviewModalProps) { }: ExportPreviewModalProps) {
const [copied, setCopied] = useState(false) const [copied, setCopied] = useState(false)
const [editedContent, setEditedContent] = useState(content)
// Sync editedContent when content prop changes (new fetch / summary toggle)
useEffect(() => {
setEditedContent(content)
}, [content])
const handleCopy = async () => { const handleCopy = async () => {
try { try {
await navigator.clipboard.writeText(content) await navigator.clipboard.writeText(editedContent)
setCopied(true) setCopied(true)
setTimeout(() => setCopied(false), 2000) setTimeout(() => setCopied(false), 2000)
} catch (err) { } catch (err) {
@@ -33,39 +43,79 @@ export function ExportPreviewModal({
} }
const handleDownload = () => { const handleDownload = () => {
onDownload() onDownload(editedContent)
onClose() onClose()
} }
// Reset copied state when modal closes const handleReset = () => {
setEditedContent(content)
}
const handleClose = () => { const handleClose = () => {
setCopied(false) setCopied(false)
onClose() onClose()
} }
const isModified = editedContent !== content
return ( return (
<Modal isOpen={isOpen} onClose={handleClose} title="Export Preview" size="xl"> <Modal isOpen={isOpen} onClose={handleClose} title="Export Preview" size="xl">
{/* Filename and format info */} {/* Filename, format info, and controls */}
<p className="mb-3 text-sm text-white/70"> <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
Filename: <span className="font-mono text-white">{filename}</span> <p className="text-sm text-white/70">
<span className="ml-3 rounded bg-white/10 px-2 py-0.5 text-xs text-white/70"> Filename: <span className="font-mono text-white">{filename}</span>
{format === 'markdown' ? 'Markdown' : format === 'html' ? 'HTML' : 'Plain Text'} <span className="ml-3 rounded bg-white/10 px-2 py-0.5 text-xs text-white/70">
</span> {format === 'markdown' ? 'Markdown' : format === 'html' ? 'HTML' : format === 'psa' ? 'PSA' : 'Plain Text'}
</p> </span>
{isModified && (
{/* Content Preview */} <span className="ml-2 text-xs text-yellow-400">(edited)</span>
<div )}
className={cn( </p>
'max-h-96 overflow-auto rounded-md border border-white/10 bg-black/50 p-4', <div className="flex items-center gap-3">
'font-mono text-sm text-white' {onToggleSummary && (
)} <label className="flex items-center gap-2 text-sm text-white/60 cursor-pointer">
> <input
<pre className="whitespace-pre-wrap">{content}</pre> type="checkbox"
checked={includeSummary}
onChange={(e) => onToggleSummary(e.target.checked)}
className="h-4 w-4 rounded border-white/20 bg-black/50"
/>
Include Summary
</label>
)}
{isModified && (
<button
type="button"
onClick={handleReset}
className="flex items-center gap-1 text-xs text-white/40 hover:text-white"
title="Reset to original"
>
<RotateCcw className="h-3 w-3" />
Reset
</button>
)}
</div>
</div> </div>
{/* Editable Content */}
<label htmlFor="export-content" className="sr-only">
Export content
</label>
<textarea
id="export-content"
value={editedContent}
onChange={(e) => setEditedContent(e.target.value)}
className={cn(
'h-96 w-full resize-y rounded-md border border-white/10 bg-black/50 p-4',
'font-mono text-sm text-white',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
)}
/>
{/* Actions */} {/* Actions */}
<div className="mt-4 flex items-center justify-end gap-2"> <div className="mt-4 flex items-center justify-end gap-2">
<button <button
type="button"
onClick={handleCopy} onClick={handleCopy}
className={cn( className={cn(
'flex items-center gap-2 rounded-md border border-white/10 px-3 py-2 text-sm font-medium', 'flex items-center gap-2 rounded-md border border-white/10 px-3 py-2 text-sm font-medium',
@@ -86,6 +136,7 @@ export function ExportPreviewModal({
)} )}
</button> </button>
<button <button
type="button"
onClick={handleDownload} onClick={handleDownload}
className={cn( className={cn(
'flex items-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black', 'flex items-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black',