mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 22:21:10 +02:00
feat(todo): inline "Neues Board" card as last column in all layouts
Replace the toolbar add-column button with an inline placeholder card that appears as the last column/sheet in the board when in edit mode. Styled with dashed border and + icon, matching each layout's aesthetic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dc4ba0a39c
commit
41ca11eddc
8 changed files with 679 additions and 46 deletions
248
apps/manacore/apps/landing/src/data/ecosystem-health.json
Normal file
248
apps/manacore/apps/landing/src/data/ecosystem-health.json
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
{
|
||||
"generatedAt": "2026-03-31T11:53:48.040Z",
|
||||
"overallScore": 76,
|
||||
"scores": {
|
||||
"sharedPackages": 90,
|
||||
"iconConsistency": 89,
|
||||
"modalConsistency": 24,
|
||||
"errorHandling": 20,
|
||||
"i18nCoverage": 86,
|
||||
"localFirst": 93,
|
||||
"styleConsistency": 87
|
||||
},
|
||||
"weights": {
|
||||
"sharedPackages": 25,
|
||||
"iconConsistency": 15,
|
||||
"modalConsistency": 10,
|
||||
"errorHandling": 10,
|
||||
"i18nCoverage": 15,
|
||||
"localFirst": 10,
|
||||
"styleConsistency": 15
|
||||
},
|
||||
"details": {
|
||||
"icons": {
|
||||
"adoption": 89,
|
||||
"phosphorFiles": 353,
|
||||
"inlineSvgFiles": 45,
|
||||
"perApp": {
|
||||
"calc": {
|
||||
"phosphor": 1,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"calendar": {
|
||||
"phosphor": 34,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"chat": {
|
||||
"phosphor": 18,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"citycorners": {
|
||||
"phosphor": 9,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"clock": {
|
||||
"phosphor": 3,
|
||||
"inlineSvg": 7
|
||||
},
|
||||
"contacts": {
|
||||
"phosphor": 27,
|
||||
"inlineSvg": 4
|
||||
},
|
||||
"context": {
|
||||
"phosphor": 13,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"inventar": {
|
||||
"phosphor": 12,
|
||||
"inlineSvg": 1
|
||||
},
|
||||
"manacore": {
|
||||
"phosphor": 34,
|
||||
"inlineSvg": 25
|
||||
},
|
||||
"manadeck": {
|
||||
"phosphor": 2,
|
||||
"inlineSvg": 1
|
||||
},
|
||||
"manavoxel": {
|
||||
"phosphor": 0,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"matrix": {
|
||||
"phosphor": 26,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"moodlit": {
|
||||
"phosphor": 5,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"mukke": {
|
||||
"phosphor": 21,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"news": {
|
||||
"phosphor": 1,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"nutriphi": {
|
||||
"phosphor": 6,
|
||||
"inlineSvg": 1
|
||||
},
|
||||
"photos": {
|
||||
"phosphor": 11,
|
||||
"inlineSvg": 3
|
||||
},
|
||||
"picture": {
|
||||
"phosphor": 27,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"planta": {
|
||||
"phosphor": 0,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"playground": {
|
||||
"phosphor": 4,
|
||||
"inlineSvg": 1
|
||||
},
|
||||
"presi": {
|
||||
"phosphor": 6,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"questions": {
|
||||
"phosphor": 7,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"skilltree": {
|
||||
"phosphor": 12,
|
||||
"inlineSvg": 1
|
||||
},
|
||||
"storage": {
|
||||
"phosphor": 25,
|
||||
"inlineSvg": 1
|
||||
},
|
||||
"times": {
|
||||
"phosphor": 13,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"todo": {
|
||||
"phosphor": 22,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"uload": {
|
||||
"phosphor": 3,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"wisekeep": {
|
||||
"phosphor": 2,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"zitare": {
|
||||
"phosphor": 9,
|
||||
"inlineSvg": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
"adoption": 24,
|
||||
"total": 51,
|
||||
"sharedUsage": 12,
|
||||
"focusTrapUsage": 7
|
||||
},
|
||||
"packages": {
|
||||
"coreAdoption": 90,
|
||||
"totalApps": 29,
|
||||
"perPackage": {
|
||||
"Auth": {
|
||||
"count": 29,
|
||||
"total": 29,
|
||||
"adoption": 100
|
||||
},
|
||||
"UI": {
|
||||
"count": 28,
|
||||
"total": 29,
|
||||
"adoption": 97
|
||||
},
|
||||
"Theme": {
|
||||
"count": 24,
|
||||
"total": 29,
|
||||
"adoption": 83
|
||||
},
|
||||
"Branding": {
|
||||
"count": 28,
|
||||
"total": 29,
|
||||
"adoption": 97
|
||||
},
|
||||
"i18n": {
|
||||
"count": 23,
|
||||
"total": 29,
|
||||
"adoption": 79
|
||||
},
|
||||
"Error Tracking": {
|
||||
"count": 24,
|
||||
"total": 29,
|
||||
"adoption": 83
|
||||
},
|
||||
"Icons": {
|
||||
"count": 25,
|
||||
"total": 29,
|
||||
"adoption": 86
|
||||
},
|
||||
"Local Store": {
|
||||
"count": 27,
|
||||
"total": 29,
|
||||
"adoption": 93
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"adoption": 20,
|
||||
"inline": 193,
|
||||
"shared": 47
|
||||
},
|
||||
"i18n": {
|
||||
"adoption": 86,
|
||||
"withI18n": 25,
|
||||
"without": 4
|
||||
},
|
||||
"localFirst": {
|
||||
"adoption": 93,
|
||||
"count": 27
|
||||
},
|
||||
"styles": {
|
||||
"themeAdoption": 83,
|
||||
"tailwindAdoption": 90
|
||||
}
|
||||
},
|
||||
"apps": [
|
||||
"calc",
|
||||
"calendar",
|
||||
"chat",
|
||||
"citycorners",
|
||||
"clock",
|
||||
"contacts",
|
||||
"context",
|
||||
"inventar",
|
||||
"manacore",
|
||||
"manadeck",
|
||||
"manavoxel",
|
||||
"matrix",
|
||||
"moodlit",
|
||||
"mukke",
|
||||
"news",
|
||||
"nutriphi",
|
||||
"photos",
|
||||
"picture",
|
||||
"planta",
|
||||
"playground",
|
||||
"presi",
|
||||
"questions",
|
||||
"skilltree",
|
||||
"storage",
|
||||
"times",
|
||||
"todo",
|
||||
"uload",
|
||||
"wisekeep",
|
||||
"zitare"
|
||||
]
|
||||
}
|
||||
252
apps/manacore/apps/landing/src/pages/manascore/ecosystem.astro
Normal file
252
apps/manacore/apps/landing/src/pages/manascore/ecosystem.astro
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro';
|
||||
import Navbar from '../../components/navigation/Navbar.astro';
|
||||
import Footer from '../../components/navigation/Footer.astro';
|
||||
import Section from '../../components/content/Section.astro';
|
||||
import Container from '../../components/layout/Container.astro';
|
||||
import HeroSection from '../../components/content/HeroSection.astro';
|
||||
import ecosystemData from '../../data/ecosystem-health.json';
|
||||
|
||||
const { overallScore, scores, weights, details } = ecosystemData;
|
||||
|
||||
function getScoreColor(score: number): string {
|
||||
if (score >= 85) return 'text-emerald-500';
|
||||
if (score >= 70) return 'text-green-500';
|
||||
if (score >= 50) return 'text-yellow-500';
|
||||
if (score >= 30) return 'text-orange-500';
|
||||
return 'text-red-500';
|
||||
}
|
||||
|
||||
function getBarColor(score: number): string {
|
||||
if (score >= 85) return 'bg-emerald-500';
|
||||
if (score >= 70) return 'bg-green-500';
|
||||
if (score >= 50) return 'bg-yellow-500';
|
||||
if (score >= 30) return 'bg-orange-500';
|
||||
return 'bg-red-500';
|
||||
}
|
||||
|
||||
function getBarBg(score: number): string {
|
||||
if (score >= 85) return 'bg-emerald-500/10';
|
||||
if (score >= 70) return 'bg-green-500/10';
|
||||
if (score >= 50) return 'bg-yellow-500/10';
|
||||
if (score >= 30) return 'bg-orange-500/10';
|
||||
return 'bg-red-500/10';
|
||||
}
|
||||
|
||||
const categories = [
|
||||
{
|
||||
key: 'sharedPackages',
|
||||
label: 'Shared Packages',
|
||||
icon: '📦',
|
||||
description: 'Adoption der 6 Core-Packages über alle Apps',
|
||||
},
|
||||
{
|
||||
key: 'iconConsistency',
|
||||
label: 'Icon Consistency',
|
||||
icon: '🎨',
|
||||
description: 'Phosphor Icons vs inline SVGs',
|
||||
},
|
||||
{
|
||||
key: 'modalConsistency',
|
||||
label: 'Modal Consistency',
|
||||
icon: '🪟',
|
||||
description: 'shared-ui Modal vs Custom-Implementierungen',
|
||||
},
|
||||
{
|
||||
key: 'errorHandling',
|
||||
label: 'Error Handling',
|
||||
icon: '⚠️',
|
||||
description: 'Shared Helper vs inline instanceof Error',
|
||||
},
|
||||
{
|
||||
key: 'i18nCoverage',
|
||||
label: 'i18n Coverage',
|
||||
icon: '🌍',
|
||||
description: 'Apps mit Internationalisierung',
|
||||
},
|
||||
{
|
||||
key: 'localFirst',
|
||||
label: 'Local-First',
|
||||
icon: '💾',
|
||||
description: 'Apps mit Offline-fähigem Local Store',
|
||||
},
|
||||
{
|
||||
key: 'styleConsistency',
|
||||
label: 'Style Consistency',
|
||||
icon: '🎭',
|
||||
description: 'Theme-Variablen + Tailwind CSS Nutzung',
|
||||
},
|
||||
];
|
||||
|
||||
const packageDetails = details.packages.perPackage;
|
||||
const generatedDate = new Date(ecosystemData.generatedAt).toLocaleDateString('de-DE', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
});
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Ecosystem Health - ManaScore"
|
||||
description="Ecosystem-weite Konsistenz- und Qualitätsmetriken für das ManaCore Ökosystem"
|
||||
>
|
||||
<Navbar />
|
||||
|
||||
<HeroSection
|
||||
badge="ManaScore"
|
||||
title="Ecosystem Health"
|
||||
subtitle="Wie konsistent und einheitlich ist das ManaCore Ökosystem? Diese Metriken messen die Adoption gemeinsamer Packages, Patterns und Konventionen über alle Apps hinweg."
|
||||
/>
|
||||
|
||||
<Section>
|
||||
<Container>
|
||||
<div class="mx-auto max-w-4xl">
|
||||
<!-- Overall Score -->
|
||||
<div class="mb-12 text-center">
|
||||
<div class={`text-7xl font-bold ${getScoreColor(overallScore)} mb-2`}>
|
||||
{overallScore}
|
||||
</div>
|
||||
<div class="text-muted-foreground text-lg">Ecosystem Health Score</div>
|
||||
<div class="text-muted-foreground/60 mt-1 text-sm">
|
||||
{details.packages.totalApps} Web Apps analysiert · Stand: {generatedDate}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Score Breakdown -->
|
||||
<div class="mb-12 space-y-4">
|
||||
<h2 class="text-foreground mb-6 text-xl font-semibold">Score Breakdown</h2>
|
||||
|
||||
{
|
||||
categories.map(({ key, label, icon, description }) => {
|
||||
const score = scores[key as keyof typeof scores];
|
||||
const weight = weights[key as keyof typeof weights];
|
||||
return (
|
||||
<div class="border-border/50 rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-lg">{icon}</span>
|
||||
<div>
|
||||
<div class="text-foreground font-medium text-sm">{label}</div>
|
||||
<div class="text-muted-foreground text-xs">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-muted-foreground text-xs">Gewicht: {weight}%</span>
|
||||
<span class={`text-2xl font-bold ${getScoreColor(score)}`}>{score}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class={`h-2 rounded-full ${getBarBg(score)}`}>
|
||||
<div
|
||||
class={`h-2 rounded-full transition-all ${getBarColor(score)}`}
|
||||
style={`width: ${score}%`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Shared Package Details -->
|
||||
<div class="mb-12">
|
||||
<h2 class="text-foreground mb-6 text-xl font-semibold">Shared Package Adoption</h2>
|
||||
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
{
|
||||
Object.entries(packageDetails).map(([name, data]: [string, any]) => (
|
||||
<div class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4 text-center">
|
||||
<div class={`text-2xl font-bold ${getScoreColor(data.adoption)}`}>
|
||||
{data.adoption}%
|
||||
</div>
|
||||
<div class="text-foreground mt-1 text-sm font-medium">{name}</div>
|
||||
<div class="text-muted-foreground text-xs">
|
||||
{data.count}/{data.total} Apps
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Icon Details -->
|
||||
<div class="mb-12">
|
||||
<h2 class="text-foreground mb-6 text-xl font-semibold">Icon Consistency Details</h2>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4"
|
||||
>
|
||||
<div class="text-emerald-500 text-3xl font-bold">{details.icons.phosphorFiles}</div>
|
||||
<div class="text-muted-foreground text-sm">Dateien mit Phosphor Icons</div>
|
||||
</div>
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4"
|
||||
>
|
||||
<div
|
||||
class={`text-3xl font-bold ${details.icons.inlineSvgFiles > 0 ? 'text-orange-500' : 'text-emerald-500'}`}
|
||||
>
|
||||
{details.icons.inlineSvgFiles}
|
||||
</div>
|
||||
<div class="text-muted-foreground text-sm">Dateien mit Inline SVGs</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Details -->
|
||||
<div class="mb-12">
|
||||
<h2 class="text-foreground mb-6 text-xl font-semibold">Modal Consistency</h2>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4 text-center"
|
||||
>
|
||||
<div class="text-foreground text-3xl font-bold">{details.modals.total}</div>
|
||||
<div class="text-muted-foreground text-sm">Modals gesamt</div>
|
||||
</div>
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4 text-center"
|
||||
>
|
||||
<div class="text-emerald-500 text-3xl font-bold">{details.modals.sharedUsage}</div>
|
||||
<div class="text-muted-foreground text-sm">Nutzen shared-ui Modal</div>
|
||||
</div>
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4 text-center"
|
||||
>
|
||||
<div class="text-foreground text-3xl font-bold">{details.modals.focusTrapUsage}</div>
|
||||
<div class="text-muted-foreground text-sm">Mit focusTrap (A11y)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Handling -->
|
||||
<div class="mb-12">
|
||||
<h2 class="text-foreground mb-6 text-xl font-semibold">Error Handling Patterns</h2>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4"
|
||||
>
|
||||
<div class="text-emerald-500 text-3xl font-bold">{details.errors.shared}</div>
|
||||
<div class="text-muted-foreground text-sm">Shared Helper Nutzungen</div>
|
||||
</div>
|
||||
<div
|
||||
class="border-border/50 rounded-lg border bg-gradient-to-br from-white/5 to-white/[0.02] p-4"
|
||||
>
|
||||
<div
|
||||
class={`text-3xl font-bold ${details.errors.inline > 20 ? 'text-orange-500' : 'text-yellow-500'}`}
|
||||
>
|
||||
{details.errors.inline}
|
||||
</div>
|
||||
<div class="text-muted-foreground text-sm">Inline instanceof Error</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Back link -->
|
||||
<div class="text-center">
|
||||
<a href="/manascore" class="text-primary hover:text-primary/80 text-sm transition-colors">
|
||||
← Zurück zur ManaScore Übersicht
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</Section>
|
||||
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
|
@ -6,6 +6,7 @@ import Section from '../../components/content/Section.astro';
|
|||
import Container from '../../components/layout/Container.astro';
|
||||
import HeroSection from '../../components/content/HeroSection.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
import ecosystemData from '../../data/ecosystem-health.json';
|
||||
|
||||
const scores = await getCollection('manascore');
|
||||
// Default: sort by score descending
|
||||
|
|
@ -53,6 +54,31 @@ const statuses = [...new Set(sortedAudits.map((a) => a.data.status))];
|
|||
|
||||
<Section>
|
||||
<Container>
|
||||
{/* Ecosystem Health Link */}
|
||||
<div class="mx-auto mb-8 max-w-4xl">
|
||||
<a
|
||||
href="/manascore/ecosystem"
|
||||
class="border-border/50 hover:border-primary/50 group flex items-center justify-between rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-5 transition-all hover:shadow-lg"
|
||||
>
|
||||
<div>
|
||||
<div class="text-foreground flex items-center gap-2 font-semibold">
|
||||
<span class="text-lg">🌐</span>
|
||||
Ecosystem Health
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-1 text-sm">
|
||||
Konsistenz- und Vereinheitlichungsmetriken über alle {ecosystemData.apps.length} Apps
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class={`text-3xl font-bold ${getScoreColor(ecosystemData.overallScore)}`}>
|
||||
{ecosystemData.overallScore}
|
||||
</span>
|
||||
<span class="text-muted-foreground transition-transform group-hover:translate-x-1"
|
||||
>→</span
|
||||
>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{/* Filter bar */}
|
||||
<div class="mx-auto mb-6 flex max-w-4xl flex-wrap items-center gap-3">
|
||||
{/* Status filter */}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
onColumnColorChange?: (colIdx: number, color: string) => void;
|
||||
onColumnMove?: (colIdx: number, dir: -1 | 1) => void;
|
||||
onColumnDelete?: (colIdx: number) => void;
|
||||
onAddColumn?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
onColumnColorChange,
|
||||
onColumnMove,
|
||||
onColumnDelete,
|
||||
onAddColumn,
|
||||
}: Props = $props();
|
||||
|
||||
let activeLayout = $derived(layoutOverride || view.layout);
|
||||
|
|
@ -89,6 +91,7 @@
|
|||
{onColumnColorChange}
|
||||
{onColumnMove}
|
||||
{onColumnDelete}
|
||||
{onAddColumn}
|
||||
/>
|
||||
{:else if activeLayout === 'grid'}
|
||||
<GridLayout
|
||||
|
|
@ -101,6 +104,7 @@
|
|||
{onColumnColorChange}
|
||||
{onColumnMove}
|
||||
{onColumnDelete}
|
||||
{onAddColumn}
|
||||
/>
|
||||
{:else}
|
||||
<KanbanLayout
|
||||
|
|
@ -113,5 +117,6 @@
|
|||
{onColumnColorChange}
|
||||
{onColumnMove}
|
||||
{onColumnDelete}
|
||||
{onAddColumn}
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
onColumnColorChange?: (colIdx: number, color: string) => void;
|
||||
onColumnMove?: (colIdx: number, dir: -1 | 1) => void;
|
||||
onColumnDelete?: (colIdx: number) => void;
|
||||
onAddColumn?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -30,6 +31,7 @@
|
|||
onColumnColorChange,
|
||||
onColumnMove,
|
||||
onColumnDelete,
|
||||
onAddColumn,
|
||||
}: Props = $props();
|
||||
|
||||
const PAGE_WIDTH_MAP: Record<string, string> = {
|
||||
|
|
@ -164,6 +166,15 @@
|
|||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if onAddColumn}
|
||||
<div class="fokus-sheet add-sheet">
|
||||
<button class="add-sheet-btn" onclick={onAddColumn}>
|
||||
<span class="add-sheet-icon">+</span>
|
||||
<span class="add-sheet-label">Neues Board</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Page dots -->
|
||||
|
|
@ -261,6 +272,46 @@
|
|||
background: rgba(139, 92, 246, 0.04);
|
||||
}
|
||||
|
||||
/* Add sheet */
|
||||
.add-sheet {
|
||||
border: 2px dashed rgba(139, 92, 246, 0.3) !important;
|
||||
background: rgba(139, 92, 246, 0.02) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.add-sheet:hover {
|
||||
border-color: #8b5cf6 !important;
|
||||
background: rgba(139, 92, 246, 0.06) !important;
|
||||
}
|
||||
:global(.dark) .add-sheet {
|
||||
border-color: rgba(139, 92, 246, 0.25) !important;
|
||||
background: rgba(139, 92, 246, 0.04) !important;
|
||||
}
|
||||
|
||||
.add-sheet-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.add-sheet-icon {
|
||||
font-size: 2rem;
|
||||
font-weight: 300;
|
||||
color: #8b5cf6;
|
||||
line-height: 1;
|
||||
}
|
||||
.add-sheet-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
/* Page dots */
|
||||
.page-dots {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
onColumnColorChange?: (colIdx: number, color: string) => void;
|
||||
onColumnMove?: (colIdx: number, dir: -1 | 1) => void;
|
||||
onColumnDelete?: (colIdx: number) => void;
|
||||
onAddColumn?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
onColumnColorChange,
|
||||
onColumnMove,
|
||||
onColumnDelete,
|
||||
onAddColumn,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
|
|
@ -46,6 +48,15 @@
|
|||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if onAddColumn}
|
||||
<div class="grid-cell">
|
||||
<button class="add-column-card" onclick={onAddColumn}>
|
||||
<span class="add-icon">+</span>
|
||||
<span class="add-label">Neues Board</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
@ -89,4 +100,40 @@
|
|||
height: 100%;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.add-column-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
border: 2px dashed rgba(139, 92, 246, 0.3);
|
||||
border-radius: 0.375rem;
|
||||
background: rgba(139, 92, 246, 0.03);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.add-column-card:hover {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.08);
|
||||
}
|
||||
:global(.dark) .add-column-card {
|
||||
background: rgba(139, 92, 246, 0.05);
|
||||
}
|
||||
:global(.dark) .add-column-card:hover {
|
||||
background: rgba(139, 92, 246, 0.12);
|
||||
}
|
||||
.add-icon {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
color: #8b5cf6;
|
||||
}
|
||||
.add-label {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
color: #8b5cf6;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
onColumnColorChange?: (colIdx: number, color: string) => void;
|
||||
onColumnMove?: (colIdx: number, dir: -1 | 1) => void;
|
||||
onColumnDelete?: (colIdx: number) => void;
|
||||
onAddColumn?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
onColumnColorChange,
|
||||
onColumnMove,
|
||||
onColumnDelete,
|
||||
onAddColumn,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
|
|
@ -46,6 +48,15 @@
|
|||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if onAddColumn}
|
||||
<div class="kanban-column-wrapper">
|
||||
<button class="add-column-card" onclick={onAddColumn}>
|
||||
<span class="add-column-icon">+</span>
|
||||
<span class="add-column-label">Neues Board</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
@ -94,4 +105,42 @@
|
|||
max-width: 340px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.add-column-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
min-height: 250px;
|
||||
border: 2px dashed rgba(139, 92, 246, 0.3);
|
||||
border-radius: 0.375rem;
|
||||
background: rgba(139, 92, 246, 0.03);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.add-column-card:hover {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.08);
|
||||
}
|
||||
:global(.dark) .add-column-card {
|
||||
background: rgba(139, 92, 246, 0.05);
|
||||
border-color: rgba(139, 92, 246, 0.25);
|
||||
}
|
||||
:global(.dark) .add-column-card:hover {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.12);
|
||||
}
|
||||
.add-column-icon {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
color: #8b5cf6;
|
||||
line-height: 1;
|
||||
}
|
||||
.add-column-label {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
color: #8b5cf6;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import { BoardViewRenderer } from '$lib/components/board-views';
|
||||
import { todoSettings, type PageWidth } from '$lib/stores/settings.svelte';
|
||||
import { boardViewsStore } from '$lib/stores/board-views.svelte';
|
||||
import { Plus } from '@manacore/shared-icons';
|
||||
|
||||
// Get active view + edit mode from layout context
|
||||
const activeViewCtx: { readonly value: LocalBoardView | null } = getContext('activeView');
|
||||
|
|
@ -147,17 +146,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if columnsEditable}
|
||||
<div class="add-col-bar">
|
||||
<button class="col-add-btn" onclick={addColumn} title="Spalte hinzufügen">
|
||||
<Plus size={14} />
|
||||
<span>Spalte</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="columns-hint">Spalten werden automatisch aus der Gruppierung erzeugt.</p>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<!-- Board Content -->
|
||||
|
|
@ -169,6 +157,7 @@
|
|||
onColumnColorChange={columnsEditable ? (i, color) => updateColumn(i, { color }) : undefined}
|
||||
onColumnMove={columnsEditable ? moveColumn : undefined}
|
||||
onColumnDelete={columnsEditable ? removeColumn : undefined}
|
||||
onAddColumn={columnsEditable && editMode ? addColumn : undefined}
|
||||
/>
|
||||
{:else}
|
||||
<div class="empty-state">
|
||||
|
|
@ -279,38 +268,4 @@
|
|||
border-color: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ── Add Column Bar ───────────────────────────────────── */
|
||||
.add-col-bar {
|
||||
display: flex;
|
||||
padding: 0.5rem 1.5rem;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid rgba(139, 92, 246, 0.08);
|
||||
}
|
||||
|
||||
.col-add-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
border-radius: 9999px;
|
||||
border: 1px dashed rgba(139, 92, 246, 0.4);
|
||||
color: #8b5cf6;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.col-add-btn:hover {
|
||||
border-color: #8b5cf6;
|
||||
background: rgba(139, 92, 246, 0.08);
|
||||
}
|
||||
|
||||
.columns-hint {
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: #9ca3af;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue