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,
|
||||
ImageIcon,
|
||||
FolderOpen,
|
||||
Download,
|
||||
AlertCircle,
|
||||
Package,
|
||||
Library,
|
||||
} from 'lucide-react'
|
||||
|
||||
@@ -63,14 +61,6 @@ interface SymbolGroup {
|
||||
symbols: TenantSymbol[]
|
||||
}
|
||||
|
||||
interface TemplatePackage {
|
||||
packageId: string
|
||||
packageName: string
|
||||
categoryCount: number
|
||||
symbolCount: number
|
||||
previewSymbols: { name: string; svgPath: string }[]
|
||||
}
|
||||
|
||||
/* ─── Component ─── */
|
||||
export function SymbolManager() {
|
||||
const { toast } = useToast()
|
||||
@@ -80,12 +70,11 @@ export function SymbolManager() {
|
||||
const [categories, setCategories] = useState<TenantCategory[]>([])
|
||||
const [symbolGroups, setSymbolGroups] = useState<SymbolGroup[]>([])
|
||||
const [flatSymbols, setFlatSymbols] = useState<TenantSymbol[]>([])
|
||||
const [templates, setTemplates] = useState<TemplatePackage[]>([])
|
||||
|
||||
/* -- UI state -- */
|
||||
const [search, setSearch] = useState('')
|
||||
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 -- */
|
||||
const [editingSymbolId, setEditingSymbolId] = useState<string | null>(null)
|
||||
@@ -103,10 +92,6 @@ export function SymbolManager() {
|
||||
const [uploading, setUploading] = useState(false)
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
/* -- Import dialog -- */
|
||||
const [importOpen, setImportOpen] = useState(false)
|
||||
const [importingPkg, setImportingPkg] = useState<string | null>(null)
|
||||
|
||||
/* -- Library -- */
|
||||
const [libraryIcons, setLibraryIcons] = useState<any[]>([])
|
||||
const [librarySearch, setLibrarySearch] = useState('')
|
||||
@@ -117,10 +102,9 @@ export function SymbolManager() {
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [catRes, symRes, tplRes] = await Promise.all([
|
||||
const [catRes, symRes] = await Promise.all([
|
||||
fetch('/api/tenant/categories'),
|
||||
fetch('/api/tenant/symbols?grouped=true'),
|
||||
fetch('/api/templates'),
|
||||
])
|
||||
if (catRes.ok) {
|
||||
const c = await catRes.json()
|
||||
@@ -133,10 +117,6 @@ export function SymbolManager() {
|
||||
const all = (s.categories || []).flatMap((c: any) => c.symbols || [])
|
||||
setFlatSymbols(all)
|
||||
}
|
||||
if (tplRes.ok) {
|
||||
const t = await tplRes.json()
|
||||
setTemplates(t.packages || [])
|
||||
}
|
||||
} catch {
|
||||
toast({ title: 'Fehler beim Laden', variant: 'destructive' })
|
||||
}
|
||||
@@ -281,30 +261,6 @@ export function SymbolManager() {
|
||||
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 ─── */
|
||||
const fetchLibrary = useCallback(async () => {
|
||||
setLibraryLoading(true)
|
||||
@@ -374,7 +330,6 @@ export function SymbolManager() {
|
||||
{ key: 'symbols', label: 'Meine Symbole', icon: LayoutGrid },
|
||||
{ key: 'categories', label: 'Kategorien', icon: FolderOpen },
|
||||
{ key: 'library', label: 'Bibliothek', icon: Library },
|
||||
{ key: 'import', label: 'Vorlagen importieren', icon: Download },
|
||||
] as const).map(t => (
|
||||
<button
|
||||
key={t.key}
|
||||
@@ -393,9 +348,6 @@ export function SymbolManager() {
|
||||
<Button size="sm" variant="outline" onClick={() => setUploadOpen(true)}>
|
||||
<Upload className="w-4 h-4 mr-1.5" /> Upload
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => setImportOpen(true)}>
|
||||
<Download className="w-4 h-4 mr-1.5" /> Import
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -461,15 +413,15 @@ export function SymbolManager() {
|
||||
<div className="text-center py-12 border rounded-lg">
|
||||
<ImageIcon className="w-10 h-10 mx-auto text-muted-foreground/40 mb-3" />
|
||||
<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>
|
||||
<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)}>
|
||||
<Upload className="w-4 h-4 mr-1" /> Hochladen
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => setImportOpen(true)}>
|
||||
<Download className="w-4 h-4 mr-1" /> Importieren
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -564,62 +516,6 @@ export function SymbolManager() {
|
||||
</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 ===== */}
|
||||
{activeTab === 'library' && (
|
||||
<div className="space-y-4">
|
||||
@@ -749,57 +645,6 @@ export function SymbolManager() {
|
||||
</DialogContent>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user