signature/src/app/dashboard/page.tsx

284 lines
9.5 KiB
TypeScript

'use client'
import { useRef, useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase/client'
import SignatureEditor from '@/components/SignatureEditor'
import SignaturePreview from '@/components/SignaturePreview'
import StyleSelector from '@/components/StyleSelector'
import BannerGenerator from '@/components/BannerGenerator'
import { useSignatures, SavedSignature } from '@/hooks/useSignatures'
import { colorThemes, SignatureStyle } from '@/lib/types'
import { generateSignatureHTML, exportAsImage, copyHTMLToClipboard, downloadHTML } from '@/lib/export'
import '@/styles/dashboard.scss'
export default function DashboardPage() {
const router = useRouter()
const [user, setUser] = useState<{ email: string } | null>(null)
const [copySuccess, setCopySuccess] = useState(false)
const [exportLoading, setExportLoading] = useState(false)
const [activeTab, setActiveTab] = useState<'editor' | 'style' | 'banner'>('editor')
const signatureRef = useRef<HTMLDivElement>(null)
const {
signatures,
currentSignature,
currentId,
loading,
saving,
updateSignature,
createNewSignature,
selectSignature,
deleteSignature,
} = useSignatures()
useEffect(() => {
const supabase = createClient()
supabase.auth.getUser().then(({ data }) => {
if (data.user) {
setUser({ email: data.user.email || '' })
} else {
router.push('/login')
}
})
}, [router])
const handleLogout = async () => {
const supabase = createClient()
await supabase.auth.signOut()
router.push('/login')
router.refresh()
}
const handleNewSignature = async () => {
await createNewSignature()
setActiveTab('editor')
}
const handleStyleChange = (style: SignatureStyle) => {
updateSignature({ ...currentSignature, styleTemplate: style })
}
const handleThemeChange = (themeIndex: number) => {
const theme = colorThemes[themeIndex]
updateSignature({
...currentSignature,
primaryColor: theme.primary,
secondaryColor: theme.secondary,
accentColor: theme.accent,
})
}
const handleBannerChange = (url: string) => {
updateSignature({ ...currentSignature, bannerUrl: url })
}
const handleBannerLinkChange = (link: string) => {
updateSignature({ ...currentSignature, bannerLink: link })
}
const handleCopyHTML = async () => {
const html = generateSignatureHTML(currentSignature)
try {
await copyHTMLToClipboard(html)
setCopySuccess(true)
setTimeout(() => setCopySuccess(false), 2000)
} catch (error) {
console.error('Error copying HTML:', error)
}
}
const handleDownloadHTML = () => {
const html = generateSignatureHTML(currentSignature)
const filename = `signature-${currentSignature.firstName || 'email'}-${currentSignature.lastName || ''}.html`
downloadHTML(html, filename)
}
const handleExportImage = async () => {
if (!signatureRef.current) return
setExportLoading(true)
try {
await exportAsImage(signatureRef.current)
} catch (error) {
console.error('Error exporting image:', error)
} finally {
setExportLoading(false)
}
}
const handleDeleteSignature = async (sig: SavedSignature, e: React.MouseEvent) => {
e.stopPropagation()
if (confirm(`Supprimer la signature de ${sig.firstName} ${sig.lastName} ?`)) {
await deleteSignature(sig.id)
}
}
if (loading) {
return (
<div className="dashboard">
<div className="loading-screen">
<div className="loading-screen__spinner" />
<p>Chargement...</p>
</div>
</div>
)
}
return (
<div className="dashboard">
{/* Header */}
<header className="header">
<div className="header__inner">
<div className="header__brand">Navier Signatures</div>
<div className="header__actions">
{saving && <span className="auto-save">Sauvegarde...</span>}
{user && <span className="header__email">{user.email}</span>}
<button className="btn btn--ghost" onClick={handleLogout}>
Déconnexion
</button>
</div>
</div>
</header>
<div className="app-layout">
{/* Sidebar */}
<aside className="sidebar">
<div className="sidebar__top">
<h2>Signatures</h2>
<button className="btn btn--primary btn--icon" onClick={handleNewSignature} title="Nouvelle signature">
+
</button>
</div>
<div className="signature-list">
{signatures.length === 0 ? (
<div className="signature-list__empty">
<p>Aucune signature</p>
<button className="btn btn--primary" onClick={handleNewSignature}>
Créer une signature
</button>
</div>
) : (
signatures.map((sig) => (
<div
key={sig.id}
className={`signature-card ${currentId === sig.id ? 'signature-card--active' : ''}`}
onClick={() => selectSignature(sig)}
>
<div className="signature-card__avatar">
{sig.firstName.charAt(0)}{sig.lastName.charAt(0)}
</div>
<div className="signature-card__info">
<span className="signature-card__name">{sig.firstName} {sig.lastName}</span>
<span className="signature-card__role">{sig.jobTitle || 'Sans titre'}</span>
</div>
<button
className="signature-card__delete"
onClick={(e) => handleDeleteSignature(sig, e)}
title="Supprimer"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2" />
</svg>
</button>
</div>
))
)}
</div>
</aside>
{/* Main content */}
<main className="main-content">
{/* Tabs */}
<nav className="tabs">
<button
className={`tabs__item ${activeTab === 'editor' ? 'tabs__item--active' : ''}`}
onClick={() => setActiveTab('editor')}
>
Informations
</button>
<button
className={`tabs__item ${activeTab === 'style' ? 'tabs__item--active' : ''}`}
onClick={() => setActiveTab('style')}
>
Style
</button>
<button
className={`tabs__item ${activeTab === 'banner' ? 'tabs__item--active' : ''}`}
onClick={() => setActiveTab('banner')}
>
Bannière
</button>
</nav>
<div className="content-grid">
{/* Editor Panel */}
<section className="panel">
{activeTab === 'editor' && (
<SignatureEditor data={currentSignature} onChange={updateSignature} />
)}
{activeTab === 'style' && (
<StyleSelector
currentStyle={currentSignature.styleTemplate}
currentThemeIndex={colorThemes.findIndex(t => t.primary === currentSignature.primaryColor)}
onStyleChange={handleStyleChange}
onThemeChange={handleThemeChange}
photoShape={currentSignature.photoShape}
onPhotoShapeChange={(shape) => updateSignature({ ...currentSignature, photoShape: shape })}
/>
)}
{activeTab === 'banner' && (
<BannerGenerator
bannerUrl={currentSignature.bannerUrl || ''}
bannerLink={currentSignature.bannerLink || ''}
onBannerChange={handleBannerChange}
onLinkChange={handleBannerLinkChange}
/>
)}
</section>
{/* Preview Panel */}
<section className="panel panel--preview">
<h3 className="panel__title">Aperçu</h3>
<div className="preview-box">
<SignaturePreview
ref={signatureRef}
data={currentSignature}
bannerUrl={currentSignature.bannerUrl}
bannerLink={currentSignature.bannerLink}
/>
</div>
<div className="export-actions">
<button
className={`btn ${copySuccess ? 'btn--success' : 'btn--primary'}`}
onClick={handleCopyHTML}
>
{copySuccess ? 'Copié !' : 'Copier HTML'}
</button>
<button className="btn btn--secondary" onClick={handleDownloadHTML}>
Télécharger
</button>
<button
className="btn btn--secondary"
onClick={handleExportImage}
disabled={exportLoading}
>
{exportLoading ? '...' : 'PNG'}
</button>
</div>
<p className="preview-hint">
Collez le HTML dans les paramètres de signature de votre client email.
</p>
</section>
</div>
</main>
</div>
</div>
)
}