# PWA Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Implementiere PWA (Progressive Web App) Features für WrestleDesk: App-Icon auf Home Screen, bessere Mobile-Optimierung, Install-Prompt für iOS/Android **Architecture:** Manifest.json für PWA-Konfiguration, Meta-Tags in HTML für iOS/Safari, CSS-Anpassungen für Mobile/Safe Areas, InstallPrompt-Component für "Add to Home Screen" **Tech Stack:** Next.js 16, Tailwind CSS, Zustand, SVG-to-PNG für Icons --- ## File Structure | File | Purpose | |------|---------| | `frontend/public/manifest.json` | PWA Manifest (Icons, Theme, Display Mode) | | `frontend/public/icon-192.png` | PWA Icon 192x192 | | `frontend/public/icon-512.png` | PWA Icon 512x512 | | `frontend/public/apple-touch-icon.png` | iOS Icon 180x180 | | `frontend/public/icon-maskable.png` | Maskable Icon für Android | | `frontend/src/app/layout.tsx` | Meta-Tags für PWA/iOS | | `frontend/src/app/globals.css` | Mobile-Optimierungen (Safe Areas, Touch Targets) | | `frontend/src/components/ui/install-prompt.tsx` | "Add to Home Screen" Banner Component | | `frontend/src/app/(dashboard)/layout.tsx` | InstallPrompt einbinden | | `frontend/package.json` | Script für dev:host hinzufügen | | `frontend/generate-icons.js` | Icon-Generierung aus SVG | | `frontend/.env.local` | API-URL auf Netzwerk-IP | --- ## Task 1: Create PWA Manifest **Files:** - Create: `frontend/public/manifest.json` - [ ] **Step 1: Create manifest.json with PWA config** ```json { "name": "WrestleDesk", "short_name": "WrestleDesk", "description": "Wrestling Club Management System", "start_url": "/", "display": "standalone", "background_color": "#070F2B", "theme_color": "#1B1A55", "orientation": "portrait", "scope": "/", "icons": [ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { "src": "/icon-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } ] } ``` - [ ] **Step 2: Verify manifest.json exists** Run: `ls -la /Volumes/T3/Opencode/WrestleDesk/frontend/public/manifest.json` Expected: File exists - [ ] **Step 3: Commit** ```bash git add frontend/public/manifest.json git commit -m "feat(pwa): add manifest.json for PWA configuration" ``` --- ## Task 2: Create App Icons **Files:** - Create: `frontend/public/icon-192.svg` (SVG template) - Create: `frontend/generate-icons.js` (Icon generator script) - Create: `frontend/public/icon-192.png` - Create: `frontend/public/icon-512.png` - Create: `frontend/public/apple-touch-icon.png` - Create: `frontend/public/icon-maskable.png` - [ ] **Step 1: Create SVG template** ```svg ``` - [ ] **Step 2: Create icon generator script** ```javascript const sharp = require('sharp'); const fs = require('fs'); const path = require('path'); const svgBuffer = fs.readFileSync(path.join(__dirname, 'public/icon-192.svg')); sharp(svgBuffer) .resize(192, 192) .png() .toFile('public/icon-192.png') .then(() => console.log('Created icon-192.png')); sharp(svgBuffer) .resize(512, 512) .png() .toFile('public/icon-512.png') .then(() => console.log('Created icon-512.png')); sharp(svgBuffer) .resize(180, 180) .png() .toFile('public/apple-touch-icon.png') .then(() => console.log('Created apple-touch-icon.png')); sharp(svgBuffer) .resize(384, 384) .extend({ top: 64, bottom: 64, left: 64, right: 64, background: { r: 27, g: 26, b: 85, alpha: 1 } }) .png() .toFile('public/icon-maskable.png') .then(() => console.log('Created icon-maskable.png')); ``` - [ ] **Step 3: Run icon generator** Run: `cd /Volumes/T3/Opencode/WrestleDesk/frontend && node generate-icons.js` Expected: All 4 PNG files created - [ ] **Step 4: Verify icons exist** Run: `ls -la /Volumes/T3/Opencode/WrestleDesk/frontend/public/*.png` Expected: icon-192.png, icon-512.png, apple-touch-icon.png, icon-maskable.png - [ ] **Step 5: Commit** ```bash git add frontend/public/icon-192.svg frontend/public/*.png frontend/generate-icons.js git commit -m "feat(pwa): add app icons (192x192, 512x512, apple-touch, maskable)" ``` --- ## Task 3: Update Layout with PWA Meta Tags **Files:** - Modify: `frontend/src/app/layout.tsx` - [ ] **Step 1: Add viewport export and PWA meta tags** ```typescript import type { Metadata, Viewport } from "next" import { Syne, DM_Sans } from "next/font/google" import "./globals.css" import { Providers } from "./providers" const syne = Syne({ variable: "--font-heading", subsets: ["latin"], weight: ["400", "500", "600", "700", "800"], }) const dmSans = DM_Sans({ variable: "--font-sans", subsets: ["latin"], weight: ["400", "500", "600", "700"], }) export const viewport: Viewport = { width: "device-width", initialScale: 1, maximumScale: 1, userScalable: false, themeColor: "#1B1A55", viewportFit: "cover", } export const metadata: Metadata = { title: "WrestleDesk", description: "Wrestling Club Management System", manifest: "/manifest.json", appleWebApp: { capable: true, statusBarStyle: "black-translucent", title: "WrestleDesk", }, icons: { icon: [ { url: "/icon-192.png", sizes: "192x192", type: "image/png" }, { url: "/icon-512.png", sizes: "512x512", type: "image/png" }, ], apple: [ { url: "/icon-192.png", sizes: "192x192", type: "image/png" }, ], }, } export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return (
WrestleDesk als App installieren
{isIOS ? (Tippe auf Teilen → "Zum Home Screen hinzufügen"
) : (Installieren für schnellen Zugriff
)}