# @ocelot-social/ui - Projektdokumentation > Dieses Dokument dient als zentrale Planungs- und Statusübersicht für das UI-Library Subprojekt. > Es ermöglicht das Pausieren und Wiederaufnehmen der Arbeit zu jedem Zeitpunkt. --- ## Inhaltsverzeichnis ### Schnellzugriff (Status) | Abschnitt | Beschreibung | |-----------|--------------| | [Fortschritt](#fortschritt) | Visuelle Fortschrittsanzeige | | [Aktueller Stand](#aktueller-stand) | Was zuletzt erledigt wurde | | [Meilensteine](#meilensteine) | Phasen 0-5 mit Checklisten | ### Nach Thema **[VISION](#vision)** | # | Abschnitt | |---|-----------| | 1 | [Projektziel & Vision](#1-projektziel--vision) | **[TECHNISCHE GRUNDLAGEN](#technische-grundlagen)** | # | Abschnitt | |---|-----------| | 2 | [Tech-Stack](#2-tech-stack) | | 3 | [Build & Distribution](#3-build--distribution) | | 4 | [Icon-Architektur](#4-icon-architektur) | | 5 | [Design-Token & Branding](#5-design-token--branding) | **[PROZESSE & QUALITÄT](#prozesse--qualität)** | # | Abschnitt | |---|-----------| | 6 | [CI/CD & Release](#6-cicd--release) | | 7 | [Dokumentation & DX](#7-dokumentation--dx) | | 8 | [Migrationsstrategie](#8-migrationsstrategie) | | 9 | [Dokumentationsstrategie](#9-dokumentationsstrategie) | | 10 | [Kompatibilitätstests](#10-kompatibilitätstests) | **[REFERENZ & HISTORIE](#referenz--historie)** | # | Abschnitt | |---|-----------| | 11 | [Entscheidungen](#11-entscheidungen) | | 12 | [Arbeitsprotokoll](#12-arbeitsprotokoll) | | 13 | [Komponenten-Katalog](#13-komponenten-katalog) | | 14 | [Ressourcen & Links](#14-ressourcen--links) | | 15 | [Dokumentationsstrategie (Details)](#15-dokumentationsstrategie-details) | **[ABGRENZUNGEN](#abgrenzungen)** | # | Abschnitt | |---|-----------| | 16 | [Library vs. Webapp](#16-library-vs-webapp) | | 16a | [Webapp ↔ Maintenance Code-Sharing](#16a-webapp--maintenance-code-sharing) | | 16b | [Daten-Entkopplung (ViewModel/Mapper)](#16b-daten-entkopplung-viewmodelmapper-pattern) | | 17 | [Externe Abhängigkeiten](#17-externe-abhängigkeiten) | | 18 | [Kompatibilitätstests (Details)](#18-kompatibilitätstests-details) | | 19 | [Komplexitätsanalyse](#19-komplexitätsanalyse) | --- ### Wie dieses Dokument verwendet wird **Zum Fortsetzen der Arbeit:** > "Lass uns am @ocelot-social/ui Projekt weiterarbeiten" (packages/ui) **Nach jeder Session aktualisieren:** - "Fortschritt" – Balkendiagramme aktualisieren - "Aktueller Stand" – Zuletzt erledigte Aufgaben - "Meilensteine" – Checklisten abhaken - §12 "Arbeitsprotokoll" – Neue Einträge hinzufügen - `KATALOG.md` – Komponenten-Status pflegen --- ## Fortschritt ### Gesamtprojekt ``` Phase 0: ██████████ 100% (6/6 Aufgaben) ✅ Phase 1: ██████████ 100% (6/6 Aufgaben) ✅ Phase 2: ██████████ 100% (26/26 Aufgaben) ✅ Phase 3: ██████████ 100% (24/24 Aufgaben) ✅ - Webapp-Integration komplett Phase 4: ██░░░░░░░░ 24% (4/17 Aufgaben) - OsButton ✅, OsIcon ✅, System-Icons ✅, BaseIcon→OsIcon Migration ✅ Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben) ─────────────────────────────────────── Gesamt: ████████░░ 77% (66/86 Aufgaben) ``` ### Katalogisierung (Details in KATALOG.md) ``` Webapp: ██████████ 100% (139 Komponenten erfasst) Styleguide: ██████████ 100% (38 Komponenten erfasst) Analyse: ██████████ 100% (Button, Modal, Menu detailiert) ``` ### OsButton Migration (Phase 3) ✅ ``` Scope gesamt: 133 Tags in 79 Webapp-Dateien ├─ Migriert: 133 Buttons (100%) ✅ ├─ : 0 verbleibend in Templates ├─ : 0 verbleibend in Templates └─ Cleanup: Snapshots/Tests müssen aktualisiert werden OsButton Features: ├─ variant: ✅ primary, secondary, danger, warning, success, info, default ├─ appearance: ✅ filled, outline, ghost ├─ size: ✅ sm, md, lg, xl ├─ disabled: ✅ mit hover/active-Override (nur as="button") ├─ icon: ✅ slot-basiert (icon-system-agnostisch) ├─ circle: ✅ rounded-full, größenabhängig (p-1.5 bis p-3) ├─ loading: ✅ animated SVG spinner, aria-busy (Milestone 4b) └─ as: ✅ polymorphes Rendering (button/a/NuxtLink/RouterLink) as-Prop Migration: 15 /-Wrapper in 15 Webapp-Dateien → as="nuxt-link"/as="a" ``` ### OsIcon (Phase 4) ``` OsIcon Features: ├─ name: ✅ System-Icon per Name (check, close, plus) ├─ icon: ✅ Custom Vue-Komponente (hat Vorrang vor name) ├─ size: ✅ xs, sm, md, lg, xl, 2xl (em-basiert) ├─ a11y: ✅ decorative (default) / semantic (mit aria-label) ├─ color: ✅ fill-current (erbt von Parent) └─ svg-plugin: ✅ vite-svg-icon (SVG → Vue Component via ?icon) System-Icons: ├─ check.svg (Checkmark) ├─ close.svg (Close/X) └─ plus.svg (Plus/Add) Ocelot-Icons (separates Entry-Point): └─ 82 Icons (Feature-Icons + Kategorie-Icons aus Webapp migriert) BaseIcon → OsIcon Webapp-Migration: ✅ ├─ 131 in 70+ Dateien → ├─ 82 SVGs in ocelot/icons/svgs/ (inkl. 17 Kategorie-Icons) ├─ vite-svg-icon Plugin erweitert (rect, circle, polygon, polyline, ellipse, line) ├─ Kategorie-Icons: DB-String → toCamelCase() → ocelotIcons Lookup ├─ Jest Mocks: @ocelot-social/ui/ocelot für ocelotIcons in Tests ├─ Tests aktualisiert: 911/939 Tests bestanden (3 pre-existing failures) └─ 0 base-icon/BaseIcon Referenzen verbleibend ``` --- ## Aktueller Stand **Letzte Aktualisierung:** 2026-02-15 (Session 22) **Aktuelle Phase:** Phase 4 - OsIcon ✅, BaseIcon → OsIcon Webapp-Migration ✅ **Zuletzt abgeschlossen (Session 22 - BaseIcon → OsIcon Webapp-Migration):** - [x] 131 `` Nutzungen in 70+ Dateien → `` migriert - [x] 82 Ocelot-Icons in `packages/ui/src/ocelot/icons/svgs/` (von 1 auf 82) - [x] 17 Kategorie-Icons aus Webapp kopiert (networking, energy, psyche, movement, finance, child, mobility, shopping-cart, peace, politics, nature, science, health, media, spirituality, culture, miscellaneous) - [x] vite-svg-icon Plugin erweitert: unterstützt ``, ``, ``, ``, ``, `` (war path-only) - [x] Alle neuen SVGs auf Single-Line minifiziert (Multiline brach JS-String-Literale) - [x] Kategorie-Icons: DB-String → `toCamelCase()` → `ocelotIcons[key]` Lookup (Category/index.vue, CategoriesFilter.vue, CategoriesSelect.vue, admin/categories.vue) - [x] `created() { this.icons = ocelotIcons }` Pattern in allen Komponenten (non-reactive) - [x] MenuLegend.vue: `legendItems` von `data()` → `computed` (data() läuft vor created(), this.icons undefined) - [x] HeaderMenu.vue: Map-Button Icon-Größe korrigiert (`size="xl"` + negative Margin) - [x] ShowPassword.vue: `:data-test="iconName"` entfernt (Icon ist jetzt Render-Function, kein String) - [x] Jest-Tests aktualisiert: OsIcon + ocelotIcons statt BaseIcon + String-Namen - Category/index.spec.js, ProfileAvatar.spec.js, CounterIcon.spec.js, ReportRow.spec.js - ActionButton.spec.js, ComponentSlider.spec.js, ShowPassword.spec.js, LoginForm.spec.js - [x] 8 stale Snapshot-Dateien gelöscht - [x] Jest Mock: `test/__mocks__/@ocelot-social/ui/ocelot.js` für ocelotIcons in Tests - [x] CSS: `.base-icon` → `.os-icon` in main.scss und Category/index.vue - [x] 0 `base-icon`/`BaseIcon` Referenzen verbleibend in gesamter Webapp - [x] 911/939 Tests bestanden (3 pre-existing Jest worker crashes) **Zuvor abgeschlossen (Session 21 - OsIcon Komponente, System-Icons, Ocelot-Umbenennung):** - [x] OsIcon Komponente implementiert (name, icon, size Props; Vue 2/3 via vue-demi h()) - [x] System-Icons: check, close, plus (SVG, viewBox 0 0 32 32, stroke-basiert) - [x] Custom vite-svg-icon Plugin: SVG → Vue Render-Function via `?icon` Query - [x] Ocelot-Icons: separates Entry-Point (ocelot.mjs) mit dynamischem Loading via import.meta.glob - [x] `src/webapp/` → `src/ocelot/` umbenannt (konsistentes Naming) - [x] 100% Test-Coverage für OsIcon **Zuvor abgeschlossen (Session 20 - `as`-Prop + nuxt-link Migration):** - [x] OsButton: `as` Prop implementiert (polymorphe Komponente: `button`, `a`, `nuxt-link`, `router-link`, Custom-Komponenten) - [x] Naming-Konvention: `tag` → `as` (moderner Standard: Headless UI, Radix Vue, Chakra UI, PrimeVue) - [x] `disabled`/`type`/`loading` nur bei `as="button"` (Links haben kein natives `disabled`-Attribut) - [x] Stories: `Polymorphic` Story + Playground `as`-Selektor (button/a) - [x] Visual Test: `polymorphic` Screenshot + a11y-Check - [x] 15 ``/``-Wrapper in 15 Webapp-Dateien → `as="nuxt-link"` / `as="a"` migriert: - GroupButton.vue, CtaUnblockAuthor.vue, terms-and-conditions-confirm.vue - CustomButton.vue (v-if/v-else → computed `linkTag`/`linkProps` konsolidiert) - groups/index.vue, GroupForm.vue, admin/users/index.vue - pages/index.vue (CSS `button.post-add-button-*` → `.post-add-button-*`) - profile/_id/_slug.vue (v-if auf ds-grid-item, symmetrisches Padding `$space-x-small`) - groups/_id/_slug.vue, MapButton.vue - ChatNotificationMenu.vue, Chat.vue, UserTeaserPopover.vue - NotificationMenu.vue (3 Instanzen, 2 zu einem Button konsolidiert via counter-icon) - [x] Verifiziert: 0 verbleibende ``/``-Wrapper um `` in Webapp **Zuvor erledigt (auf master gemergt):** - [x] `os-button` CSS-Klasse auf Button-Element für Branding-Kompatibilität (#9211) - [x] eslint-config-it4c v0.11.2 Update: Flat Config, path alias #src, CSS-Linting (#9233) - [x] Release @ocelot-social/ui v0.0.2 - [x] Release v3.14.1 **Abgeschlossene Phasen:** - [x] Phase 0: Analyse (177 Komponenten katalogisiert) - [x] Phase 1: Vue 2.7 Upgrade (2.6.14 → 2.7.16, 979 Tests ✅) - [x] Phase 2: Projekt-Setup (Vite, vue-demi, Tailwind v4, CVA, Storybook 10, CI/CD, 100% Coverage) - [x] Phase 3: Webapp-Integration — 133 os-button in 79 Dateien, 0 base-button/ds-button verbleibend **Zuvor abgeschlossen (Sessions 11-19 — Details im Arbeitsprotokoll §12):** - [x] Session 19: CodeRabbit Review Cleanup, ~30 Bugfixes + A11y-Verbesserungen - [x] Session 18: CodeRabbit Review data-test Selektoren, ~25 A11y aria-labels, OsButton Refactoring - [x] Session 16: Letzte ds-button Migration, Bugfixes, data-variant Attribut - [x] Session 15: Milestone 4c — 59 Buttons migriert, 0 base-button verbleibend - [x] Session 14: Loading Prop, Circle Prop, Spinner-Architektur, Code-Optimierung - [x] Session 13: Icon-Slot, Storybook Playground, 6 Icon-Buttons migriert - [x] Session 12: CSS-Linting, CI-Optimierung, Code-Review Fixes - [x] Session 11: Wasserfarben-Farbschema, Stories konsolidiert, Keyboard A11y **Nächste Schritte:** - [ ] OsSpinner Komponente (vereint DsSpinner + LoadingSpinner) - [ ] OsCard Komponente (vereint DsCard + BaseCard) - [ ] Weitere Tier 1 Komponenten - [ ] Browser-Fehler untersuchen: `TypeError: Cannot read properties of undefined (reading 'heartO')` (ocelotIcons undefined im Browser trotz korrekter Webpack-Aliase) **Manuelle Setup-Aufgaben (außerhalb Code):** - [ ] `NPM_TOKEN` als GitHub Secret einrichten (für npm publish in ui-release.yml) - npm Token erstellen: https://www.npmjs.com/settings/ocelot-social/tokens - GitHub Secret: Repository → Settings → Secrets → Actions → New secret - [ ] Storybook auf externem Host deployen (via Webhook) - Server einrichten für Storybook-Hosting - Webhook-Endpoint erstellen (zieht + baut bei Release) - GitHub Webhook konfigurieren (trigger bei Release) --- ## Meilensteine ### Phase 0: Analyse & Katalogisierung ✅ - [x] Vollständige Katalogisierung Webapp-Komponenten (139 Komponenten) - [x] Vollständige Katalogisierung Styleguide-Komponenten (38 Komponenten) - [x] Duplikate identifizieren und dokumentieren (5 direkte + 3 Familien) - [x] Inkonsistenzen und Probleme erfassen (Button/Modal/Menu analysiert) - [x] Konsolidierungsplan erstellen (Token-Liste) - [x] Priorisierung der zu migrierenden Komponenten (15 Komponenten in 4 Tiers) ### Phase 1: Vue 2.7 Upgrade ✅ - [x] Vue 2.6 → Vue 2.7 Upgrade in Webapp (2.6.14 → 2.7.16) - [x] Abhängigkeiten aktualisieren: - [x] vue-template-compiler entfernt (in Vue 2.7 eingebaut) - [x] @vue/composition-api entfernt (in Vue 2.7 eingebaut) - [x] @nuxtjs/composition-api entfernt (nicht mehr nötig) - [x] vue-server-renderer auf 2.7.16 aktualisiert - [x] Tests durchführen: **157 Suites, 979 passed, 87 Snapshots** ✅ - [x] Regressionstests (`yarn dev` und manuelle Prüfung) ✅ ### Phase 2: Projekt-Setup ✅ - [x] Vite + Vue 3 Projekt initialisieren - [x] vue-demi einrichten für Vue 2 Kompatibilität - [x] Tailwind CSS einrichten (v4 mit @tailwindcss/vite) - [x] Dual-Build konfigurieren (Tailwind Preset + vorkompilierte CSS) - [x] CSS Custom Properties Token-System aufsetzen (requiredCssVariables + validateCssVariables) - [x] Dark Mode Grundstruktur (via Tailwind `dark:` Prefix, dokumentiert) - [x] Storybook für Dokumentation einrichten - [x] Vitest konfigurieren - [x] eslint-config-it4c einrichten (v0.8.0: TypeScript, Vue 3, Vitest, Prettier) - [x] npm Package-Struktur (@ocelot-social/ui) mit korrekten exports - [x] Vue 2/3 Kompatibilitätstests (via Example Apps) - [x] GitHub Workflows einrichten (ui-lint.yml, ui-test.yml, ui-build.yml, ui-compatibility.yml) - [x] Docker Setup (Dockerfile, docker-compose, ui-docker.yml Workflow) - [x] Visual Regression Tests einrichten (Playwright, colocated mit Komponenten) - [x] Accessibility Tests in Visual Tests integriert (@axe-core/playwright) - [x] Keyboard Accessibility Tests (describe('keyboard accessibility')) - [x] ESLint Plugins: vuejs-accessibility, playwright, storybook, jsdoc, @eslint/css - [x] Bundle Size Check einrichten (size-limit, ui-size.yml) - [x] Package-Validierung einrichten (publint, arethetypeswrong) - [x] Example Apps erstellen (vue3-tailwind, vue3-css, vue2-tailwind, vue2-css) - [x] Kompatibilitätstest-Workflow einrichten (4er-Matrix, siehe §18) - [x] release-please Manifest-Konfiguration - [x] npm Publish Workflow (ui-release.yml) - [x] Storybook Build Workflow (ui-storybook.yml) - [x] LICENSE Datei (Apache 2.0) - [x] README.md Grundgerüst (Installation, Quick Start, Struktur) - [x] CONTRIBUTING.md - [x] Completeness Check Script (Story, Visual+checkA11y, Keyboard, Varianten) ### Phase 3: Webapp-Integration (Validierung) **Ziel:** OsButton in der Webapp einbinden, ohne visuelle oder funktionale Änderungen. **Ansatz:** Integration First - Library einbinden, dann schrittweise OsButton ersetzen, beginnend mit einfachsten Stellen. **Milestone 1: Library-Einbindung** ✅ - [x] @ocelot-social/ui in Webapp installieren (yarn link + Webpack-Alias) - [x] vue-demi zur Webapp hinzugefügt (für Vue 2.7 Kompatibilität) - [x] Webpack-Alias für vue-demi (nutzt Webapp's Vue 2.7) - [x] Webpack-Alias für @ocelot-social/ui$ und style.css$ - [x] CSS Custom Properties in Webapp definieren (ocelot-ui-variables.scss) - [x] CSS-Reihenfolge angepasst (UI-Library nach Styleguide) - [x] Import-Pfade testen - [x] Docker Build Stage für UI-Library (Dockerfile + Dockerfile.maintenance) - [x] Relativer Pfad für CI-Kompatibilität (file:../packages/ui) - [x] Jest Mock für @ocelot-social/ui (test/__mocks__/@ocelot-social/ui.js) **Milestone 2: Erste Integration (Minimaler Aufwand)** ✅ - [x] OsButton mit isVue2 Render-Funktion (Vue 2/3 kompatibel) - [x] Button-Variants an ds-button angepasst (font-semibold, rounded, box-shadow) - [x] OsButton in UserTeaserPopover.vue eingesetzt (`variant="primary"`) - [x] Manueller visueller Vergleich ✅ - [x] Webapp-Tests bestehen ✅ (979 Tests, jest moduleNameMapper für vue-demi) **Milestone 3: Schrittweise Erweiterung** ✅ - [x] GroupForm.vue Cancel-Button migriert - [x] OsButton attrs/listeners Forwarding (Vue 2 $listeners via getCurrentInstance) - [x] 14 weitere Buttons migriert (alle ohne icon/circle/loading) **Milestone 4a: Weitere Buttons migrieren (14 ohne neue Props)** ✅ - [x] Modal Cancel-Buttons (DisableModal, DeleteUserModal, ReleaseModal) - [x] Form Cancel/Submit-Buttons (ContributionForm, EnterNonce, MySomethingList) - [x] ImageUploader.vue (2× Crop-Buttons) - [x] Page-Buttons (donations, badges, notifications/index, profile Unblock/Unmute) - [x] ReportRow.vue More-Details-Button **Milestone 4b: OsButton Props erweitern** ✅ - [x] `icon` Slot implementiert (slot-basiert, icon-system-agnostisch) ✅ - [x] `circle` Prop implementiert (rounded-full, größenabhängige Breiten) ✅ - [x] `loading` Prop mit animiertem SVG-Spinner implementiert ✅ **Milestone 4c: Buttons mit icon/circle/loading migrieren** ✅ ABGESCHLOSSEN *Button-Komponenten (Wrapper):* - [x] Button/JoinLeaveButton.vue (icon, loading) ✅ - [x] Button/FollowButton.vue (icon, loading) ✅ - [x] LoginButton/LoginButton.vue (icon, circle) ✅ - [x] InviteButton/InviteButton.vue (icon, circle) ✅ - [x] EmotionButton/EmotionButton.vue (circle) ✅ - [x] CustomButton/CustomButton.vue (2× circle) ✅ - [x] LabeledButton/LabeledButton.vue (icon, circle) ✅ *Navigation & Menus:* - [x] ContentMenu/ContentMenu.vue (icon, circle) ✅ - [x] ContentMenu/GroupContentMenu.vue (icon, circle) ✅ - [x] ChatNotificationMenu.vue (circle) ✅ - [x] NotificationMenu.vue (3× icon, circle) ✅ - [x] HeaderMenu/HeaderMenu.vue (icon, circle) ✅ - [x] Map/MapButton.vue (circle) ✅ *Editor:* - [x] Editor/MenuBar.vue (~11× icon, circle) ✅ - [x] Editor/MenuLegend.vue (2× icon) ✅ *Filter & Input:* - [x] HashtagsFilter.vue (icon, circle) ✅ - [x] CategoriesSelect.vue (icon) ✅ - [x] SearchableInput.vue (icon, circle) ✅ - [x] Select/LocationSelect.vue (icon) ✅ - [x] PaginationButtons.vue (2× icon, circle) ✅ *Chat:* - [x] Chat/Chat.vue (2× icon, circle) ✅ - [x] Chat/AddChatRoomByUserSearch.vue (icon, circle) ✅ *Forms & Auth:* - [x] LoginForm/LoginForm.vue (icon, loading) ✅ - [x] PasswordReset/Request.vue (loading) ✅ - [x] PasswordReset/ChangePassword.vue (loading) ✅ - [x] Password/Change.vue (loading) ✅ - [x] ContributionForm.vue Submit (icon, loading) ✅ - [x] CommentForm/CommentForm.vue (loading) ✅ *Modals:* - [x] Modal/ConfirmModal.vue (2× icon, loading) ✅ - [x] Modal/ReportModal.vue (2× icon, loading) ✅ - [x] Modal/DisableModal.vue Confirm (icon) ✅ - [x] Modal/DeleteUserModal.vue Confirm (icon) ✅ - [x] Modal/ReleaseModal.vue Confirm (icon) ✅ *Features:* - [x] ComponentSlider.vue (2× icon) ✅ - [x] MySomethingList.vue (3× icon, circle) ✅ - [x] CreateInvitation.vue (icon, circle) ✅ - [x] Invitation.vue (2× icon, circle) ✅ - [x] ProfileList.vue (loading) ✅ - [x] ReportRow.vue Confirm (icon) ✅ - [x] ImageUploader.vue Delete/Cancel (2× icon, circle) ✅ - [x] CommentCard.vue Reply (icon, circle) ✅ - [x] EmbedComponent.vue Close (icon, circle) ✅ - [x] CtaUnblockAuthor.vue (icon) ✅ - [x] data-download.vue (icon, loading) ✅ - [x] ActionButton.vue (icon, circle) ✅ - [x] DeleteData.vue (icon) ✅ - [x] GroupButton.vue (icon, circle) ✅ *Filter-Menüs:* - [x] FilterMenu/FilterMenu.vue (icon) ✅ - [x] FilterMenu/HeaderButton.vue (2× icon) ✅ - [x] FilterMenu/CategoriesFilter.vue (2× icon) ✅ - [x] FilterMenu/OrderByFilter.vue (2×) ✅ - [x] FilterMenu/EventsByFilter.vue (2×) ✅ - [x] FilterMenu/FollowingFilter.vue (3×) ✅ *Pages:* - [x] pages/index.vue (2× icon, circle) ✅ - [x] pages/groups/index.vue (icon, circle) ✅ - [x] pages/groups/_id/_slug.vue (3× icon, circle) ✅ - [x] pages/admin/users/index.vue (2× icon, circle) ✅ - [x] pages/settings/index.vue (icon) ✅ - [x] pages/settings/blocked-users.vue (icon, circle) ✅ - [x] pages/settings/muted-users.vue (icon, circle) ✅ - [x] pages/settings/data-download.vue (icon) ✅ - [x] pages/settings/my-email-address/index.vue (icon) ✅ - [x] pages/settings/my-email-address/enter-nonce.vue (icon) ✅ - [x] pages/profile/_id/_slug.vue (icon, circle) ✅ - [x] pages/post/_id/_slug/index.vue (icon, circle) ✅ **Milestone 5: Validierung & Dokumentation** ✅ - [x] Keine visuellen Änderungen bestätigt (16/16 Buttons validiert) - [x] Keine funktionalen Änderungen bestätigt - [x] Disabled-Styles korrigiert (hover/active-Override, Border-Fix) - [ ] Webapp-Tests bestehen weiterhin (TODO: Snapshots aktualisieren) - [ ] Erkenntnisse in KATALOG.md dokumentiert **Einsatzstellen-Übersicht:** | Kategorie | Buttons | Status | |-----------|---------|--------| | ✅ Migriert (gesamt) | 133 | 79 Dateien | | ⬜ `` verbleibend | 0 | Nur BaseButton.vue Definition + Test-Dateien | | ⬜ `` verbleibend | 0 | Alle ersetzt | | **Gesamt** | **133** | **100% erledigt** ✅ | **Details siehe KATALOG.md** (vollständige Tracking-Tabellen) **Erfolgskriterien:** | Kriterium | Prüfung | |-----------|---------| | Visuell identisch | Manueller Screenshot-Vergleich | | Funktional identisch | Click, Disabled funktionieren | | Keine Regression | Webapp Unit-Tests bestehen | **Visuelle Validierung (OsButton vs Original):** Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Active, Disabled. | Datei | Button | Props | Validiert | |-------|--------|-------|-----------| | `components/Group/GroupForm.vue` | Cancel | `default` | ✅ | | `components/Group/GroupMember.vue` | Remove Member | `appearance="outline" variant="primary" size="sm"` | ✅ | | `components/CommentCard/CommentCard.vue` | Show more/less | `appearance="ghost" variant="primary" size="sm"` | ✅ | | `components/UserTeaser/UserTeaserPopover.vue` | Open Profile | `variant="primary"` | ✅ | | `components/DonationInfo/DonationInfo.vue` | Donate Now | `size="sm" variant="primary"` | ✅ | | `components/Map/MapStylesButtons.vue` | Map Styles | `:appearance` dynamisch + custom CSS | ✅ | | `components/Embed/EmbedComponent.vue` | Cancel | `appearance="outline" variant="danger"` + custom CSS | ✅ | | `components/Embed/EmbedComponent.vue` | Play Now | `variant="primary"` + custom CSS | ✅ | | `pages/terms-and-conditions-confirm.vue` | Read T&C | `appearance="outline" variant="primary"` | ✅ | | `pages/terms-and-conditions-confirm.vue` | Save | `variant="primary"` + disabled | ✅ | | `pages/settings/privacy.vue` | Save | `variant="primary"` + disabled | ✅ | | `pages/settings/notifications.vue` | Check All | `appearance="outline" variant="primary"` + disabled | ✅ | | `pages/settings/notifications.vue` | Uncheck All | `appearance="outline" variant="primary"` + disabled | ✅ | | `pages/settings/notifications.vue` | Save | `variant="primary"` + disabled | ✅ | | `pages/settings/embeds.vue` | Allow All | `appearance="outline" variant="primary"` + disabled | ✅ | | `pages/settings/embeds.vue` | Deny All | `variant="primary"` + disabled | ✅ | **Validierung abgeschlossen:** 16/16 (100%) ✅ **Nach Abschluss aller Validierungen:** - [ ] Gesamt-Regressionstest durchführen - [ ] Alle Unit-Tests bestehen - [ ] Dokumentation aktualisieren ### Phase 4: Komponenten-Migration (15 Komponenten + 2 Infrastruktur) **Tier 1: Kern-Komponenten** - [x] OsIcon (vereint DsIcon + BaseIcon) ✅ System-Icons + vite-svg-icon Plugin - [ ] OsSpinner (vereint DsSpinner + LoadingSpinner) - [x] OsButton (vereint DsButton + BaseButton) ✅ Entwickelt in Phase 2 - [ ] OsCard (vereint DsCard + BaseCard) **Tier 2: Layout & Feedback** - [ ] OsModal (Basis: DsModal) - [ ] OsDropdown (Basis: Webapp Dropdown) - [ ] OsAvatar (vereint DsAvatar + ProfileAvatar) - [ ] OsInput (Basis: DsInput) **Tier 3: Navigation & Typography** - [ ] OsMenu (Basis: DsMenu) - [ ] OsMenuItem (Basis: DsMenuItem) - [ ] OsHeading (Basis: DsHeading) - [ ] OsText (Basis: DsText) **Tier 4: Spezial-Komponenten** - [ ] OsSelect - [ ] OsTable - [ ] OsTag **Infrastruktur** - [x] System-Icons einrichten ✅ vite-svg-icon Plugin, 3 System-Icons, Ocelot-Icons Entry-Point - [x] BaseIcon → OsIcon Webapp-Migration ✅ 131 Nutzungen, 82 Ocelot-Icons, 0 BaseIcon verbleibend - [ ] CI docs-check Workflow (JSDoc-Coverage, README-Aktualität) ### Phase 5: Finalisierung - [ ] Alle Komponenten migriert und getestet - [ ] Alte Komponenten aus Vue 2 Projekt entfernt - [ ] Build als npm Library verifiziert - [ ] README.md finalisieren (alle Sektionen vollständig) - [ ] ARCHITECTURE.md erstellen (aus PROJEKT.md §2, §4, §5, §11, §15, §16) - [ ] PROJEKT.md und KATALOG.md archivieren (docs/archive/) - [ ] Dokumentation vollständig und CI-geprüft --- # VISION ## 1. Projektziel & Vision **Kurzbeschreibung:** Neue Vue 3 Komponentenbibliothek aufbauen, die später die Vue 2 Komponenten in der Webapp ersetzen soll. **Hintergrund:** - Bestehendes Projekt nutzt Vue 2.7 mit Nuxt 2 (Upgrade von 2.6 → 2.7 in Phase 1 erledigt ✅) - Existierender `styleguide` Ordner als Git-Submodul (Vue 2, Vue CLI 3) - Design-Token-System mit Theo vorhanden - Branding erfolgt über SCSS-Dateien mit Variablen-Overrides - **Problem:** Viele doppelte Komponenten, inkonsistente Styles, nicht konsequent genutztes Design-System **Vision:** Ein stark definiertes und flexibles Design-System, das den Branding-Anforderungen von ocelot.social gerecht wird und eine saubere, schrittweise Migration von Vue 2 nach Vue 3 ermöglicht. **Geplanter Ansatz:** Migration vorbereiten - schrittweise neue Komponenten in Vue 3 entwickeln, die das bestehende Design-System respektieren und flexible Branding-Optionen bieten. --- # TECHNISCHE GRUNDLAGEN ## 2. Tech-Stack | Komponente | Entscheidung | Notizen | |------------|--------------|---------| | Framework | **Vue 3 + Vite** | Schnellstes Setup, modernes Tooling | | Build-Tool | **Vite** | Schnelles HMR, einfache Konfiguration | | Dokumentation | **Storybook 10** | Komponenten-Dokumentation mit Vue 3 + Vite | | Styling | **Tailwind CSS** | Mit CSS Custom Properties für Branding | | Testing | **Vitest** | Vite-nativ, Jest-kompatible API | | Paket-Name | **@ocelot-social/ui** | Unter ocelot-social npm Org | | Komponenten-Prefix | **Os** | OsButton, OsCard, etc. | | Vue 2 Kompatibilität | **vue-demi** | Library funktioniert in Vue 2 und Vue 3 | | Varianten-System | **CVA** | class-variance-authority für typsichere Prop-Varianten | | Klassen-Merge | **cn()** | clsx + tailwind-merge für Klassen-Kombination | | Linting | **eslint-config-it4c** | Enthält: TypeScript, Vue, Prettier, weitere Regeln | | Release | **release-please** | Automatische Versionen und Changelogs | | Icons | **Hybrid-Architektur** | System-Icons in Library, Feature-Icons in App (siehe §4) | | Browser-Support | **Modern only** | Chrome, Firefox, Safari, Edge (letzte 2 Versionen) | | SSR | **Ja** | Nuxt-kompatibel | | Dark Mode | **Ja, von Anfang an** | Alle Komponenten mit Light/Dark Varianten | | Lizenz | **Apache 2.0** | Permissiv mit Patent-Schutz | | Repository | **Monorepo** | Ocelot-Social/packages/ui/ | ### Konventionen | Aspekt | Entscheidung | Notizen | |--------|--------------|---------| | Vue API | **` ``` ### Checkliste bei neuer Komponente Vor dem Erstellen einer Komponente diese Fragen beantworten: ``` [ ] Wo gehört die Komponente hin? (Entscheidungsbaum durchlaufen) [ ] Falls Library: Sind alle Texte via Props? [ ] Falls Library: Keine direkten Store/Router Imports? [ ] Falls Webapp: Welche Library-Komponenten werden genutzt? [ ] Falls Grenzfall: Kann sie aufgeteilt werden? ``` --- ## 16a. Webapp ↔ Maintenance Code-Sharing ### Problemstellung Die Webapp und Maintenance-App sind aktuell verschachtelt und sollen getrennt werden. Einige Business-Komponenten werden in beiden Apps benötigt, gehören aber nicht in die UI-Library. **Das DX-Problem:** "shared" hat kein logisches Kriterium außer "wird in beiden gebraucht". ### Analysierte Optionen | Option | Beschreibung | Bewertung | |--------|--------------|-----------| | **A: Domain Packages** | `@ocelot-social/auth`, `@ocelot-social/posts`, etc. | Gut bei vielen Komponenten, aber Overhead | | **B: Core + Duplikation** | Composables teilen, Komponenten duplizieren | Gut wenn UI unterschiedlich | | **C: Webapp als Source** | Maintenance importiert aus Webapp | Einfachste Lösung | ### Empfehlung: Option C (Webapp als Source of Truth) ``` ┌─────────────────────────────────────────────────────────────┐ │ @ocelot-social/ui │ │ ───────────────── │ │ • OsButton, OsModal, OsCard, OsInput │ │ • Rein präsentational, keine Abhängigkeiten │ ├─────────────────────────────────────────────────────────────┤ │ webapp/ │ │ ─────── │ │ • Alle Business-Komponenten (Source of Truth) │ │ • Composables in webapp/lib/composables/ │ │ • GraphQL in webapp/graphql/ │ │ • Ist die "Haupt-App" │ ├─────────────────────────────────────────────────────────────┤ │ maintenance/ │ │ ──────────── │ │ • Importiert aus @ocelot-social/ui │ │ • Importiert aus webapp/ via Alias │ │ • Nur maintenance-spezifische Komponenten lokal │ └─────────────────────────────────────────────────────────────┘ ``` ### Umsetzung **maintenance/nuxt.config.js:** ```javascript export default { alias: { '@webapp': '../webapp', '@ocelot-social/ui': '../packages/ui/dist' } } ``` **Import in Maintenance:** ```typescript // UI-Komponenten aus Library import { OsButton, OsModal } from '@ocelot-social/ui' // Business-Komponenten aus Webapp import FollowButton from '@webapp/components/FollowButton.vue' import PostTeaser from '@webapp/components/PostTeaser.vue' // Composables aus Webapp import { useAuth } from '@webapp/lib/composables/useAuth' import { useFollow } from '@webapp/lib/composables/useFollow' ``` ### Kriterien für Entwickler | Frage | Antwort | |-------|---------| | Wo suche ich eine UI-Komponente? | `@ocelot-social/ui` | | Wo suche ich eine Business-Komponente? | `webapp/components/` | | Wo erstelle ich eine neue geteilte Komponente? | `webapp/components/` | | Wo erstelle ich maintenance-spezifische Komponenten? | `maintenance/components/` | ### Vorteile 1. **Klare Regel:** Alles Business-bezogene ist in Webapp 2. **Kein neues Package:** Weniger Overhead 3. **Eine Source of Truth:** Keine Sync-Probleme 4. **Einfache Migration:** Später ggf. Domain-Packages extrahieren ### Spätere Evolution (optional) Wenn klare Patterns entstehen, können Domain-Packages extrahiert werden: ``` Phase 1 (jetzt): Webapp ist Source of Truth Phase 2 (später): Patterns identifizieren Phase 3 (später): @ocelot-social/auth, @ocelot-social/posts, etc. ``` ### Entscheidung | # | Datum | Entscheidung | |---|-------|--------------| | 72 | 2026-02-09 | Webapp als Source of Truth für geteilte Business-Komponenten | --- ## 16b. Daten-Entkopplung (ViewModel/Mapper Pattern) ### Problemstellung Komponenten sind oft direkt an API/GraphQL-Strukturen gekoppelt: ```vue // Komponente kennt GraphQL-Struktur props.user.avatar.url props.user._followedByCurrentUserCount // Underscore?! props.user.__typename // Leaked! ``` **Probleme:** - Schema-Änderung = alle Komponenten anpassen - `__typename`, `_count` etc. leaken in die UI - Schwer testbar (braucht echte GraphQL-Struktur) - Komponenten nicht wiederverwendbar ### Lösung: ViewModel + Mapper Pattern ``` ┌─────────────────────────────────────────────────────────────┐ │ GraphQL / API Layer │ │ • Queries & Mutations │ │ • Generated Types (graphql-codegen) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Mappers (einziger Ort der API-Struktur kennt) │ │ • toUserCardViewModel(graphqlUser) → UserCardViewModel │ │ • toPostTeaserViewModel(graphqlPost) → PostTeaserViewModel │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ViewModels (was die UI braucht) │ │ • UserCardViewModel { displayName, avatarUrl, ... } │ │ • PostTeaserViewModel { title, excerpt, ... } │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Presentational Components (kennen nur ViewModels) │ │ • │ │ • │ └─────────────────────────────────────────────────────────────┘ ``` ### Implementierung **1. ViewModels definieren:** ```typescript // types/viewModels.ts export interface UserCardViewModel { id: string displayName: string avatarUrl: string | null followerCount: number isFollowedByMe: boolean } export interface PostTeaserViewModel { id: string title: string excerpt: string authorName: string authorAvatarUrl: string | null createdAt: Date commentCount: number canEdit: boolean } ``` **2. Mapper-Funktionen:** ```typescript // mappers/userMapper.ts import type { UserCardViewModel } from '~/types/viewModels' import type { UserGraphQL } from '~/graphql/types' export function toUserCardViewModel( user: UserGraphQL, currentUserId?: string ): UserCardViewModel { return { id: user.id, displayName: user.name || user.slug || 'Anonymous', avatarUrl: user.avatar?.url ?? null, followerCount: user._followedByCurrentUserCount ?? 0, isFollowedByMe: user.followedByCurrentUser ?? false, } } ``` **3. Komponenten nutzen nur ViewModels:** ```vue ``` **4. Composables kapseln Mapping:** ```typescript // composables/useUser.ts import { computed } from 'vue' import { useQuery } from '@vue/apollo-composable' import { GET_USER } from '~/graphql/queries' import { toUserCardViewModel } from '~/mappers/userMapper' import type { UserCardViewModel } from '~/types/viewModels' export function useUser(userId: string) { const { result, loading, error } = useQuery(GET_USER, { id: userId }) const user = computed(() => { if (!result.value?.User) return null return toUserCardViewModel(result.value.User) }) return { user, loading, error } } ``` ### Ordnerstruktur ``` webapp/ ├── graphql/ │ ├── queries/ │ ├── mutations/ │ └── types/ # Generated by graphql-codegen ├── types/ │ └── viewModels.ts # UI-spezifische Interfaces ├── mappers/ │ ├── userMapper.ts │ ├── postMapper.ts │ └── index.ts ├── lib/ │ └── composables/ │ ├── useAuth.ts │ ├── useUser.ts # Gibt ViewModel zurück │ └── usePost.ts ├── components/ # Presentational (nur ViewModels) │ ├── UserCard.vue │ └── PostTeaser.vue └── pages/ # Nutzen Composables └── users/[id].vue ``` ### Vorteile | Aspekt | Ohne Mapper | Mit Mapper | |--------|-------------|------------| | **API-Änderung** | Alle Komponenten anpassen | Nur Mapper anpassen | | **Testing** | Mock GraphQL Response | Einfaches ViewModel-Objekt | | **Wiederverwendung** | Komponente an API gebunden | Komponente API-agnostisch | | **TypeScript** | Komplexe/generierte Types | Klare, einfache Interfaces | | **webapp ↔ maintenance** | Verschiedene Strukturen | Gleiche ViewModels | ### Regeln ``` ┌─────────────────────────────────────────────────────────────┐ │ Regel 1: Komponenten definieren was sie BRAUCHEN │ │ (ViewModel), nicht was die API LIEFERT │ ├─────────────────────────────────────────────────────────────┤ │ Regel 2: Mapper sind der EINZIGE Ort der API kennt │ │ API-Änderung = nur Mapper ändern │ ├─────────────────────────────────────────────────────────────┤ │ Regel 3: Composables kapseln Fetching + Mapping │ │ useUser() gibt UserCardViewModel zurück │ ├─────────────────────────────────────────────────────────────┤ │ Regel 4: Presentational Components sind API-agnostisch │ │ Einfach testbar, wiederverwendbar │ └─────────────────────────────────────────────────────────────┘ ``` ### Entscheidung | # | Datum | Entscheidung | |---|-------|--------------| | 73 | 2026-02-09 | ViewModel/Mapper Pattern für Daten-Entkopplung | --- ## 17. Externe Abhängigkeiten ### Übersicht | Abhängigkeit | Status | Beschreibung | |--------------|--------|--------------| | **eslint-config-it4c** | ⚠️ ESLint 10 ausstehend | v0.8.0 eingebunden, ESLint 10 inkompatibel | ### eslint-config-it4c **Status:** Funktional, ESLint 10 Update ausstehend **TODO:** eslint-config-it4c muss für ESLint 10 aktualisiert werden (aktuell inkompatibel wegen @typescript-eslint/utils). **Lösung:** Das Paket wurde in Version 0.8.0 modularisiert und unterstützt jetzt ESLint Flat Config. **Aktuelle Nutzung in @ocelot-social/ui:** ```typescript // eslint.config.ts import config, { vue3, vitest } from 'eslint-config-it4c' export default [ ...config, // Base + TypeScript + Prettier + weitere ...vue3, // Vue 3 Regeln ...vitest, // Vitest Test-Regeln // Projekt-spezifische Overrides... ] ``` **Projekt-spezifische Anpassungen:** - `n/file-extension-in-import`: Ausnahmen für `.vue`, `.css`, `.scss`, `.json` - `import-x/no-unassigned-import`: CSS-Imports erlaubt - `vitest/consistent-test-filename`: Pattern `*.spec.ts` - `vitest/prefer-expect-assertions`: Ausgeschaltet - `vitest/no-hooks`: Ausgeschaltet - Example Apps: Eigene ESLint-Configs (ignoriert in Hauptpaket, gelintet im Compatibility-Workflow) --- ## 18. Kompatibilitätstests (Details) ### Testmatrix Die Library muss in 4 Kombinationen funktionieren: ``` │ Tailwind │ CSS (vorkompiliert) ────────────────────┼───────────────────┼────────────────────── Vue 3.4+ │ ✓ Muss testen │ ✓ Muss testen Vue 2.7 │ ✓ Muss testen │ ✓ Muss testen ``` ### Werkzeuge & Strategien #### 1. vue-demi (Code-Ebene) Abstrahiert Vue 2/3 API-Unterschiede: ```typescript // Komponente importiert von vue-demi, nicht vue import { ref, computed, defineComponent } from 'vue-demi' ``` #### 2. Unit Tests mit Vitest Hauptpaket testet nur mit Vue 3 (Entwicklungsumgebung). Vue 2 Kompatibilität wird via vue-demi gewährleistet und in Example Apps getestet. **Begründung:** Inline Vue 2/3 Matrix verursacht Peer-Dependency-Konflikte. #### 3. Example Apps (4 Kombinationen) - Hauptstrategie für Vue 2 Tests ``` packages/ui/ ├── examples/ │ ├── vue3-tailwind/ # Vite + Vue 3 + Tailwind Preset │ │ ├── package.json │ │ ├── vite.config.ts │ │ ├── tailwind.config.js # Nutzt @ocelot-social/ui/tailwind.preset │ │ └── src/App.vue # Importiert alle Komponenten │ │ │ ├── vue3-css/ # Vite + Vue 3 + style.css │ │ ├── package.json │ │ ├── vite.config.ts │ │ └── src/ │ │ ├── main.ts # import '@ocelot-social/ui/style.css' │ │ └── App.vue │ │ │ ├── vue2-tailwind/ # Vue CLI / Nuxt 2 + Tailwind │ │ └── ... │ │ │ └── vue2-css/ # Vue CLI / Nuxt 2 + style.css │ └── ... ``` **Jede Example App:** - Importiert `@ocelot-social/ui` via Workspace-Link - Rendert alle exportierten Komponenten - Kann lokal gestartet werden (`npm run dev`) - Wird in CI gebaut und getestet #### 4. Playwright E2E Tests ```typescript // e2e/compatibility.spec.ts import { test, expect } from '@playwright/test' const examples = [ { name: 'vue3-tailwind', port: 3001 }, { name: 'vue3-css', port: 3002 }, { name: 'vue2-tailwind', port: 3003 }, { name: 'vue2-css', port: 3004 }, ] for (const example of examples) { test.describe(example.name, () => { test('all components render', async ({ page }) => { await page.goto(`http://localhost:${example.port}`) // Prüfe dass alle Komponenten sichtbar sind await expect(page.locator('[data-testid="os-button"]')).toBeVisible() await expect(page.locator('[data-testid="os-card"]')).toBeVisible() await expect(page.locator('[data-testid="os-modal"]')).toBeVisible() }) test('styles are applied correctly', async ({ page }) => { await page.goto(`http://localhost:${example.port}`) const button = page.locator('[data-testid="os-button-primary"]') // Prüfe dass CSS korrekt angewendet wird await expect(button).toHaveCSS('background-color', /rgb/) }) test('visual regression', async ({ page }) => { await page.goto(`http://localhost:${example.port}`) await expect(page).toHaveScreenshot(`${example.name}.png`) }) }) } ``` #### 5. Package Validation | Tool | Zweck | |------|-------| | **publint** | Prüft package.json auf Export-Fehler | | **arethetypeswrong** | Prüft TypeScript-Typen für alle Entry Points | ```json { "scripts": { "check:exports": "publint && attw --pack .", "prepublishOnly": "npm run check:exports" } } ``` #### 6. package.json Exports (korrekte Struktur) ```json { "name": "@ocelot-social/ui", "type": "module", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs", "types": "./dist/index.d.ts" }, "./style.css": "./dist/style.css", "./tailwind.preset": { "import": "./dist/tailwind.preset.mjs", "require": "./dist/tailwind.preset.cjs", "types": "./dist/tailwind.preset.d.ts" } }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" }, "peerDependenciesMeta": { "tailwindcss": { "optional": true } } } ``` ### CI Workflow: Compatibility Matrix ```yaml # .github/workflows/compatibility.yml name: Compatibility Tests on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - run: npm run build - uses: actions/upload-artifact@v4 with: name: dist path: packages/ui/dist test-unit: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: vue-version: ['2.7', '3.4'] steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: dist path: packages/ui/dist - run: npm ci - run: npm test env: VUE_VERSION: ${{ matrix.vue-version }} test-e2e: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: example: ['vue3-tailwind', 'vue3-css', 'vue2-tailwind', 'vue2-css'] steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: dist path: packages/ui/dist - name: Setup example app working-directory: packages/ui/examples/${{ matrix.example }} run: | npm ci npm run build - name: Start preview server working-directory: packages/ui/examples/${{ matrix.example }} run: npm run preview & - name: Wait for server run: npx wait-on http://localhost:4173 --timeout 30000 - name: Run Playwright tests working-directory: packages/ui run: npx playwright test --grep "${{ matrix.example }}" - uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report-${{ matrix.example }} path: packages/ui/playwright-report/ validate-package: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: dist path: packages/ui/dist - run: npm ci - name: Validate exports run: | npx publint packages/ui npx @arethetypeswrong/cli packages/ui ``` ### Werkzeug-Übersicht | Werkzeug | Zweck | Phase | |----------|-------|-------| | **vue-demi** | Vue 2/3 API-Kompatibilität im Code | Phase 2 | | **Vitest + Matrix** | Unit Tests für Vue 2.7 und Vue 3.4 | Phase 2 | | **Example Apps (4x)** | Echte Projekte für jede Kombination | Phase 2 | | **Playwright** | E2E + Visual Regression für alle 4 | Phase 2 | | **publint** | Package.json Export-Validierung | Phase 2 | | **arethetypeswrong** | TypeScript Entry Points Check | Phase 2 | | **pkg-pr-new** | Preview-Releases in PRs (optional) | Optional | ### Checkliste für neue Komponenten ``` [ ] Unit Tests laufen mit VUE_VERSION=2.7 [ ] Unit Tests laufen mit VUE_VERSION=3.4 [ ] Komponente in allen 4 Example Apps hinzugefügt [ ] E2E Tests für Komponente geschrieben [ ] Visual Regression Baseline erstellt [ ] Keine Vue 3-only APIs verwendet (oder via vue-demi abstrahiert) ``` --- ## 19. Komplexitätsanalyse ### Umfang nach Phasen | Phase | Aufgaben | Komplexität | Abhängigkeiten | |-------|----------|-------------|----------------| | Phase 0: Analyse | 6 Tasks | ✅ Erledigt | - | | Phase 1: Vue 2.7 | 6 Tasks | ✅ Erledigt | - | | Phase 2: Setup | 26 Tasks | Mittel | ⚠️ eslint-config-it4c (extern) | | Phase 3: Tokens | 6 Tasks | Niedrig | Keine externen | | Phase 4: Migration | 15 Komponenten | Hoch | Pro Komponente: Spec→Dev→Test→Integrate | | Phase 5: Finalisierung | 7 Tasks | Niedrig | Alle vorherigen Phasen | ### Bekannte Risikofaktoren | Risiko | Beschreibung | Auswirkung | Mitigation | |--------|--------------|------------|------------| | **eslint-config-it4c** | Externes Projekt muss zuerst modularisiert werden | Blockiert Linting-Setup in Phase 2 | Temporäre lokale ESLint-Config als Workaround | | **vue-demi Kompatibilität** | Unbekannte Edge-Cases bei Vue 2/3 Dual-Support | Unerwartete Bugs bei Integration | Frühzeitig in Example Apps testen | | **Visual Regression Baselines** | Können bei Design-Änderungen viel Nacharbeit erfordern | Zusätzlicher Aufwand bei Änderungen | Baselines erst nach Design-Freeze erstellen | | **Feature-Parity** | Alte Komponenten haben undokumentierte Verhaltensweisen | Regressions bei Migration | Gründliche Analyse vor Implementierung | | **Tailwind + CSS Dual-Build** | Komplexe Build-Konfiguration | Build-Fehler, Inkonsistenzen | Früh beide Varianten parallel testen | ### Parallelisierbarkeit | Phase | Parallelisierbar | Details | |-------|------------------|---------| | Phase 2 | Teilweise | Die meisten Tasks sind sequentiell (Setup-Reihenfolge wichtig) | | Phase 3 | Nein | Token-Ebenen bauen aufeinander auf (Base → Semantic → Component) | | Phase 4 | Ja (nach Tier 1) | Tier 2/3 Komponenten können parallel entwickelt werden | | Phase 5 | Teilweise | Dokumentation kann parallel zur letzten Integration | **Parallelisierbare Aufgaben in Phase 2:** - 4 Example Apps (vue3-tailwind, vue3-css, vue2-tailwind, vue2-css) - GitHub Workflows (unabhängig voneinander) - LICENSE, README.md, CONTRIBUTING.md **Sequentielle Abhängigkeiten in Phase 2:** 1. Vite + Vue 3 Projekt → vue-demi → Tailwind → Build-Pipeline 2. Vitest → Tests → Visual Regression 3. Package-Struktur → release-please → npm Publish Workflow ### Aufwandstreiber pro Komponente (Phase 4) Jede Komponente durchläuft: ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ ANALYSE │ → │ SPEC │ → │ DEVELOP │ → │ QA │ → │ INTEGRATE │ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ │ Bestehende │ │ Props │ │ Vue 3 Code │ │ Unit Tests │ │ In Webapp │ │ Varianten │ │ Events │ │ TypeScript │ │ Vue 2 Tests │ │ einbinden │ │ analysieren │ │ Slots │ │ Tailwind │ │ A11y Tests │ │ │ │ │ │ Tokens │ │ Storybook │ │ Visual Reg. │ │ Alte Komp. │ │ │ │ A11y │ │ Stories │ │ 4 Examples │ │ entfernen │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ``` **Aufwand variiert stark nach Komponente:** | Komponente | Komplexität | Grund | |------------|-------------|-------| | OsIcon | Niedrig | Einfache Wrapper-Komponente | | OsSpinner | Niedrig | Nur Animation + Größen | | OsButton | Hoch | Viele Varianten, Link-Support, States | | OsCard | Niedrig | Einfaches Layout | | OsModal | Hoch | Teleport, Focus-Trap, Animations, A11y | | OsDropdown | Hoch | Positioning, Click-Outside, Hover-States | | OsInput | Mittel | Validierung, States, Icons | | OsAvatar | Niedrig | Bild + Fallback |