From 5d1cabda469e7f8b0216520325e69822ea7496eb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 9 Feb 2026 11:53:12 +0100 Subject: [PATCH] refactor(webapp): vue3 migration - phase 2 - setup (#9161) --- .github/dependabot.yml | 68 + .github/workflows/ui-build.yml | 81 + .github/workflows/ui-compatibility.yml | 88 + .github/workflows/ui-docker.yml | 45 + .github/workflows/ui-lint.yml | 40 + .github/workflows/ui-release.yml | 65 + .github/workflows/ui-size.yml | 40 + .github/workflows/ui-storybook.yml | 60 + .github/workflows/ui-test.yml | 45 + .github/workflows/ui-verify.yml | 37 + .github/workflows/ui-visual.yml | 50 + .release-please-manifest.json | 3 + docker-compose.override.yml | 10 + docker-compose.yml | 8 + packages/ui/.gitignore | 34 + packages/ui/.storybook/main.ts | 26 + packages/ui/.storybook/preview.ts | 11 + packages/ui/.storybook/storybook.css | 34 + packages/ui/.tool-versions | 1 + packages/ui/CONTRIBUTING.md | 217 + packages/ui/Dockerfile | 29 + packages/ui/KATALOG.md | 1075 ++ packages/ui/LICENSE | 190 + packages/ui/PROJEKT.md | 1988 +++ packages/ui/README.md | 64 + packages/ui/eslint.config.ts | 126 + .../ui/examples/vue2-css/eslint.config.ts | 32 + packages/ui/examples/vue2-css/index.html | 12 + .../ui/examples/vue2-css/package-lock.json | 10162 +++++++++++ packages/ui/examples/vue2-css/package.json | 26 + .../ui/examples/vue2-css/prettier.config.mjs | 3 + packages/ui/examples/vue2-css/src/App.spec.ts | 18 + packages/ui/examples/vue2-css/src/App.vue | 19 + packages/ui/examples/vue2-css/src/main.ts | 9 + packages/ui/examples/vue2-css/tsconfig.json | 14 + packages/ui/examples/vue2-css/vite.config.ts | 10 + .../examples/vue2-tailwind/eslint.config.ts | 32 + packages/ui/examples/vue2-tailwind/index.html | 12 + .../examples/vue2-tailwind/package-lock.json | 10552 +++++++++++ .../ui/examples/vue2-tailwind/package.json | 28 + .../vue2-tailwind/prettier.config.mjs | 3 + .../ui/examples/vue2-tailwind/src/App.spec.ts | 16 + .../ui/examples/vue2-tailwind/src/App.vue | 16 + .../ui/examples/vue2-tailwind/src/main.ts | 9 + .../examples/vue2-tailwind/src/shims-vue.d.ts | 6 + .../ui/examples/vue2-tailwind/src/style.css | 4 + .../ui/examples/vue2-tailwind/tsconfig.json | 14 + .../ui/examples/vue2-tailwind/vite.config.ts | 11 + .../ui/examples/vue3-css/eslint.config.ts | 32 + packages/ui/examples/vue3-css/index.html | 12 + .../ui/examples/vue3-css/package-lock.json | 10140 +++++++++++ packages/ui/examples/vue3-css/package.json | 26 + .../ui/examples/vue3-css/prettier.config.mjs | 3 + packages/ui/examples/vue3-css/src/App.spec.ts | 16 + packages/ui/examples/vue3-css/src/App.vue | 13 + packages/ui/examples/vue3-css/src/main.ts | 8 + packages/ui/examples/vue3-css/tsconfig.json | 14 + packages/ui/examples/vue3-css/vite.config.ts | 10 + .../examples/vue3-tailwind/eslint.config.ts | 32 + packages/ui/examples/vue3-tailwind/index.html | 12 + .../examples/vue3-tailwind/package-lock.json | 10530 +++++++++++ .../ui/examples/vue3-tailwind/package.json | 28 + .../vue3-tailwind/prettier.config.mjs | 3 + .../ui/examples/vue3-tailwind/src/App.spec.ts | 16 + .../ui/examples/vue3-tailwind/src/App.vue | 11 + .../ui/examples/vue3-tailwind/src/main.ts | 8 + .../examples/vue3-tailwind/src/shims-vue.d.ts | 6 + .../ui/examples/vue3-tailwind/src/style.css | 4 + .../ui/examples/vue3-tailwind/tsconfig.json | 14 + .../ui/examples/vue3-tailwind/vite.config.ts | 11 + packages/ui/package-lock.json | 14458 ++++++++++++++++ packages/ui/package.json | 144 + packages/ui/playwright.config.ts | 63 + packages/ui/prettier.config.ts | 3 + packages/ui/scripts/check-completeness.ts | 179 + .../src/components/OsButton/OsButton.spec.ts | 97 + .../components/OsButton/OsButton.stories.ts | 131 + .../OsButton/OsButton.visual.spec.ts | 82 + .../ui/src/components/OsButton/OsButton.vue | 50 + packages/ui/src/components/OsButton/STATUS.md | 251 + .../__screenshots__/chromium/all-sizes.png | Bin 0 -> 8199 bytes .../__screenshots__/chromium/all-variants.png | Bin 0 -> 8455 bytes .../__screenshots__/chromium/danger.png | Bin 0 -> 1851 bytes .../__screenshots__/chromium/disabled.png | Bin 0 -> 1870 bytes .../__screenshots__/chromium/full-width.png | Bin 0 -> 2142 bytes .../__screenshots__/chromium/primary.png | Bin 0 -> 1807 bytes .../__screenshots__/chromium/secondary.png | Bin 0 -> 2122 bytes .../components/OsButton/button.variants.ts | 82 + packages/ui/src/components/OsButton/index.ts | 2 + packages/ui/src/components/index.ts | 10 + packages/ui/src/index.ts | 20 + packages/ui/src/plugin.spec.ts | 50 + packages/ui/src/plugin.ts | 26 + packages/ui/src/styles/index.css | 5 + packages/ui/src/tailwind.preset.spec.ts | 94 + packages/ui/src/tailwind.preset.ts | 95 + packages/ui/src/types.d.ts | 29 + packages/ui/src/utils/cn.spec.ts | 34 + packages/ui/src/utils/cn.ts | 16 + packages/ui/src/utils/index.ts | 1 + packages/ui/src/vite-env.d.ts | 8 + packages/ui/tsconfig.json | 46 + packages/ui/vite.config.ts | 80 + release-please-config.json | 19 + 104 files changed, 62467 insertions(+) create mode 100644 .github/workflows/ui-build.yml create mode 100644 .github/workflows/ui-compatibility.yml create mode 100644 .github/workflows/ui-docker.yml create mode 100644 .github/workflows/ui-lint.yml create mode 100644 .github/workflows/ui-release.yml create mode 100644 .github/workflows/ui-size.yml create mode 100644 .github/workflows/ui-storybook.yml create mode 100644 .github/workflows/ui-test.yml create mode 100644 .github/workflows/ui-verify.yml create mode 100644 .github/workflows/ui-visual.yml create mode 100644 .release-please-manifest.json create mode 100644 packages/ui/.gitignore create mode 100644 packages/ui/.storybook/main.ts create mode 100644 packages/ui/.storybook/preview.ts create mode 100644 packages/ui/.storybook/storybook.css create mode 100644 packages/ui/.tool-versions create mode 100644 packages/ui/CONTRIBUTING.md create mode 100644 packages/ui/Dockerfile create mode 100644 packages/ui/KATALOG.md create mode 100644 packages/ui/LICENSE create mode 100644 packages/ui/PROJEKT.md create mode 100644 packages/ui/README.md create mode 100644 packages/ui/eslint.config.ts create mode 100644 packages/ui/examples/vue2-css/eslint.config.ts create mode 100644 packages/ui/examples/vue2-css/index.html create mode 100644 packages/ui/examples/vue2-css/package-lock.json create mode 100644 packages/ui/examples/vue2-css/package.json create mode 100644 packages/ui/examples/vue2-css/prettier.config.mjs create mode 100644 packages/ui/examples/vue2-css/src/App.spec.ts create mode 100644 packages/ui/examples/vue2-css/src/App.vue create mode 100644 packages/ui/examples/vue2-css/src/main.ts create mode 100644 packages/ui/examples/vue2-css/tsconfig.json create mode 100644 packages/ui/examples/vue2-css/vite.config.ts create mode 100644 packages/ui/examples/vue2-tailwind/eslint.config.ts create mode 100644 packages/ui/examples/vue2-tailwind/index.html create mode 100644 packages/ui/examples/vue2-tailwind/package-lock.json create mode 100644 packages/ui/examples/vue2-tailwind/package.json create mode 100644 packages/ui/examples/vue2-tailwind/prettier.config.mjs create mode 100644 packages/ui/examples/vue2-tailwind/src/App.spec.ts create mode 100644 packages/ui/examples/vue2-tailwind/src/App.vue create mode 100644 packages/ui/examples/vue2-tailwind/src/main.ts create mode 100644 packages/ui/examples/vue2-tailwind/src/shims-vue.d.ts create mode 100644 packages/ui/examples/vue2-tailwind/src/style.css create mode 100644 packages/ui/examples/vue2-tailwind/tsconfig.json create mode 100644 packages/ui/examples/vue2-tailwind/vite.config.ts create mode 100644 packages/ui/examples/vue3-css/eslint.config.ts create mode 100644 packages/ui/examples/vue3-css/index.html create mode 100644 packages/ui/examples/vue3-css/package-lock.json create mode 100644 packages/ui/examples/vue3-css/package.json create mode 100644 packages/ui/examples/vue3-css/prettier.config.mjs create mode 100644 packages/ui/examples/vue3-css/src/App.spec.ts create mode 100644 packages/ui/examples/vue3-css/src/App.vue create mode 100644 packages/ui/examples/vue3-css/src/main.ts create mode 100644 packages/ui/examples/vue3-css/tsconfig.json create mode 100644 packages/ui/examples/vue3-css/vite.config.ts create mode 100644 packages/ui/examples/vue3-tailwind/eslint.config.ts create mode 100644 packages/ui/examples/vue3-tailwind/index.html create mode 100644 packages/ui/examples/vue3-tailwind/package-lock.json create mode 100644 packages/ui/examples/vue3-tailwind/package.json create mode 100644 packages/ui/examples/vue3-tailwind/prettier.config.mjs create mode 100644 packages/ui/examples/vue3-tailwind/src/App.spec.ts create mode 100644 packages/ui/examples/vue3-tailwind/src/App.vue create mode 100644 packages/ui/examples/vue3-tailwind/src/main.ts create mode 100644 packages/ui/examples/vue3-tailwind/src/shims-vue.d.ts create mode 100644 packages/ui/examples/vue3-tailwind/src/style.css create mode 100644 packages/ui/examples/vue3-tailwind/tsconfig.json create mode 100644 packages/ui/examples/vue3-tailwind/vite.config.ts create mode 100644 packages/ui/package-lock.json create mode 100644 packages/ui/package.json create mode 100644 packages/ui/playwright.config.ts create mode 100644 packages/ui/prettier.config.ts create mode 100644 packages/ui/scripts/check-completeness.ts create mode 100644 packages/ui/src/components/OsButton/OsButton.spec.ts create mode 100644 packages/ui/src/components/OsButton/OsButton.stories.ts create mode 100644 packages/ui/src/components/OsButton/OsButton.visual.spec.ts create mode 100644 packages/ui/src/components/OsButton/OsButton.vue create mode 100644 packages/ui/src/components/OsButton/STATUS.md create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/all-sizes.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/all-variants.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/danger.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/disabled.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/full-width.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/primary.png create mode 100644 packages/ui/src/components/OsButton/__screenshots__/chromium/secondary.png create mode 100644 packages/ui/src/components/OsButton/button.variants.ts create mode 100644 packages/ui/src/components/OsButton/index.ts create mode 100644 packages/ui/src/components/index.ts create mode 100644 packages/ui/src/index.ts create mode 100644 packages/ui/src/plugin.spec.ts create mode 100644 packages/ui/src/plugin.ts create mode 100644 packages/ui/src/styles/index.css create mode 100644 packages/ui/src/tailwind.preset.spec.ts create mode 100644 packages/ui/src/tailwind.preset.ts create mode 100644 packages/ui/src/types.d.ts create mode 100644 packages/ui/src/utils/cn.spec.ts create mode 100644 packages/ui/src/utils/cn.ts create mode 100644 packages/ui/src/utils/index.ts create mode 100644 packages/ui/src/vite-env.d.ts create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/ui/vite.config.ts create mode 100644 release-please-config.json diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 06f4c4851..6505896ff 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -126,3 +126,71 @@ updates: day: "saturday" timezone: "Europe/Berlin" time: "03:00" + +# ui library +- package-ecosystem: npm + open-pull-requests-limit: 99 + directory: "/packages/ui" + rebase-strategy: "disabled" + schedule: + interval: weekly + day: "saturday" + timezone: "Europe/Berlin" + time: "03:00" + groups: + vue: + applies-to: version-updates + patterns: + - "vue*" + - "@vue*" + vite: + applies-to: version-updates + patterns: + - "vite*" + - "@vitejs*" + vitest: + applies-to: version-updates + patterns: + - "vitest*" + - "@vitest*" + +# ui examples +- package-ecosystem: npm + open-pull-requests-limit: 99 + directory: "/packages/ui/examples/vue3-tailwind" + rebase-strategy: "disabled" + schedule: + interval: weekly + day: "saturday" + timezone: "Europe/Berlin" + time: "03:00" + +- package-ecosystem: npm + open-pull-requests-limit: 99 + directory: "/packages/ui/examples/vue3-css" + rebase-strategy: "disabled" + schedule: + interval: weekly + day: "saturday" + timezone: "Europe/Berlin" + time: "03:00" + +- package-ecosystem: npm + open-pull-requests-limit: 99 + directory: "/packages/ui/examples/vue2-tailwind" + rebase-strategy: "disabled" + schedule: + interval: weekly + day: "saturday" + timezone: "Europe/Berlin" + time: "03:00" + +- package-ecosystem: npm + open-pull-requests-limit: 99 + directory: "/packages/ui/examples/vue2-css" + rebase-strategy: "disabled" + schedule: + interval: weekly + day: "saturday" + timezone: "Europe/Berlin" + time: "03:00" diff --git a/.github/workflows/ui-build.yml b/.github/workflows/ui-build.yml new file mode 100644 index 000000000..bf134db5c --- /dev/null +++ b/.github/workflows/ui-build.yml @@ -0,0 +1,81 @@ +name: UI Build + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build library + run: npm run build + + - name: Verify build output + run: | + echo "Checking build output..." + + # Check that dist directory exists + if [ ! -d "dist" ]; then + echo "::error::dist directory not found" + exit 1 + fi + + # Check required files exist + FILES=( + "dist/index.mjs" + "dist/index.cjs" + "dist/index.d.ts" + "dist/index.d.cts" + "dist/tailwind.preset.mjs" + "dist/tailwind.preset.cjs" + "dist/tailwind.preset.d.ts" + "dist/tailwind.preset.d.cts" + "dist/style.css" + ) + + for file in "${FILES[@]}"; do + if [ ! -f "$file" ]; then + echo "::error::Missing required file: $file" + exit 1 + fi + echo "✓ $file" + done + + echo "" + echo "All build outputs verified!" + + - name: Validate package + run: npm run validate + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: packages/ui/dist/ + retention-days: 7 diff --git a/.github/workflows/ui-compatibility.yml b/.github/workflows/ui-compatibility.yml new file mode 100644 index 000000000..9bc1080af --- /dev/null +++ b/.github/workflows/ui-compatibility.yml @@ -0,0 +1,88 @@ +name: UI Compatibility + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +jobs: + build-library: + name: Build Library + runs-on: ubuntu-latest + defaults: + run: + working-directory: packages/ui + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build library + run: npm run build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: ui-dist + path: packages/ui/dist/ + retention-days: 1 + + test-compatibility: + name: Test ${{ matrix.example }} + needs: build-library + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - vue3-tailwind + - vue3-css + - vue2-tailwind + - vue2-css + defaults: + run: + working-directory: packages/ui/examples/${{ matrix.example }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: ui-dist + path: packages/ui/dist/ + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/examples/${{ matrix.example }}/package-lock.json + + - name: Install dependencies + run: npm install + + - name: Lint + run: npm run lint + + - name: Run tests + run: npm test + + - name: Build example app + run: npm run build diff --git a/.github/workflows/ui-docker.yml b/.github/workflows/ui-docker.yml new file mode 100644 index 000000000..2f783aeaa --- /dev/null +++ b/.github/workflows/ui-docker.yml @@ -0,0 +1,45 @@ +name: UI Docker + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +jobs: + build: + name: Build Docker Image + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build development image + uses: docker/build-push-action@v6 + with: + context: ./packages/ui + file: ./packages/ui/Dockerfile + target: development + push: false + tags: ocelot-social/ui:development + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build production image + uses: docker/build-push-action@v6 + with: + context: ./packages/ui + file: ./packages/ui/Dockerfile + target: production + push: false + tags: ocelot-social/ui:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/ui-lint.yml b/.github/workflows/ui-lint.yml new file mode 100644 index 000000000..d0c78be44 --- /dev/null +++ b/.github/workflows/ui-lint.yml @@ -0,0 +1,40 @@ +name: UI Lint + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + lint: + name: ESLint + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint + + - name: Run TypeScript type check + run: npm run typecheck diff --git a/.github/workflows/ui-release.yml b/.github/workflows/ui-release.yml new file mode 100644 index 000000000..52205fa75 --- /dev/null +++ b/.github/workflows/ui-release.yml @@ -0,0 +1,65 @@ +name: UI Release + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + - 'release-please-config.json' + - '.release-please-manifest.json' + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + name: Release Please + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs['packages/ui--release_created'] }} + tag_name: ${{ steps.release.outputs['packages/ui--tag_name'] }} + version: ${{ steps.release.outputs['packages/ui--version'] }} + + steps: + - name: Release Please + id: release + uses: googleapis/release-please-action@v4 + with: + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + + publish: + name: Publish to npm + needs: release-please + if: ${{ needs.release-please.outputs.release_created == 'true' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: packages/ui + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + registry-url: 'https://registry.npmjs.org' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Validate package + run: npm run validate + + - name: Publish to npm + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/ui-size.yml b/.github/workflows/ui-size.yml new file mode 100644 index 000000000..45e73d803 --- /dev/null +++ b/.github/workflows/ui-size.yml @@ -0,0 +1,40 @@ +name: UI Size + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + size: + name: Bundle Size Check + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Check bundle size + run: npm run size diff --git a/.github/workflows/ui-storybook.yml b/.github/workflows/ui-storybook.yml new file mode 100644 index 000000000..b26f5c9dc --- /dev/null +++ b/.github/workflows/ui-storybook.yml @@ -0,0 +1,60 @@ +name: UI Storybook + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + build: + name: Build Storybook + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build Storybook + run: npm run storybook:build + + - name: Verify build output + run: | + echo "Checking Storybook build output..." + + if [ ! -d "storybook-static" ]; then + echo "::error::storybook-static directory not found" + exit 1 + fi + + if [ ! -f "storybook-static/index.html" ]; then + echo "::error::index.html not found in storybook-static" + exit 1 + fi + + echo "✓ Storybook build verified!" + + - name: Upload Storybook artifacts + uses: actions/upload-artifact@v4 + with: + name: storybook-static + path: packages/ui/storybook-static/ + retention-days: 7 diff --git a/.github/workflows/ui-test.yml b/.github/workflows/ui-test.yml new file mode 100644 index 000000000..41e16518d --- /dev/null +++ b/.github/workflows/ui-test.yml @@ -0,0 +1,45 @@ +name: UI Test + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + test: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Run tests with coverage + run: npm run test:coverage + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-report + path: packages/ui/coverage/ + retention-days: 7 diff --git a/.github/workflows/ui-verify.yml b/.github/workflows/ui-verify.yml new file mode 100644 index 000000000..0d594d79e --- /dev/null +++ b/.github/workflows/ui-verify.yml @@ -0,0 +1,37 @@ +name: UI Verify + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + verify: + name: Completeness Check + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Check component completeness + run: npm run verify diff --git a/.github/workflows/ui-visual.yml b/.github/workflows/ui-visual.yml new file mode 100644 index 000000000..af95e57ab --- /dev/null +++ b/.github/workflows/ui-visual.yml @@ -0,0 +1,50 @@ +name: UI Visual + +on: + push: + branches: [master] + paths: + - 'packages/ui/**' + pull_request: + branches: [master] + paths: + - 'packages/ui/**' + +defaults: + run: + working-directory: packages/ui + +jobs: + visual: + name: Visual Regression + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'packages/ui/.tool-versions' + cache: 'npm' + cache-dependency-path: packages/ui/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install chromium --with-deps + + - name: Run visual tests + run: npm run test:visual + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: failure() + with: + name: visual-test-results + path: | + packages/ui/test-results/ + packages/ui/playwright-report/ + retention-days: 7 diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 000000000..eac1e4426 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + "packages/ui": "0.0.1" +} diff --git a/docker-compose.override.yml b/docker-compose.override.yml index bb7ae55f6..fd9930d0c 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -91,5 +91,15 @@ services: S3_RESULT_STORAGE_BUCKET: ocelot # enable S3 result storage by specifying bucket HTTP_LOADER_BASE_URL: http://minio:9000 + ui: + image: ghcr.io/ocelot-social-community/ocelot-social/ui:local-development + build: + target: development + ports: + - 6006:6006 + volumes: + - ./packages/ui:/app + - /app/node_modules + volumes: minio_data: diff --git a/docker-compose.yml b/docker-compose.yml index c632f0632..10236f5de 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,5 +83,13 @@ services: # bring the database in offline mode to export or load dumps # command: ["tail", "-f", "/dev/null"] + ui: + image: ghcr.io/ocelot-social-community/ocelot-social/ui:${OCELOT_VERSION:-latest} + build: + context: ./packages/ui + target: production + ports: + - 6006:80 + volumes: neo4j_data: diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore new file mode 100644 index 000000000..c5f72feb2 --- /dev/null +++ b/packages/ui/.gitignore @@ -0,0 +1,34 @@ +# Dependencies +node_modules/ + +# Build output +dist/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Test coverage +coverage/ + +# Playwright +test-results/ +playwright-report/ + +# Storybook +storybook-static/ + +# Logs +*.log +npm-debug.log* + +# Temporary files +*.tmp +*.temp diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts new file mode 100644 index 000000000..b8cdf8aec --- /dev/null +++ b/packages/ui/.storybook/main.ts @@ -0,0 +1,26 @@ +import type { StorybookConfig } from '@storybook/vue3-vite' + +const config: StorybookConfig = { + stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + framework: { + name: '@storybook/vue3-vite', + options: {}, + }, + viteFinal(viteConfig) { + // Remove plugins that are only needed for library build + viteConfig.plugins = viteConfig.plugins?.filter((plugin) => { + const name = plugin && 'name' in plugin ? plugin.name : '' + return name !== 'vite:dts' && name !== 'build-css' + }) + + // Remove library build config + if (viteConfig.build) { + delete viteConfig.build.lib + delete viteConfig.build.rollupOptions + } + + return viteConfig + }, +} + +export default config diff --git a/packages/ui/.storybook/preview.ts b/packages/ui/.storybook/preview.ts new file mode 100644 index 000000000..6b57b34c3 --- /dev/null +++ b/packages/ui/.storybook/preview.ts @@ -0,0 +1,11 @@ +// eslint-disable-next-line import-x/no-unassigned-import +import './storybook.css' + +export const parameters = { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, +} diff --git a/packages/ui/.storybook/storybook.css b/packages/ui/.storybook/storybook.css new file mode 100644 index 000000000..854f50ae6 --- /dev/null +++ b/packages/ui/.storybook/storybook.css @@ -0,0 +1,34 @@ +/* Inter font for consistent rendering across platforms */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); + +@import "tailwindcss"; + +/* Greyscale theme for Storybook - demonstrates that colors come from the app */ +/* All colors meet WCAG AA contrast requirements (4.5:1 for normal text) */ +:root { + font-family: 'Inter', system-ui, sans-serif; + + --color-primary: #404040; + --color-primary-hover: #262626; + --color-primary-contrast: #ffffff; + + --color-secondary: #595959; + --color-secondary-hover: #404040; + --color-secondary-contrast: #ffffff; + + --color-danger: #525252; + --color-danger-hover: #404040; + --color-danger-contrast: #ffffff; + + --color-warning: #d4d4d4; + --color-warning-hover: #a3a3a3; + --color-warning-contrast: #000000; + + --color-success: #525252; + --color-success-hover: #404040; + --color-success-contrast: #ffffff; + + --color-info: #595959; + --color-info-hover: #404040; + --color-info-contrast: #ffffff; +} diff --git a/packages/ui/.tool-versions b/packages/ui/.tool-versions new file mode 100644 index 000000000..a25a7a076 --- /dev/null +++ b/packages/ui/.tool-versions @@ -0,0 +1 @@ +nodejs 25.5.0 diff --git a/packages/ui/CONTRIBUTING.md b/packages/ui/CONTRIBUTING.md new file mode 100644 index 000000000..f867e28cd --- /dev/null +++ b/packages/ui/CONTRIBUTING.md @@ -0,0 +1,217 @@ +# Contributing to @ocelot-social/ui + +Thank you for contributing to the ocelot.social UI library! + +## Development Setup + +```bash +# Install dependencies +npm install + +# Start development server +npm run dev + +# Run tests +npm test + +# Run linter +npm run lint +``` + +## Creating a New Component + +### File Structure + +``` +src/components/ +└── OsButton/ + ├── OsButton.vue # Component + ├── OsButton.spec.ts # Tests + └── index.ts # Export +``` + +### Naming Convention + +- All components use the `Os` prefix: `OsButton`, `OsCard`, `OsModal` +- Files use PascalCase: `OsButton.vue` + +### Component Template + +```vue + + + +``` + +## Code Standards + +### TypeScript + +- `strict: true` is enabled +- All props must be typed +- Use JSDoc comments for documentation + +### Props + +Use the complete Tailwind-based scales: + +| Prop | Values | +|------|--------| +| `size` | `xs`, `sm`, `md`, `lg`, `xl`, `2xl` | +| `rounded` | `none`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `full` | +| `shadow` | `none`, `sm`, `md`, `lg`, `xl`, `2xl` | +| `variant` | `primary`, `secondary`, `danger`, `warning`, `success`, `info` | + +### Styling + +- Use CSS Custom Properties (no hardcoded colors) +- Use Tailwind utility classes +- Dark mode via `dark:` prefix + +```vue + +``` + +### Vue 2/3 Compatibility + +Always import from `vue-demi`, not `vue`: + +```typescript +// Correct +import { ref, computed } from 'vue-demi' + +// Wrong +import { ref, computed } from 'vue' +``` + +## Testing + +### Requirements + +- **100% code coverage** is required +- Tests must pass for both Vue 2.7 and Vue 3 + +### Writing Tests + +```typescript +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' + +import OsButton from './OsButton.vue' + +describe('OsButton', () => { + it('renders slot content', () => { + const wrapper = mount(OsButton, { + slots: { default: 'Click me' }, + }) + expect(wrapper.text()).toBe('Click me') + }) + + it('applies size class', () => { + const wrapper = mount(OsButton, { + props: { size: 'lg' }, + }) + expect(wrapper.classes()).toContain('os-button--lg') + }) +}) +``` + +### Running Tests + +```bash +# Run once +npm test + +# Watch mode +npm run test:watch + +# With coverage +npm run test:coverage +``` + +## Commit Conventions + +We use [Conventional Commits](https://www.conventionalcommits.org/) for automatic releases: + +```bash +# Features (minor version bump) +feat(button): add loading state + +# Bug fixes (patch version bump) +fix(modal): correct focus trap behavior + +# Breaking changes (major version bump) +feat(input)!: rename value prop to modelValue + +# Other types +docs: update README +test: add button accessibility tests +chore: update dependencies +refactor: simplify dropdown logic +``` + +## Pull Request Checklist + +Before submitting a PR, ensure: + +- [ ] Tests pass (`npm test`) +- [ ] Linter passes (`npm run lint`) +- [ ] Build succeeds (`npm run build`) +- [ ] 100% code coverage maintained +- [ ] New components have Histoire stories +- [ ] Props have JSDoc documentation +- [ ] Commit messages follow Conventional Commits + +## Example Apps + +Test your changes in the example apps: + +```bash +# Vue 3 + Tailwind +cd examples/vue3-tailwind && npm install && npm run dev + +# Vue 3 + CSS +cd examples/vue3-css && npm install && npm run dev + +# Vue 2 + Tailwind +cd examples/vue2-tailwind && npm install && npm run dev + +# Vue 2 + CSS +cd examples/vue2-css && npm install && npm run dev +``` + +## Questions? + +- Check the [main CONTRIBUTING.md](../../CONTRIBUTING.md) for general guidelines +- Join the [Discord](https://discord.gg/AJSX9DCSUA) for questions +- Open an issue for bugs or feature requests diff --git a/packages/ui/Dockerfile b/packages/ui/Dockerfile new file mode 100644 index 000000000..4d796bd93 --- /dev/null +++ b/packages/ui/Dockerfile @@ -0,0 +1,29 @@ +FROM node:25.5.0-alpine AS base +LABEL org.label-schema.name="ocelot.social:ui" +LABEL org.label-schema.description="UI Component Library for ocelot.social" +LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/packages/ui/README.md" +LABEL org.label-schema.url="https://ocelot.social" +LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/packages/ui" +LABEL org.label-schema.vendor="ocelot.social Community" +LABEL org.label-schema.schema-version="1.0" +LABEL maintainer="devops@ocelot.social" +RUN mkdir -p /app +WORKDIR /app + +FROM base AS development +ENV NODE_ENV="development" +EXPOSE 6006 +CMD ["/bin/sh", "-c", "npm install && npm run dev"] + +FROM base AS build +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run storybook:build + +FROM nginx:alpine AS production +LABEL org.label-schema.name="ocelot.social:ui-storybook" +LABEL org.label-schema.description="UI Component Library Storybook for ocelot.social" +COPY --from=build /app/storybook-static /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/packages/ui/KATALOG.md b/packages/ui/KATALOG.md new file mode 100644 index 000000000..7b932a545 --- /dev/null +++ b/packages/ui/KATALOG.md @@ -0,0 +1,1075 @@ +# Komponenten-Katalog + +> Tracking der Katalogisierung aller bestehenden Komponenten. +> Diese Datei ermöglicht das Unterbrechen und Fortsetzen der Analyse. + +--- + +## Fortschritt + +### Übersicht +``` +Phase 0: Analyse ██████████ 100% (8/8 Schritte) +─────────────────────────────────────────── +Nächste Phase: Phase 2 (Projekt-Setup) +``` + +### Statistiken +| Metrik | Wert | +|--------|------| +| Webapp Komponenten | 139 | +| Styleguide Komponenten | 38 | +| **Gesamt** | **177** | +| Detailiert analysiert | 3 Familien (Button, Modal, Menu) | +| Duplikate gefunden | 5 direkte + 3 Familien | +| Zur Migration priorisiert | 15 Kern-Komponenten | + +--- + +## Styleguide Komponenten (38) + +> Quelle: `../../styleguide/src/system/components/` +> Live: http://styleguide.ocelot.social/ + +### Data Display +| # | Komponente | Status | Webapp-Duplikat | Varianten | Priorität | Notizen | +|---|------------|--------|-----------------|-----------|-----------|---------| +| 1 | Avatar | ⬜ Ausstehend | | | | | +| 2 | Card | ⬜ Ausstehend | BaseCard? | | | | +| 3 | Chip | ⬜ Ausstehend | | | | | +| 4 | Code | ⬜ Ausstehend | | | | | +| 5 | Icon | ⬜ Ausstehend | BaseIcon? | | | | +| 6 | Number | ⬜ Ausstehend | | | | | +| 7 | Placeholder | ⬜ Ausstehend | | | | | +| 8 | Spinner | ⬜ Ausstehend | LoadingSpinner? | | | | +| 9 | Table | ⬜ Ausstehend | | | | | +| 10 | TableCol | ⬜ Ausstehend | | | | | +| 11 | TableHeadCol | ⬜ Ausstehend | | | | | +| 12 | Tag | ⬜ Ausstehend | | | | | + +### Data Input +| # | Komponente | Status | Webapp-Duplikat | Varianten | Priorität | Notizen | +|---|------------|--------|-----------------|-----------|-----------|---------| +| 13 | Button | ⬜ Ausstehend | BaseButton, CustomButton, ActionButton, ... | | | VIELE Varianten! | +| 14 | CopyField | ⬜ Ausstehend | | | | | +| 15 | Form | ⬜ Ausstehend | | | | | +| 16 | FormItem | ⬜ Ausstehend | | | | | +| 17 | Input | ⬜ Ausstehend | SearchableInput, LinkInput | | | | +| 18 | InputError | ⬜ Ausstehend | | | | | +| 19 | InputLabel | ⬜ Ausstehend | | | | | +| 20 | Radio | ⬜ Ausstehend | | | | | +| 21 | Select | ⬜ Ausstehend | Dropdown, LocationSelect | | | | + +### Layout +| # | Komponente | Status | Webapp-Duplikat | Varianten | Priorität | Notizen | +|---|------------|--------|-----------------|-----------|-----------|---------| +| 22 | Container | ⬜ Ausstehend | | | | | +| 23 | Flex | ⬜ Ausstehend | | | | | +| 24 | FlexItem | ⬜ Ausstehend | | | | | +| 25 | Grid | ⬜ Ausstehend | MasonryGrid? | | | | +| 26 | GridItem | ⬜ Ausstehend | MasonryGridItem? | | | | +| 27 | Modal | ⬜ Ausstehend | Modal, ConfirmModal, ... | | | | +| 28 | Page | ⬜ Ausstehend | InternalPage? | | | | +| 29 | PageTitle | ⬜ Ausstehend | | | | | +| 30 | Section | ⬜ Ausstehend | | | | | +| 31 | Space | ⬜ Ausstehend | | | | | + +### Navigation +| # | Komponente | Status | Webapp-Duplikat | Varianten | Priorität | Notizen | +|---|------------|--------|-----------------|-----------|-----------|---------| +| 32 | List | ⬜ Ausstehend | | | | | +| 33 | ListItem | ⬜ Ausstehend | | | | | +| 34 | Logo | ⬜ Ausstehend | Logo | | | DUPLIKAT | +| 35 | Menu | ⬜ Ausstehend | HeaderMenu, ContentMenu, ... | | | | +| 36 | MenuItem | ⬜ Ausstehend | | | | | + +### Typography +| # | Komponente | Status | Webapp-Duplikat | Varianten | Priorität | Notizen | +|---|------------|--------|-----------------|-----------|-----------|---------| +| 37 | Heading | ⬜ Ausstehend | SearchHeading? | | | | +| 38 | Text | ⬜ Ausstehend | | | | | + +--- + +## Webapp Komponenten (139) + +> Quelle: `../../webapp/components/` + +### Status-Legende +- ⬜ Ausstehend +- ⏳ In Arbeit +- ✅ Analysiert +- 🔗 Duplikat (siehe Notizen) +- ⛔ Nicht migrieren (veraltet/ungenutzt) +- 🔄 Konsolidieren (mit anderen zusammenführen) + +### A-B +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 1 | ActionButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 2 | ActionRadiusSelect | ⬜ Ausstehend | Input | | | +| 3 | AddChatRoomByUserSearch | ⬜ Ausstehend | Feature | | Chat-spezifisch | +| 4 | AddGroupMember | ⬜ Ausstehend | Feature | | Group-spezifisch | +| 5 | AvatarMenu | ⬜ Ausstehend | Navigation | Avatar + Menu | | +| 6 | AvatarUploader | ⬜ Ausstehend | Input | | | +| 7 | BadgeSelection | ⬜ Ausstehend | Input | | | +| 8 | Badges | ⬜ Ausstehend | Display | | | +| 9 | BadgesSection | ⬜ Ausstehend | Display | | | +| 10 | BaseButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 11 | BaseCard | ⬜ Ausstehend | Layout | Card | 🔗 DUPLIKAT | +| 12 | BaseIcon | ⬜ Ausstehend | Display | Icon | 🔗 DUPLIKAT | + +### C +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 13 | CategoriesFilter | ⬜ Ausstehend | Filter | | | +| 14 | CategoriesMenu | ⬜ Ausstehend | Navigation | Menu | | +| 15 | CategoriesSelect | ⬜ Ausstehend | Input | Select | | +| 16 | ChangePassword | ⬜ Ausstehend | Feature | | Auth-spezifisch | +| 17 | Change | ⬜ Ausstehend | Feature | | | +| 18 | Chat | ⬜ Ausstehend | Feature | | Chat-spezifisch | +| 19 | ChatNotificationMenu | ⬜ Ausstehend | Feature | | Chat-spezifisch | +| 20 | CommentCard | ⬜ Ausstehend | Display | Card | | +| 21 | CommentForm | ⬜ Ausstehend | Input | Form | | +| 22 | CommentList | ⬜ Ausstehend | Display | List | | +| 23 | ComponentSlider | ⬜ Ausstehend | Layout | | | +| 24 | ConfirmModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | +| 25 | ContentMenu | ⬜ Ausstehend | Navigation | Menu | | +| 26 | ContentViewer | ⬜ Ausstehend | Display | | | +| 27 | ContextMenu | ⬜ Ausstehend | Navigation | Menu | | +| 28 | ContributionForm | ⬜ Ausstehend | Feature | Form | Post-spezifisch | +| 29 | CounterIcon | ⬜ Ausstehend | Display | Icon | | +| 30 | CountTo | ⬜ Ausstehend | Display | Number | Animation | +| 31 | CreateInvitation | ⬜ Ausstehend | Feature | | | +| 32 | CtaJoinLeaveGroup | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 33 | CtaUnblockAuthor | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 34 | CustomButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | + +### D-E +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 35 | DateTimeRange | ⬜ Ausstehend | Input | | | +| 36 | DeleteData | ⬜ Ausstehend | Feature | | | +| 37 | DeleteUserModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | +| 38 | DisableModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | +| 39 | DonationInfo | ⬜ Ausstehend | Display | | | +| 40 | Dropdown | ⬜ Ausstehend | Input | Select | | +| 41 | DropdownFilter | ⬜ Ausstehend | Filter | Select | | +| 42 | Editor | ⬜ Ausstehend | Input | | Rich-Text | +| 43 | EmailDisplayAndVerify | ⬜ Ausstehend | Feature | | | +| 44 | EmbedComponent | ⬜ Ausstehend | Display | | | +| 45 | EmotionButton | ⬜ Ausstehend | Button | Button | | +| 46 | Emotions | ⬜ Ausstehend | Feature | | | +| 47 | Empty | ⬜ Ausstehend | Feedback | Placeholder | | +| 48 | EnterNonce | ⬜ Ausstehend | Feature | | Auth | + +### F-G +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 49 | EventsByFilter | ⬜ Ausstehend | Filter | | | +| 50 | FiledReportsTable | ⬜ Ausstehend | Display | Table | | +| 51 | FilterMenu | ⬜ Ausstehend | Navigation | Menu | | +| 52 | FilterMenuComponent | ⬜ Ausstehend | Navigation | Menu | | +| 53 | FilterMenuSection | ⬜ Ausstehend | Navigation | Menu | | +| 54 | FollowButton | ⬜ Ausstehend | Button | Button | | +| 55 | FollowingFilter | ⬜ Ausstehend | Filter | | | +| 56 | FollowList | ⬜ Ausstehend | Display | List | | +| 57 | GroupButton | ⬜ Ausstehend | Button | Button | | +| 58 | GroupContentMenu | ⬜ Ausstehend | Navigation | Menu | | +| 59 | GroupForm | ⬜ Ausstehend | Input | Form | | +| 60 | GroupLink | ⬜ Ausstehend | Navigation | | | +| 61 | GroupList | ⬜ Ausstehend | Display | List | | +| 62 | GroupMember | ⬜ Ausstehend | Display | | | +| 63 | GroupTeaser | ⬜ Ausstehend | Display | Card | | + +### H-L +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 64 | Hashtag | ⬜ Ausstehend | Display | Tag/Chip | | +| 65 | HashtagsFilter | ⬜ Ausstehend | Filter | | | +| 66 | HeaderButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 67 | HeaderMenu | ⬜ Ausstehend | Navigation | Menu | | +| 68 | ImageUploader | ⬜ Ausstehend | Input | | | +| 69 | index | ⬜ Ausstehend | ? | | Prüfen | +| 70 | InternalPage | ⬜ Ausstehend | Layout | Page | | +| 71 | Invitation | ⬜ Ausstehend | Feature | | | +| 72 | InvitationList | ⬜ Ausstehend | Display | List | | +| 73 | InviteButton | ⬜ Ausstehend | Button | Button | | +| 74 | JoinLeaveButton | ⬜ Ausstehend | Button | Button | | +| 75 | LabeledButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 76 | LinkInput | ⬜ Ausstehend | Input | Input | | +| 77 | LoadingSpinner | ⬜ Ausstehend | Feedback | Spinner | 🔗 DUPLIKAT | +| 78 | LocaleSwitch | ⬜ Ausstehend | Navigation | | | +| 79 | LocationInfo | ⬜ Ausstehend | Display | | | +| 80 | LocationSelect | ⬜ Ausstehend | Input | Select | | +| 81 | LocationTeaser | ⬜ Ausstehend | Display | Card | | +| 82 | LoginButton | ⬜ Ausstehend | Button | Button | | +| 83 | LoginForm | ⬜ Ausstehend | Feature | Form | Auth | +| 84 | Logo | ⬜ Ausstehend | Display | Logo | 🔗 DUPLIKAT | + +### M-O +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 85 | MapButton | ⬜ Ausstehend | Button | Button | | +| 86 | MapStylesButtons | ⬜ Ausstehend | Button | Button | | +| 87 | MasonryGrid | ⬜ Ausstehend | Layout | Grid | | +| 88 | MasonryGridItem | ⬜ Ausstehend | Layout | GridItem | | +| 89 | MenuBar | ⬜ Ausstehend | Navigation | Menu | | +| 90 | MenuBarButton | ⬜ Ausstehend | Button | Button | 🔄 Button-Familie | +| 91 | MenuLegend | ⬜ Ausstehend | Navigation | | | +| 92 | Modal | ⬜ Ausstehend | Feedback | Modal | 🔗 DUPLIKAT | +| 93 | MySomethingList | ⬜ Ausstehend | Display | List | | +| 94 | NotificationMenu | ⬜ Ausstehend | Navigation | Menu | | +| 95 | NotificationsTable | ⬜ Ausstehend | Display | Table | | +| 96 | ObserveButton | ⬜ Ausstehend | Button | Button | | +| 97 | OrderByFilter | ⬜ Ausstehend | Filter | | | + +### P-R +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 98 | PageFooter | ⬜ Ausstehend | Layout | | | +| 99 | PageParamsLink | ⬜ Ausstehend | Navigation | | | +| 100 | PaginationButtons | ⬜ Ausstehend | Navigation | | | +| 101 | PostTeaser | ⬜ Ausstehend | Display | Card | | +| 102 | PostTypeFilter | ⬜ Ausstehend | Filter | | | +| 103 | ProfileAvatar | ⬜ Ausstehend | Display | Avatar | | +| 104 | ProfileList | ⬜ Ausstehend | Display | List | | +| 105 | ProgressBar | ⬜ Ausstehend | Feedback | | | +| 106 | RegistrationSlideCreate | ⬜ Ausstehend | Feature | | Auth | +| 107 | RegistrationSlideEmail | ⬜ Ausstehend | Feature | | Auth | +| 108 | RegistrationSlideInvite | ⬜ Ausstehend | Feature | | Auth | +| 109 | RegistrationSlideNonce | ⬜ Ausstehend | Feature | | Auth | +| 110 | RegistrationSlideNoPublic | ⬜ Ausstehend | Feature | | Auth | +| 111 | RegistrationSlider | ⬜ Ausstehend | Feature | | Auth | +| 112 | ReleaseModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | +| 113 | ReportList | ⬜ Ausstehend | Display | List | | +| 114 | ReportModal | ⬜ Ausstehend | Feedback | Modal | 🔄 Modal-Familie | +| 115 | ReportRow | ⬜ Ausstehend | Display | | | +| 116 | ReportsTable | ⬜ Ausstehend | Display | Table | | +| 117 | Request | ⬜ Ausstehend | Feature | | | +| 118 | ResponsiveImage | ⬜ Ausstehend | Display | | | + +### S +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 119 | SearchableInput | ⬜ Ausstehend | Input | Input | | +| 120 | SearchField | ⬜ Ausstehend | Input | Input | | +| 121 | SearchGroup | ⬜ Ausstehend | Feature | | Search | +| 122 | SearchHeading | ⬜ Ausstehend | Display | Heading | | +| 123 | SearchPost | ⬜ Ausstehend | Feature | | Search | +| 124 | SearchResults | ⬜ Ausstehend | Feature | | Search | +| 125 | SelectUserSearch | ⬜ Ausstehend | Input | Select | | +| 126 | ShoutButton | ⬜ Ausstehend | Button | Button | | +| 127 | ShowPassword | ⬜ Ausstehend | Input | | | +| 128 | Signup | ⬜ Ausstehend | Feature | | Auth | +| 129 | SocialMedia | ⬜ Ausstehend | Display | | | +| 130 | SocialMediaListItem | ⬜ Ausstehend | Display | ListItem | | +| 131 | Strength | ⬜ Ausstehend | Feedback | | Password | +| 132 | SuggestionList | ⬜ Ausstehend | Display | List | | + +### T-Z +| # | Komponente | Status | Kategorie | Styleguide-Pendant | Notizen | +|---|------------|--------|-----------|-------------------|---------| +| 133 | TabNavigation | ⬜ Ausstehend | Navigation | | | +| 134 | UserTeaser | ⬜ Ausstehend | Display | Card | | +| 135 | UserTeaserHelper | ⬜ Ausstehend | Display | | | +| 136 | UserTeaserNonAnonymous | ⬜ Ausstehend | Display | | | +| 137 | UserTeaserPopover | ⬜ Ausstehend | Display | | | + +--- + +## Identifizierte Duplikate & Konsolidierung + +### Direkte Duplikate (Webapp ↔ Styleguide) +| Webapp | Styleguide | Aktion | +|--------|------------|--------| +| Logo | Logo | Konsolidieren zu OsLogo | +| Modal | Modal | Konsolidieren zu OsModal | +| BaseCard | Card | Konsolidieren zu OsCard | +| BaseIcon | Icon | Konsolidieren zu OsIcon | +| LoadingSpinner | Spinner | Konsolidieren zu OsSpinner | + +### Button-Familie (zur Konsolidierung) +| Komponente | Beschreibung | Ziel | +|------------|--------------|------| +| Button (Styleguide) | Basis-Button | OsButton | +| BaseButton | Basis-Button | → OsButton | +| CustomButton | Angepasster Button | → OsButton variant | +| ActionButton | Aktions-Button | → OsButton variant | +| HeaderButton | Header-Button | → OsButton variant | +| LabeledButton | Button mit Label | → OsButton + Label | +| MenuBarButton | Menü-Button | → OsButton variant | +| FollowButton | Follow-Aktion | Feature-spezifisch | +| GroupButton | Gruppen-Aktion | Feature-spezifisch | +| InviteButton | Einladen | Feature-spezifisch | +| LoginButton | Login | Feature-spezifisch | +| ShoutButton | Shout-Aktion | Feature-spezifisch | +| ObserveButton | Beobachten | Feature-spezifisch | +| EmotionButton | Emotion | Feature-spezifisch | +| JoinLeaveButton | Beitreten/Verlassen | Feature-spezifisch | +| MapButton | Karten-Button | Feature-spezifisch | +| MapStylesButtons | Kartenstile | Feature-spezifisch | +| CtaJoinLeaveGroup | CTA | Feature-spezifisch | +| CtaUnblockAuthor | CTA | Feature-spezifisch | + +### Modal-Familie (zur Konsolidierung) +| Komponente | Beschreibung | Ziel | +|------------|--------------|------| +| Modal (Styleguide) | Basis-Modal | OsModal | +| Modal (Webapp) | Basis-Modal | → OsModal | +| ConfirmModal | Bestätigungs-Dialog | → OsModal type="confirm" | +| DeleteUserModal | Löschen-Dialog | → OsModal type="confirm" | +| DisableModal | Deaktivieren-Dialog | → OsModal type="confirm" | +| ReleaseModal | Release-Dialog | Feature-spezifisch | +| ReportModal | Report-Dialog | Feature-spezifisch | + +### Menu-Familie (zur Konsolidierung) +| Komponente | Beschreibung | Ziel | +|------------|--------------|------| +| Menu (Styleguide) | Basis-Menu | OsMenu | +| MenuItem (Styleguide) | Menu-Item | OsMenuItem | +| HeaderMenu | Header-Navigation | → OsMenu | +| ContentMenu | Kontext-Menu | → OsMenu variant | +| ContextMenu | Kontext-Menu | → OsMenu variant | +| FilterMenu | Filter-Menu | → OsMenu variant | +| NotificationMenu | Benachrichtigungen | Feature-spezifisch | +| CategoriesMenu | Kategorien | Feature-spezifisch | +| AvatarMenu | User-Menu | Feature-spezifisch | + +--- + +## Kategorisierung + +### Basis-Komponenten (hohe Priorität) +Diese sollten zuerst migriert werden: +- Button → OsButton +- Card → OsCard +- Icon → OsIcon +- Modal → OsModal +- Input → OsInput +- Select → OsSelect +- Avatar → OsAvatar +- Spinner → OsSpinner + +### Layout-Komponenten +- Container, Flex, Grid, Page, Section, Space + +### Typography +- Heading, Text + +### Feature-Komponenten (niedrigere Priorität) +- Chat, Group, Registration, Search, etc. + +--- + +## Analyse-Protokoll + +| Datum | Bearbeiter | Aktion | Details | +|-------|------------|--------|---------| +| 2026-02-04 | Claude | Katalog erstellt | 177 Komponenten erfasst | +| 2026-02-04 | Claude | Duplikate identifiziert | Button, Modal, Menu Familien | +| 2026-02-04 | Claude | Button-Analyse | Props-Vergleich, Konsolidierungsvorschlag, Token-Extraktion | +| 2026-02-04 | Claude | Modal-Analyse | Architektur erkannt: DsModal = Basis, Feature-Modals nutzen DsModal | +| 2026-02-04 | Claude | Menu-Analyse | DsMenu, Dropdown, Feature-Menus - 3 separate Patterns identifiziert | +| 2026-02-04 | Claude | Priorisierung | 15 Komponenten in 4 Tiers priorisiert | +| 2026-02-04 | Claude | Konsolidierungsplan | 3 Phasen definiert, Token-Liste erstellt | +| 2026-02-04 | Claude | **Phase 0 abgeschlossen** | Bereit für Phase 2 (Projekt-Setup) | + +--- + +## Nächste Schritte + +1. [x] Webapp-Komponenten auflisten +2. [x] Styleguide-Komponenten auflisten +3. [x] Offensichtliche Duplikate identifizieren +4. [x] Button-Familie im Detail analysieren +5. [x] Modal-Familie im Detail analysieren +6. [x] Menu-Familie im Detail analysieren +7. [x] Priorisierung festlegen +8. [x] Konsolidierungsplan finalisieren + +--- + +**✅ Phase 0 abgeschlossen!** Weiter mit Phase 2 (Projekt-Setup). + +--- + +## Detailanalyse: Button-Familie + +### Styleguide Button (DsButton) + +**Pfad:** `styleguide/src/system/components/navigation/Button/Button.vue` + +**Props:** +| Prop | Typ | Default | Beschreibung | +|------|-----|---------|--------------| +| path | String\|Object | null | URL oder Vue Router Pfad | +| size | String | null | `small` \| `base` \| `large` | +| linkTag | String | auto | `router-link` \| `a` \| `button` | +| name | String | null | Accessibility name | +| primary | Boolean | false | Primärer Stil (grün) | +| secondary | Boolean | false | Sekundärer Stil (blau) | +| danger | Boolean | false | Danger Stil (rot) | +| hover | Boolean | false | Hover-State erzwingen | +| ghost | Boolean | false | Transparenter Hintergrund | +| icon | String | null | Icon-Name | +| right | Boolean | false | Icon rechts positionieren | +| fullwidth | Boolean | false | Volle Breite | +| loading | Boolean | false | Ladezustand | + +**Besonderheiten:** +- Automatische Link-Erkennung (router-link/a/button) +- Icon-Only Modus wenn kein Slot-Content +- Spinner bei loading +- CSS-Klassen: `ds-button`, `ds-button-primary`, etc. + +--- + +### Webapp BaseButton + +**Pfad:** `webapp/components/_new/generic/BaseButton/BaseButton.vue` + +**Props:** +| Prop | Typ | Default | Beschreibung | +|------|-----|---------|--------------| +| bullet | Boolean | false | Kleiner runder Punkt (18px) | +| circle | Boolean | false | Runder Button | +| danger | Boolean | false | Danger-Farbschema | +| filled | Boolean | false | Gefüllter Hintergrund | +| ghost | Boolean | false | Ohne Border | +| icon | String | - | Icon-Name | +| loading | Boolean | false | Ladezustand | +| size | String | 'regular' | `tiny` \| `small` \| `regular` \| `large` | +| padding | Boolean | false | Extra Padding | +| type | String | 'button' | `button` \| `submit` | +| disabled | Boolean | false | Deaktiviert | + +**Besonderheiten:** +- Kein automatischer Link-Support (nur `