refactor(symbol-manager): remove template import, focus on library UX
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 12m43s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 12m43s
This commit is contained in:
123
build-log.txt
Normal file
123
build-log.txt
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
▲ Next.js 15.5.12
|
||||||
|
- Environments: .env
|
||||||
|
- Experiments (use with caution):
|
||||||
|
· serverActions
|
||||||
|
|
||||||
|
Creating an optimized production build ...
|
||||||
|
✓ Compiled successfully in 5.8s
|
||||||
|
Skipping validation of types
|
||||||
|
Linting ...
|
||||||
|
Collecting page data ...
|
||||||
|
⚠ Using edge runtime on a page currently disables static generation for that page
|
||||||
|
Generating static pages (0/53) ...
|
||||||
|
Generating static pages (13/53)
|
||||||
|
Generating static pages (26/53)
|
||||||
|
Generating static pages (39/53)
|
||||||
|
✓ Generating static pages (53/53)
|
||||||
|
Finalizing page optimization ...
|
||||||
|
Collecting build traces ...
|
||||||
|
|
||||||
|
Route (app) Size First Load JS
|
||||||
|
┌ ○ / 3.03 kB 361 kB
|
||||||
|
├ ○ /_not-found 1.02 kB 340 kB
|
||||||
|
├ ƒ /[slug] 3.52 kB 348 kB
|
||||||
|
├ ○ /admin 35.3 kB 424 kB
|
||||||
|
├ ƒ /api/admin/categories 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/categories/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/icons 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/icons/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/icons/upload 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/projects 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/settings 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/tenants 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/tenants/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/tenants/[id]/logo 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/tenants/[id]/logo/serve 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/tenants/[id]/members 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/trial-reminders 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/users 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/users/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/admin/users/[id]/reset-password 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/change-password 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/delete-account 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/forgot-password 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/login 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/logout 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/me 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/register 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/resend-verification 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/reset-password 325 B 340 kB
|
||||||
|
├ ƒ /api/auth/verify-email 325 B 340 kB
|
||||||
|
├ ƒ /api/contact 325 B 340 kB
|
||||||
|
├ ƒ /api/demo 325 B 340 kB
|
||||||
|
├ ƒ /api/dictionary 325 B 340 kB
|
||||||
|
├ ƒ /api/dictionary/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/donate/checkout 325 B 340 kB
|
||||||
|
├ ƒ /api/donate/config 325 B 340 kB
|
||||||
|
├ ƒ /api/donate/webhook 325 B 340 kB
|
||||||
|
├ ƒ /api/hose-types 325 B 340 kB
|
||||||
|
├ ƒ /api/hose-types/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/icons 325 B 340 kB
|
||||||
|
├ ƒ /api/icons/[id]/image 325 B 340 kB
|
||||||
|
├ ƒ /api/icons/[id]/toggle-visibility 325 B 340 kB
|
||||||
|
├ ƒ /api/icons/upload 325 B 340 kB
|
||||||
|
├ ƒ /api/projects 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id] 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/editing 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/export 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/features 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/check-items 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/check-items/[itemId] 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/entries 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/entries/[entryId] 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/pendenzen 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/pendenzen/[pendenzId] 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/journal/send-report 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/plan-image 325 B 340 kB
|
||||||
|
├ ƒ /api/projects/[id]/plan-image/serve 325 B 340 kB
|
||||||
|
├ ƒ /api/rapports 325 B 340 kB
|
||||||
|
├ ƒ /api/rapports/[token] 325 B 340 kB
|
||||||
|
├ ƒ /api/rapports/[token]/pdf 325 B 340 kB
|
||||||
|
├ ƒ /api/rapports/[token]/send 325 B 340 kB
|
||||||
|
├ ƒ /api/settings/public 325 B 340 kB
|
||||||
|
├ ƒ /api/templates 325 B 340 kB
|
||||||
|
├ ƒ /api/templates/import 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/categories 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/delete 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/info 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/logo 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/soma-templates 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/symbols 325 B 340 kB
|
||||||
|
├ ƒ /api/tenant/symbols/[id]/image 325 B 340 kB
|
||||||
|
├ ƒ /api/tenants/[tenantId]/suggestions 325 B 340 kB
|
||||||
|
├ ƒ /api/tenants/by-slug/[slug] 325 B 340 kB
|
||||||
|
├ ƒ /api/upgrade-requests 325 B 340 kB
|
||||||
|
├ ƒ /api/upgrade-requests/[id] 325 B 340 kB
|
||||||
|
├ ○ /app 281 kB 879 kB
|
||||||
|
├ ○ /datenschutz 3.18 kB 347 kB
|
||||||
|
├ ○ /demo 8.78 kB 555 kB
|
||||||
|
├ ○ /forgot-password 3.88 kB 361 kB
|
||||||
|
├ ○ /impressum 3.01 kB 353 kB
|
||||||
|
├ ○ /login 5.74 kB 363 kB
|
||||||
|
├ ƒ /opengraph-image 325 B 340 kB
|
||||||
|
├ ƒ /rapport/[token] 4.83 kB 344 kB
|
||||||
|
├ ○ /register 5.48 kB 363 kB
|
||||||
|
├ ○ /reset-password 4.01 kB 362 kB
|
||||||
|
├ ○ /robots.txt 325 B 340 kB
|
||||||
|
├ ○ /settings 4.28 kB 356 kB
|
||||||
|
├ ○ /sitemap.xml 325 B 340 kB
|
||||||
|
├ ○ /spenden 5.26 kB 363 kB
|
||||||
|
└ ○ /spenden/danke 2.54 kB 360 kB
|
||||||
|
+ First Load JS shared by all 339 kB
|
||||||
|
├ chunks/1255-94429a3f41c08b44.js 65.5 kB
|
||||||
|
├ chunks/4bd1b696-100b9d70ed4e49c1.js 54.2 kB
|
||||||
|
├ chunks/ed9f2dc4-1b30afa125168b53.js 217 kB
|
||||||
|
└ other shared chunks (total) 2.21 kB
|
||||||
|
|
||||||
|
|
||||||
|
ƒ Middleware 40.1 kB
|
||||||
|
|
||||||
|
○ (Static) prerendered as static content
|
||||||
|
ƒ (Dynamic) server-rendered on demand
|
||||||
|
|
||||||
1599
deploy-build-and-push-305.log
Normal file
1599
deploy-build-and-push-305.log
Normal file
File diff suppressed because one or more lines are too long
@@ -32,9 +32,7 @@ import {
|
|||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
ImageIcon,
|
ImageIcon,
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
Download,
|
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
Package,
|
|
||||||
Library,
|
Library,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
@@ -63,14 +61,6 @@ interface SymbolGroup {
|
|||||||
symbols: TenantSymbol[]
|
symbols: TenantSymbol[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TemplatePackage {
|
|
||||||
packageId: string
|
|
||||||
packageName: string
|
|
||||||
categoryCount: number
|
|
||||||
symbolCount: number
|
|
||||||
previewSymbols: { name: string; svgPath: string }[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── Component ─── */
|
/* ─── Component ─── */
|
||||||
export function SymbolManager() {
|
export function SymbolManager() {
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
@@ -80,12 +70,11 @@ export function SymbolManager() {
|
|||||||
const [categories, setCategories] = useState<TenantCategory[]>([])
|
const [categories, setCategories] = useState<TenantCategory[]>([])
|
||||||
const [symbolGroups, setSymbolGroups] = useState<SymbolGroup[]>([])
|
const [symbolGroups, setSymbolGroups] = useState<SymbolGroup[]>([])
|
||||||
const [flatSymbols, setFlatSymbols] = useState<TenantSymbol[]>([])
|
const [flatSymbols, setFlatSymbols] = useState<TenantSymbol[]>([])
|
||||||
const [templates, setTemplates] = useState<TemplatePackage[]>([])
|
|
||||||
|
|
||||||
/* -- UI state -- */
|
/* -- UI state -- */
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const [expandedCats, setExpandedCats] = useState<Set<string>>(new Set())
|
const [expandedCats, setExpandedCats] = useState<Set<string>>(new Set())
|
||||||
const [activeTab, setActiveTab] = useState<'symbols' | 'categories' | 'import' | 'library'>('symbols')
|
const [activeTab, setActiveTab] = useState<'symbols' | 'categories' | 'library'>('library')
|
||||||
|
|
||||||
/* -- Symbol editing -- */
|
/* -- Symbol editing -- */
|
||||||
const [editingSymbolId, setEditingSymbolId] = useState<string | null>(null)
|
const [editingSymbolId, setEditingSymbolId] = useState<string | null>(null)
|
||||||
@@ -103,10 +92,6 @@ export function SymbolManager() {
|
|||||||
const [uploading, setUploading] = useState(false)
|
const [uploading, setUploading] = useState(false)
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
/* -- Import dialog -- */
|
|
||||||
const [importOpen, setImportOpen] = useState(false)
|
|
||||||
const [importingPkg, setImportingPkg] = useState<string | null>(null)
|
|
||||||
|
|
||||||
/* -- Library -- */
|
/* -- Library -- */
|
||||||
const [libraryIcons, setLibraryIcons] = useState<any[]>([])
|
const [libraryIcons, setLibraryIcons] = useState<any[]>([])
|
||||||
const [librarySearch, setLibrarySearch] = useState('')
|
const [librarySearch, setLibrarySearch] = useState('')
|
||||||
@@ -117,10 +102,9 @@ export function SymbolManager() {
|
|||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const [catRes, symRes, tplRes] = await Promise.all([
|
const [catRes, symRes] = await Promise.all([
|
||||||
fetch('/api/tenant/categories'),
|
fetch('/api/tenant/categories'),
|
||||||
fetch('/api/tenant/symbols?grouped=true'),
|
fetch('/api/tenant/symbols?grouped=true'),
|
||||||
fetch('/api/templates'),
|
|
||||||
])
|
])
|
||||||
if (catRes.ok) {
|
if (catRes.ok) {
|
||||||
const c = await catRes.json()
|
const c = await catRes.json()
|
||||||
@@ -133,10 +117,6 @@ export function SymbolManager() {
|
|||||||
const all = (s.categories || []).flatMap((c: any) => c.symbols || [])
|
const all = (s.categories || []).flatMap((c: any) => c.symbols || [])
|
||||||
setFlatSymbols(all)
|
setFlatSymbols(all)
|
||||||
}
|
}
|
||||||
if (tplRes.ok) {
|
|
||||||
const t = await tplRes.json()
|
|
||||||
setTemplates(t.packages || [])
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
toast({ title: 'Fehler beim Laden', variant: 'destructive' })
|
toast({ title: 'Fehler beim Laden', variant: 'destructive' })
|
||||||
}
|
}
|
||||||
@@ -281,30 +261,6 @@ export function SymbolManager() {
|
|||||||
toast({ title: `${success} Datei(en) hochgeladen` })
|
toast({ title: `${success} Datei(en) hochgeladen` })
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Import ─── */
|
|
||||||
const importPackage = async (packageId: string) => {
|
|
||||||
setImportingPkg(packageId)
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/templates/import', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ packageId }),
|
|
||||||
})
|
|
||||||
const data = await res.json().catch(() => ({}))
|
|
||||||
if (!res.ok) {
|
|
||||||
toast({ title: data.error || 'Import fehlgeschlagen', variant: 'destructive' })
|
|
||||||
} else {
|
|
||||||
toast({ title: `${data.imported} Symbole importiert` })
|
|
||||||
await fetchData()
|
|
||||||
setImportOpen(false)
|
|
||||||
setActiveTab('symbols')
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
toast({ title: 'Import fehlgeschlagen', variant: 'destructive' })
|
|
||||||
}
|
|
||||||
setImportingPkg(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── Library ─── */
|
/* ─── Library ─── */
|
||||||
const fetchLibrary = useCallback(async () => {
|
const fetchLibrary = useCallback(async () => {
|
||||||
setLibraryLoading(true)
|
setLibraryLoading(true)
|
||||||
@@ -374,7 +330,6 @@ export function SymbolManager() {
|
|||||||
{ key: 'symbols', label: 'Meine Symbole', icon: LayoutGrid },
|
{ key: 'symbols', label: 'Meine Symbole', icon: LayoutGrid },
|
||||||
{ key: 'categories', label: 'Kategorien', icon: FolderOpen },
|
{ key: 'categories', label: 'Kategorien', icon: FolderOpen },
|
||||||
{ key: 'library', label: 'Bibliothek', icon: Library },
|
{ key: 'library', label: 'Bibliothek', icon: Library },
|
||||||
{ key: 'import', label: 'Vorlagen importieren', icon: Download },
|
|
||||||
] as const).map(t => (
|
] as const).map(t => (
|
||||||
<button
|
<button
|
||||||
key={t.key}
|
key={t.key}
|
||||||
@@ -393,9 +348,6 @@ export function SymbolManager() {
|
|||||||
<Button size="sm" variant="outline" onClick={() => setUploadOpen(true)}>
|
<Button size="sm" variant="outline" onClick={() => setUploadOpen(true)}>
|
||||||
<Upload className="w-4 h-4 mr-1.5" /> Upload
|
<Upload className="w-4 h-4 mr-1.5" /> Upload
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="outline" onClick={() => setImportOpen(true)}>
|
|
||||||
<Download className="w-4 h-4 mr-1.5" /> Import
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -461,15 +413,15 @@ export function SymbolManager() {
|
|||||||
<div className="text-center py-12 border rounded-lg">
|
<div className="text-center py-12 border rounded-lg">
|
||||||
<ImageIcon className="w-10 h-10 mx-auto text-muted-foreground/40 mb-3" />
|
<ImageIcon className="w-10 h-10 mx-auto text-muted-foreground/40 mb-3" />
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{search ? 'Keine Symbole gefunden.' : 'Noch keine Symbole vorhanden.'}
|
{search ? 'Keine Symbole gefunden.' : 'Noch keine Symbole. Füge Symbole aus der Bibliothek hinzu oder lade eigene hoch.'}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex justify-center gap-2 mt-3">
|
<div className="flex justify-center gap-2 mt-3">
|
||||||
|
<Button size="sm" variant="outline" onClick={() => setActiveTab('library')}>
|
||||||
|
<Library className="w-4 h-4 mr-1" /> Bibliothek durchsuchen
|
||||||
|
</Button>
|
||||||
<Button size="sm" variant="outline" onClick={() => setUploadOpen(true)}>
|
<Button size="sm" variant="outline" onClick={() => setUploadOpen(true)}>
|
||||||
<Upload className="w-4 h-4 mr-1" /> Hochladen
|
<Upload className="w-4 h-4 mr-1" /> Hochladen
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="outline" onClick={() => setImportOpen(true)}>
|
|
||||||
<Download className="w-4 h-4 mr-1" /> Importieren
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -564,62 +516,6 @@ export function SymbolManager() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ===== TAB: Import ===== */}
|
|
||||||
{activeTab === 'import' && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{templates.length === 0 ? (
|
|
||||||
<div className="text-center py-12 border rounded-lg text-muted-foreground">
|
|
||||||
<Package className="w-10 h-10 mx-auto mb-3 opacity-40" />
|
|
||||||
<p className="text-sm">Keine Vorlagen-Pakete verfügbar.</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
||||||
{templates.map(pkg => (
|
|
||||||
<div key={pkg.packageId} className="border rounded-lg p-4 space-y-3 hover:border-primary/30 transition-colors">
|
|
||||||
<div className="flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<h4 className="font-medium text-sm">{pkg.packageName}</h4>
|
|
||||||
<p className="text-xs text-muted-foreground mt-0.5">
|
|
||||||
{pkg.categoryCount} Kategorien · {pkg.symbolCount} Symbole
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Package className="w-5 h-5 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Preview */}
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{pkg.previewSymbols.slice(0, 4).map((p, i) => (
|
|
||||||
<div key={i} className="w-10 h-10 border rounded flex items-center justify-center bg-muted/30">
|
|
||||||
<img
|
|
||||||
src={`/signaturen/${p.svgPath.replace(/^.*[\\/]/, '')}`}
|
|
||||||
alt={p.name}
|
|
||||||
className="w-8 h-8 object-contain"
|
|
||||||
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className="w-full"
|
|
||||||
onClick={() => importPackage(pkg.packageId)}
|
|
||||||
disabled={importingPkg === pkg.packageId}
|
|
||||||
>
|
|
||||||
{importingPkg === pkg.packageId ? (
|
|
||||||
<Loader2 className="w-4 h-4 animate-spin mr-1.5" />
|
|
||||||
) : (
|
|
||||||
<Download className="w-4 h-4 mr-1.5" />
|
|
||||||
)}
|
|
||||||
{importingPkg === pkg.packageId ? 'Importiere...' : 'Importieren'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ===== TAB: Bibliothek ===== */}
|
{/* ===== TAB: Bibliothek ===== */}
|
||||||
{activeTab === 'library' && (
|
{activeTab === 'library' && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -749,57 +645,6 @@ export function SymbolManager() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{/* ===== IMPORT DIALOG ===== */}
|
|
||||||
<Dialog open={importOpen} onOpenChange={setImportOpen}>
|
|
||||||
<DialogContent className="max-w-lg">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Aus Vorlagen importieren</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Wähle ein Vorlagen-Paket aus, das als Mandanten-Symbole importiert werden soll.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-3 max-h-[60vh] overflow-y-auto pr-1">
|
|
||||||
{templates.map(pkg => (
|
|
||||||
<div key={pkg.packageId} className="flex items-center gap-3 border rounded-lg p-3">
|
|
||||||
<div className="flex -space-x-2">
|
|
||||||
{pkg.previewSymbols.slice(0, 3).map((p, i) => (
|
|
||||||
<div key={i} className="w-9 h-9 border-2 border-background rounded bg-muted/30 flex items-center justify-center">
|
|
||||||
<img
|
|
||||||
src={`/signaturen/${p.svgPath.replace(/^.*[\\/]/, '')}`}
|
|
||||||
alt=""
|
|
||||||
className="w-7 h-7 object-contain"
|
|
||||||
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<p className="text-sm font-medium truncate">{pkg.packageName}</p>
|
|
||||||
<p className="text-xs text-muted-foreground">{pkg.symbolCount} Symbole · {pkg.categoryCount} Kategorien</p>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
onClick={() => importPackage(pkg.packageId)}
|
|
||||||
disabled={importingPkg === pkg.packageId}
|
|
||||||
>
|
|
||||||
{importingPkg === pkg.packageId ? (
|
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Download className="w-4 h-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{templates.length === 0 && (
|
|
||||||
<div className="text-center py-6 text-sm text-muted-foreground">
|
|
||||||
Keine Vorlagen verfügbar.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user