readme und docs von der tenant app hinzugefügt

This commit is contained in:
2025-09-15 13:27:31 +02:00
parent 216ee063ff
commit 5fbb9cb240
8 changed files with 1366 additions and 0 deletions

View File

@@ -0,0 +1,318 @@
# 13 - Backend Authentication Implementation
## Overview
This document outlines the authentication requirements and implementation details for the Fotospiel tenant backend. The system uses OAuth2 with PKCE (Proof Key for Code Exchange) for secure authorization, providing tenant-specific access tokens for API operations.
## Authentication Flow
### 1. Authorization Request
- **Endpoint**: `POST /oauth/authorize`
- **Method**: GET (redirect from frontend)
- **Parameters**:
- `client_id`: Fixed client ID for tenant-admin-app (`tenant-admin-app`)
- `redirect_uri`: Frontend callback URL (e.g., `https://tenant-admin-app.example.com/auth/callback`)
- `response_type`: `code`
- `scope`: `tenant:read tenant:write` (tenant-specific scopes)
- `state`: CSRF protection state parameter
- `code_challenge`: PKCE challenge (SHA-256 hash of code verifier)
- `code_challenge_method`: `S256`
**Response**: Redirect to frontend with authorization code and state parameters.
### 2. Token Exchange
- **Endpoint**: `POST /oauth/token`
- **Method**: POST
- **Content-Type**: `application/x-www-form-urlencoded`
- **Parameters**:
- `grant_type`: `authorization_code` or `refresh_token`
- `client_id`: `tenant-admin-app`
- `code`: Authorization code (for initial exchange)
- `redirect_uri`: Same as authorization request
- `code_verifier`: PKCE verifier (plain text)
- `refresh_token`: For token refresh (optional)
**Response** (JSON):
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "def50200...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "tenant:read tenant:write"
}
```
### 3. Token Refresh
- **Endpoint**: `POST /oauth/token`
- **Method**: POST
- **Content-Type**: `application/x-www-form-urlencoded`
- **Parameters**:
- `grant_type`: `refresh_token`
- `client_id`: `tenant-admin-app`
- `refresh_token`: Current refresh token
**Response**: Same as token exchange (new access and refresh tokens).
### 4. Token Validation
- **Endpoint**: `GET /api/v1/tenant/me`
- **Method**: GET
- **Authorization**: `Bearer {access_token}`
- **Purpose**: Validate token and return tenant information
**Response** (JSON):
```json
{
"tenant_id": "tenant-uuid",
"tenant_name": "Event Photo Company",
"credits": 150,
"role": "admin",
"email": "admin@eventphoto.com"
}
```
## Security Requirements
### PKCE Implementation
- Validate `code_challenge_method` is `S256`
- Store `code_challenge` and `code_verifier` association temporarily (Redis, 5-minute expiry)
- Verify SHA-256 hash of `code_verifier` matches stored `code_challenge` during token exchange
- Reject requests without valid PKCE parameters
### State Validation
- Generate cryptographically secure state parameter
- Store state in session or Redis with 5-minute expiry
- Validate state parameter matches stored value during callback
- Reject requests with state mismatch (CSRF protection)
### Token Security
- Access tokens: JWT with 1-hour expiry, signed with RS256
- Refresh tokens: Secure random 128-character strings, stored in database
- Refresh tokens: Single-use or rotation (new refresh token with each refresh)
- Rate limiting on token endpoints (100 requests/hour per IP)
- IP binding for refresh tokens (optional for enhanced security)
### Scopes and Permissions
- `tenant:read`: Access to tenant data (events, photos, members)
- `tenant:write`: Create/update/delete tenant resources
- `tenant:admin`: Full administrative access (user management, billing)
- Token claims include `tenant_id` and `scope` for authorization
## Database Schema
### oauth_clients Table
```sql
CREATE TABLE oauth_clients (
id VARCHAR(255) PRIMARY KEY,
client_id VARCHAR(255) UNIQUE NOT NULL,
client_secret VARCHAR(255),
redirect_uris TEXT,
scopes TEXT DEFAULT 'tenant:read tenant:write',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
### oauth_codes Table (Temporary)
```sql
CREATE TABLE oauth_codes (
id VARCHAR(255) PRIMARY KEY,
client_id VARCHAR(255) NOT NULL,
user_id VARCHAR(255) NOT NULL,
code VARCHAR(255) UNIQUE NOT NULL,
code_challenge VARCHAR(255) NOT NULL,
state VARCHAR(255),
redirect_uri VARCHAR(255),
scope TEXT,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_code (code),
INDEX idx_expires (expires_at)
);
```
### refresh_tokens Table
```sql
CREATE TABLE refresh_tokens (
id VARCHAR(255) PRIMARY KEY,
tenant_id VARCHAR(255) NOT NULL,
token VARCHAR(255) UNIQUE NOT NULL,
access_token VARCHAR(255),
expires_at TIMESTAMP NOT NULL,
scope TEXT,
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
revoked_at TIMESTAMP NULL,
INDEX idx_tenant (tenant_id),
INDEX idx_token (token),
INDEX idx_expires (expires_at)
);
```
### tenant_tokens Table (JWT Blacklist)
```sql
CREATE TABLE tenant_tokens (
id VARCHAR(255) PRIMARY KEY,
tenant_id VARCHAR(255) NOT NULL,
jti VARCHAR(255) UNIQUE NOT NULL, -- JWT ID
token_type VARCHAR(50) NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_jti (jti),
INDEX idx_tenant (tenant_id),
INDEX idx_expires (expires_at)
);
```
## API Endpoints
### Authentication Endpoints
| Endpoint | Method | Description | Authentication |
|----------|--------|-------------|----------------|
| `/oauth/authorize` | GET | Authorization request | None |
| `/oauth/token` | POST | Token exchange/refresh | None |
| `/api/v1/tenant/me` | GET | Validate token | Bearer Token |
### Protected Endpoints
All tenant API endpoints require `Authorization: Bearer {access_token}` header.
#### Token Validation Middleware
```javascript
// Pseudocode
function validateTenantToken(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'Missing token' });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const { tenant_id, scope, exp, jti } = decoded;
// Check if token is blacklisted
const blacklisted = await db.query('SELECT * FROM tenant_tokens WHERE jti = ? AND revoked_at IS NULL', [jti]);
if (blacklisted.length > 0) {
return res.status(401).json({ error: 'Token revoked' });
}
// Check expiry
if (Date.now() >= exp * 1000) {
return res.status(401).json({ error: 'Token expired' });
}
// Set tenant context
req.tenant = { id: tenant_id, scope };
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}
```
## Environment Variables
### Backend (.env)
```
JWT_SECRET=your-super-secret-jwt-signing-key
API_BASE_URL=https://api.fotospiel.com
OAUTH_CLIENT_SECRET=your-oauth-client-secret
DATABASE_URL=your-database-connection-string
REDIS_URL=redis://localhost:6379
```
### Frontend (.env)
```
VITE_API_URL=https://api.fotospiel.com
VITE_OAUTH_CLIENT_ID=tenant-admin-app
```
## Error Handling
### Common Error Responses
```json
// 400 Bad Request
{
"error": "invalid_request",
"error_description": "Missing required parameter: code"
}
// 401 Unauthorized
{
"error": "invalid_token",
"error_description": "Token signature invalid"
}
// 403 Forbidden
{
"error": "insufficient_scope",
"error_description": "Scope tenant:write required"
}
```
## Implementation Notes
### 1. PKCE Storage
- Use Redis for temporary code_challenge storage (5-minute TTL)
- Key format: `pkce:{code_challenge}:{client_id}`
- Value: JSON with `code_verifier`, `user_id`, `redirect_uri`, `scope`
### 2. Refresh Token Rotation
- Issue new refresh token with each refresh
- Revoke old refresh token immediately
- Limit refresh tokens per tenant to 5 active
### 3. Rate Limiting
- Authorization requests: 10/minute per IP
- Token exchanges: 5/minute per IP
- Token validation: 100/minute per tenant
### 4. Logging and Monitoring
- Log all authentication attempts (success/failure)
- Monitor token usage patterns
- Alert on unusual activity (multiple failed attempts, token anomalies)
- Track refresh token usage for security analysis
### 5. Database Cleanup
- Cron job to remove expired authorization codes (daily)
- Remove expired refresh tokens (weekly)
- Clean blacklisted tokens after expiry (daily)
## Testing Requirements
### Unit Tests
- PKCE generation and validation
- State parameter security
- Token signing and verification
- Scope validation middleware
### Integration Tests
- Complete OAuth2 flow (authorize → token → validate)
- Token refresh cycle
- Error scenarios (invalid code, expired tokens, state mismatch)
- Concurrent access testing
### Security Tests
- CSRF protection validation
- PKCE bypass attempts
- Token replay attacks
- Rate limiting enforcement
## Deployment Considerations
### 1. Secrets Management
- Store JWT secret and OAuth client secret in secure vault (AWS Secrets Manager, HashiCorp Vault)
- Rotate secrets every 90 days
- Use different secrets for dev/staging/production
### 2. Certificate Management
- Use Let's Encrypt or commercial SSL certificates
- Rotate certificates before expiry
- Enable HSTS headers
### 3. Monitoring
- Track authentication success/failure rates
- Monitor token expiry patterns
- Alert on PKCE validation failures
- Log all security-related events
This implementation provides secure, scalable authentication for the Fotospiel tenant system, following OAuth2 best practices with PKCE for public clients.

View File

@@ -0,0 +1,183 @@
# 14 - Freemium Business Model Implementation
## Executive Summary
This document details the Freemium business model for the Fotospiel tenant app, combining free access with in-app purchases for event credits. The model prioritizes user acquisition through a free app download while monetizing through value-driven upgrades. Key metrics: 5-10% conversion rate, ARPU €10-15, scalable to 100k+ users.
## Model Analysis
### Model A: Paid App (€4.99) with Free First Event
**Pros:**
- Higher paying user conversion (20-30% retention)
- Premium perception for professional users
- Stable revenue from app sales (70% net after store fees)
- No freeloader problem
**Cons:**
- Lower acquisition (<5% download conversion)
- High churn if first event doesn't impress
- Slower scaling, higher marketing costs per install (€2-5)
### Model B: Free App with In-App Purchases (Recommended Base)
**Pros:**
- 10x more downloads, viral potential
- Low acquisition costs (€0.50-1 per install)
- Flexible pricing (starter €4.99, pro €9.99)
- Better App Store ranking for free apps
**Cons:**
- 80-95% freeloader rate
- Complex IAP setup and testing
- Requires strong onboarding to drive conversions
### Hybrid Freemium Recommendation
**Core Strategy:** Free app with limited first event (50 photos, basic features), unlimited upgrades via IAP credits/subscriptions.
**Pricing Structure:**
- **Free Tier:** 1 basic event (50 photos, standard tasks, no custom branding)
- **Consumable Credits:**
- Starter Pack: €4.99 for 5 events (100 photos each)
- Pro Pack: €14.99 for 20 events (unlimited photos)
- **Subscriptions:**
- Pro Unlimited: €4.99/month (all features, unlimited events)
- Agency: €19.99/month (multi-tenant, analytics, white-label)
- **Non-Consumables:** Lifetime Unlimited: €49.99 (one-time purchase)
**Expected Metrics:**
- Downloads: 50k/year
- Conversion: 5-8% (2,500-4,000 paying users)
- ARPU: €12 (mix of one-time + recurring)
- Annual Revenue: €30,000-48,000 (after 30% store fees)
## User Acquisition Strategy
### App Store Optimization (ASO)
- **Keywords:** "Event Foto App", "Hochzeit Galerie", "Party Foto Sharing", "Event Planner kostenlos"
- **Screenshots:** 5-6 screens showing value (event creation, photo upload, QR sharing)
- **Description:** 4,000 chars emphasizing "Free start, upgrade anytime"
- **App Preview Video:** 30s demo of creating/sharing first event
### Marketing Channels
- **Paid Ads:** Facebook/Instagram targeting wedding planners (€1-2/install)
- **Organic:** Wedding forums, photographer communities, local event groups
- **Partnerships:** Affiliate program for event agencies (20% commission)
- **Content Marketing:** Blog posts "How to create digital photo galleries for events"
### Retention & Re-engagement
- **Push Notifications:** "Your event ends soon - extend for €2.99" (opt-in only)
- **Email Marketing:** Post-event surveys with upgrade offers
- **In-App Messaging:** Contextual upgrade prompts after value demonstration
## Conversion Optimization
### Onboarding Funnel
1. **Download → First Launch:** 90% completion target
2. **Onboarding Wizard → First Event:** 80% completion
3. **First Event Success → Upgrade Prompt:** 10% conversion
4. **Repeat Usage → Subscription:** 20% of one-time buyers
### A/B Testing Plan
- **CTA Text:** "Get Started Free" vs "Create First Event"
- **Pricing Display:** Static prices vs dynamic local currency
- **Upgrade Timing:** After first photo upload vs after event sharing
- **Tools:** Firebase Remote Config + RevenueCat Experiments
## Technical Requirements
### Frontend (React/Capacitor)
- **IAP Integration:** RevenueCat SDK for cross-platform purchases
- **State Management:** Context API for credits, React Query for API sync
- **Offline Support:** Capacitor Storage for temporary event data
- **Analytics:** Firebase for funnel tracking, RevenueCat for purchase events
### Backend API Extensions
- **Credit Management:** `/api/v1/tenant/credits` endpoints
- **Purchase Validation:** Webhook receiver from RevenueCat
- **Event Limiting:** Middleware checking credit balance before creation
- **Subscription Sync:** Real-time updates via WebSockets (optional)
### Database Schema Additions
```sql
-- tenant_credits table
CREATE TABLE tenant_credits (
tenant_id VARCHAR(255) PRIMARY KEY,
balance INTEGER DEFAULT 1, -- 1 free event
total_purchased INTEGER DEFAULT 0,
subscription_active BOOLEAN DEFAULT FALSE,
subscription_tier VARCHAR(50),
last_sync TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- purchase_history table
CREATE TABLE purchase_history (
id VARCHAR(255) PRIMARY KEY,
tenant_id VARCHAR(255) NOT NULL,
package_id VARCHAR(255) NOT NULL,
credits_added INTEGER,
price DECIMAL(10,2),
currency VARCHAR(3),
platform VARCHAR(50), -- 'ios' or 'android'
transaction_id VARCHAR(255),
purchased_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);
```
## Risk Mitigation
### Store Compliance
- **No Paywalls Before Value:** First event creation always free
- **Clear Descriptions:** App Store screenshots show free tier capabilities
- **Receipt Validation:** All purchases server-side verified
- **Refund Policy:** 14-day money-back for subscriptions
### Technical Risks
- **IAP Failures:** Fallback to manual credit allocation via support
- **Sync Issues:** Offline-first approach, sync on reconnect
- **Store Rejections:** Beta testing with TestFlight/Internal Testing
- **Revenue Recognition:** Use RevenueCat for proper accounting
### Business Risks
- **Low Conversion:** Monthly A/B testing and user surveys
- **Churn:** Re-engagement campaigns for inactive users
- **Competition:** Regular competitor analysis and feature updates
- **Platform Changes:** Monitor Apple/Google policy updates
## Launch Timeline
### Phase 1: MVP (4 weeks)
- Week 1: RevenueCat integration, basic credit system
- Week 2: IAP store page, purchase flow
- Week 3: Backend endpoints, database schema
- Week 4: Testing, beta distribution
### Phase 2: Optimization (Ongoing)
- Month 1: A/B testing of upgrade prompts
- Month 2: Push notifications, email marketing
- Month 3: Subscription model introduction
- Quarterly: Feature updates based on user feedback
## Success Metrics
### Acquisition
- Downloads: 10k in first 3 months
- Cost per Install: <€1.50
- App Store Rating: >4.5 stars
### Conversion
- Free → Paid: 5% within 30 days
- First Event → Upgrade: 15% conversion
- Average Order Value: €8-12
### Retention
- Day 1 Retention: >40%
- Day 7 Retention: >25%
- Monthly Active Users: 20% of downloads
### Revenue
- Month 1 Revenue: €1,000-2,000
- ARPU: €0.50-1.00 overall
- LTV >3x acquisition cost
This Freemium model balances user acquisition with sustainable revenue growth, leveraging the event-based nature of the app for recurring purchases while maintaining an accessible entry point.

View File

@@ -0,0 +1,43 @@
# Detaillierte PRP für Tenant Admin App (Capacitor + Framework7)
## Status
- **Aktiv**: Erste Version (2025-09-13)
- **Version**: 1.0.0
- **Autor**: Sonoma (AI Architect)
- **Supersedes**: docs/prp/06-tenant-admin-pwa.md (erweitert und detailliert)
## Überblick
Diese detaillierte Product Requirement Plan (PRP) beschreibt die Spezifikationen für die Tenant Admin App. Die App ist eine store-ready mobile Anwendung, die mit Capacitor für iOS und Trusted Web Activity (TWA) für Android gepackt wird. Die UI basiert auf Framework7 für ein natives Mobile-Erlebnis. Die App ermöglicht Tenant-Admins (z.B. Event-Organisatoren) die vollständige Verwaltung ihrer Events, Galerien, Mitglieder, Einstellungen und Käufe über eine API-first Backend-Integration.
Die App ersetzt das frühere Filament-basierte Tenant-Panel und fokussiert auf Mobile-First-UX mit Offline-Fähigkeiten, Push-Notifications und sicherer Authentifizierung. Sie respektiert das Multi-Tenancy-Modell und GDPR-Anforderungen.
## Kernziele
- **Deliverables**: Voll funktionsfähige App mit CRUD-Operationen für Tenant-Ressourcen (Events, Photos, Tasks, etc.).
- **UI/UX**: Framework7-Komponenten für konsistente, native Mobile-Interfaces (iOS/Android).
- **Technologie-Stack**: React/Vite (Core), Framework7 (UI), Capacitor (Native), OAuth2 + PKCE (Auth).
- **Distribution**: App Store (iOS), Google Play (Android), PWA-Install (Web).
## Struktur dieser PRP
- **README.md**: Dieser Überblick.
- **functional-specs.md**: Funktionale Anforderungen, Capabilities und API-Integration.
- **pages-ui.md**: Detaillierte Seitenbeschreibungen, Framework7-Komponenten und Wireframe-Ideen.
- **settings-config.md**: App- und Tenant-spezifische Einstellungen, Capacitor-Plugins.
- **capacitor-setup.md**: Packaging, Distribution und Native-Features.
## Referenzen
- **Haupt-PRP**: docs/prp/README.md
- **Tenancy**: docs/prp/02-tenancy.md
- **API**: docs/prp/03-api.md
- **Bestehende Tenant PWA**: docs/prp/06-tenant-admin-pwa.md
- **Addendum**: docs/prp-addendum-2025-09-08-tenant-admin-pwa.md
- **ADR**: docs/adr/ADR-0006-tenant-admin-pwa.md
- **Billing**: docs/prp/08-billing.md
- **Glossar**: docs/prp/99-glossary.md
## Änderungen und Erweiterungen
Diese PRP erweitert die knappe Beschreibung in 06-tenant-admin-pwa.md um:
- Spezifische Seiten und UI-Elemente mit Framework7.
- Detaillierte Settings und Capacitor-Integration.
- Mobile-spezifische Features wie Push-Notifications und Offline-Sync.
Für Feedback oder Änderungen: Siehe TODO.md oder Issues.

View File

@@ -0,0 +1,257 @@
# API-Nutzung der Tenant Admin App
Diese Dokumentation beschreibt alle API-Endpunkte, die die Tenant Admin App mit der Backend-Hauptapp kommuniziert. Alle Requests sind tenant-scoped und erfordern JWT-Authentifizierung.
## Authentifizierung
### OAuth2 Flow (PKCE)
- **Start**: `GET /oauth/authorize` (Browser-Redirect mit PKCE-Challenge)
- **Params**: `client_id`, `redirect_uri`, `scope=tenant:read tenant:write`, `state`, `code_challenge`, `code_challenge_method=S256`
- **Response**: Authorization Code
- **Token Exchange**: `POST /oauth/token`
- **Body**: `grant_type=authorization_code`, `client_id`, `code`, `redirect_uri`, `code_verifier`
- **Response**: `{ access_token, refresh_token, expires_in, token_type }`
- **Headers**: `Content-Type: application/x-www-form-urlencoded`
- **Token Refresh**: `POST /oauth/token`
- **Body**: `grant_type=refresh_token`, `client_id`, `refresh_token`
- **Response**: Neuer Access/Refresh-Token
- **Token Validation**: `GET /api/v1/tenant/me`
- **Headers**: `Authorization: Bearer {access_token}`
- **Response**: `{ id, email, tenant_id, role, name }`
## Dashboard
### Stats laden
- **GET /api/v1/tenant/dashboard**
- **Headers**: `Authorization: Bearer {token}`
- **Response**: `{ credits, active_events, new_photos, task_progress }`
- **Zweck**: Übersicht-Daten für Dashboard-Cards
## Events
### Events-Liste
- **GET /api/v1/tenant/events**
- **Headers**: `Authorization: Bearer {token}`
- **Params**:
- `page=1` (Pagination)
- `per_page=50` (max für Mobile)
- `status=draft|active|archived` (Filter)
- `search=query` (Suche in Titel/Ort)
- **Response**: `{ data: Event[], current_page, last_page, total }`
- **Event-Shape**: `{ id, title, date, location, status, photoCount, slug }`
### Event erstellen
- **POST /api/v1/tenant/events**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
- **Body**: `{ title, date, location, description }`
- **Response**: 201 Created mit erstelltem Event
- **Validierung**: Prüft Credit-Balance (1 Credit pro Event)
### Event-Details
- **GET /api/v1/tenant/events/{id}**
- **Headers**: `Authorization: Bearer {token}`
- **Response**: Erweitertes Event mit `{ tasks[], members, stats { likes, views, uploads } }`
### Event updaten
- **PATCH /api/v1/tenant/events/{id}**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`, `If-Match: {etag}`
- **Body**: Partial Event-Daten (title, date, location, description)
- **Response**: Updated Event
### Event archivieren
- **DELETE /api/v1/tenant/events/{id}**
- **Headers**: `Authorization: Bearer {token}`, `If-Match: {etag}`
- **Response**: 204 No Content (soft-delete)
## Photos
### Photos laden
- **GET /api/v1/tenant/events/{event_id}/photos**
- **Headers**: `Authorization: Bearer {token}`
- **Params**:
- `page=1`, `per_page=50`
- `status=pending|approved|rejected|featured`
- `sort=date|likes`
- `since=cursor` (für Infinite Scroll)
- **Response**: `{ data: Photo[], current_page, last_page }`
- **Photo-Shape**: `{ id, eventId, url, thumbnail, uploadedAt, status, likes, views, uploader, etag }`
### Upload-URL anfordern
- **POST /api/v1/tenant/events/{event_id}/photos**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
- **Body**: `{ file_name, description? }`
- **Response**: `{ id, upload_url (S3 signed), thumbnail_url }`
### Photo moderieren
- **PATCH /api/v1/tenant/photos/{id}**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`, `If-Match: {etag}`
- **Body**: `{ status: 'approved'|'rejected'|'featured', featured?, reason? }`
- **Response**: Updated Photo
### Photo löschen
- **DELETE /api/v1/tenant/photos/{id}**
- **Headers**: `Authorization: Bearer {token}`, `If-Match: {etag}`
- **Response**: 204 No Content
## Members
### Mitglieder laden
- **GET /api/v1/tenant/events/{event_id}/members**
- **Headers**: `Authorization: Bearer {token}`
- **Params**: `page`, `per_page`, `status=pending|active|invited`
- **Response**: `{ data: Member[], current_page, last_page }`
- **Member-Shape**: `{ id, name, email, role, joinedAt, avatar?, status }`
### Mitglied einladen
- **POST /api/v1/tenant/events/{event_id}/members**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
- **Body**: `{ email, role: 'member'|'guest', name? }`
- **Response**: 201 Created, E-Mail wird versendet
## Tasks
### Tasks laden
- **GET /api/v1/tasks**
- **Headers**: `Authorization: Bearer {token}`
- **Params**:
- `global=true/false` (globale vs. tenant Tasks)
- `tenant_id=me` (nur eigene Tasks)
- `category=setup|photo|event|cleanup`
- **Response**: `{ data: Task[], global: boolean }`
- **Task-Shape**: `{ id, title, description?, category, isGlobal, tenantId?, createdAt, color? }`
### Event-Tasks laden
- **GET /api/v1/tenant/events/{event_id}/tasks**
- **Headers**: `Authorization: Bearer {token}`
- **Response**: `{ data: EventTask[], overall_progress }`
- **EventTask-Shape**: `{ id, eventId, taskId, task: Task, order, completed, assignedTo?, progress }`
### Tasks bulk zuweisen
- **POST /api/v1/tenant/events/{event_id}/tasks/bulk**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
- **Body**: `{ task_ids: string[], order: number[] }`
- **Response**: Updated EventTasks mit neuer Reihenfolge
## Settings
### Settings laden
- **GET /api/v1/tenant/settings**
- **Headers**: `Authorization: Bearer {token}`
- **Response**: `{ primaryColor, tenantName, maxEventsPerMonth, enableTasks, enableEmotions, legalPages { impressumUrl, privacyUrl } }`
### Settings updaten
- **PATCH /api/v1/tenant/settings**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
- **Body**: Partial Settings-Daten
- **Response**: Updated Settings
## Billing
### Balance laden
- **GET /api/v1/tenant/credits/balance**
- **Headers**: `Authorization: Bearer {token}`
- **Response**: `{ balance: number }`
### Ledger-Verlauf
- **GET /api/v1/tenant/ledger**
- **Headers**: `Authorization: Bearer {token}`
- **Params**: `page`, `per_page` (Pagination)
- **Response**: `{ data: LedgerEntry[], current_page, last_page }`
- **LedgerEntry**: `{ id, type, amount, credits, date, description, transactionId? }`
### Kauf-Intent erstellen
- **POST /api/v1/tenant/purchases/intent**
- **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
- **Body**: `{ package_id }`
- **Response**: `{ checkout_url: string }` (Stripe-Checkout)
- **Nach dem Kauf**: Webhook-Handling auf Backend für Balance-Update
## Allgemeine Headers
Alle API-Requests enthalten:
- **Authorization**: `Bearer {access_token}` (JWT mit tenant_id)
- **Content-Type**: `application/json` (für POST/PATCH)
- **If-Match**: `{etag}` (für Concurrency-Control bei Updates)
- **Accept**: `application/json`
## Error-Handling
**Standard-Error-Response:**
```json
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Nicht genügend Credits verfügbar"
}
}
```
**HTTP Status Codes:**
- 200: Erfolg
- 201: Created
- 400: Bad Request (Validierungsfehler)
- 401: Unauthorized (Token-Refresh wird versucht)
- 403: Forbidden (RBAC-Verletzung)
- 404: Not Found
- 422: Unprocessable Entity
- 429: Rate Limited (Client retry mit Backoff)
## Pagination
Alle Listen-Endpunkte unterstützen:
- **page**: Aktuelle Seite (default 1)
- **per_page**: Einträge pro Seite (default 20, max 50 für Mobile)
- **Response**: `{ data: [], current_page, last_page, per_page, total }`
## Headers für Concurrency
Mutierende Endpunkte (PATCH/DELETE) erfordern:
- **If-Match**: `{etag}` aus GET-Response
- **Response**: 412 Precondition Failed bei Conflict
## Sicherheit
- **Tenant-Isolation**: Alle Endpunkte prüfen JWT-tenant_id gegen request tenant_id
- **RBAC**: Nur tenant_admin kann mutieren, member kann nur lesen/hochladen
- **Rate Limiting**: 100 Requests/Minute pro Tenant
- **ETag**: Automatische Concurrency-Control
- **No PII-Logging**: Keine sensiblen Daten in Logs
## Testing
### API-Test-Setup
1. **Backend starten**: Stelle sicher, dass die Hauptapp läuft und Endpunkte verfügbar sind.
2. **Token erzeugen**: Verwende Postman mit gültigem Access-Token.
3. **Endpoints testen**: Jeder Endpunkt einzeln mit curl oder Postman.
### Beispiel curl (mit Token)
```bash
curl -H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
https://api.fotospiel.com/api/v1/tenant/events
```
### Frontend-Test
1. `.env` mit korrekter API-URL konfigurieren.
2. `npm run dev` starten.
3. Browser-Network-Tab überprüfen für API-Calls.
## Deployment
### Environment-Variablen
- **REACT_APP_API_URL**: Backend-API-URL (Pflicht)
- **REACT_APP_OAUTH_CLIENT_ID**: OAuth-Client-ID (Pflicht)
### Build & Deploy
1. **Development**: `npm run dev`
2. **Production**: `npm run build`
3. **Vorschau**: `npm run preview`
### PWA-Installation
- App ist PWA-fähig (Manifest, Service Worker).
- Installierbar auf Desktop/Mobile via "Zum Startbildschirm hinzufügen".
Für weitere Details siehe die spezifischen Dokumentationsdateien.

View File

@@ -0,0 +1,155 @@
# Capacitor Setup für die Tenant Admin App
## Status
- **Version**: 1.0.0 (2025-09-13)
- **Fokus**: Mobile Packaging, Distribution und Native-Integration basierend auf ADR-0006.
## Überblick
Die Tenant Admin App wird als Progressive Web App (PWA) entwickelt und mit Capacitor für native Mobile-Distribution gepackt. Dies ermöglicht:
- **Android**: Trusted Web Activity (TWA) für Chrome-basierte Installation (Google Play) oder voller Capacitor-Build bei Bedarf an Native APIs.
- **iOS**: Capacitor-App für App Store-Distribution mit Push-Notifications und Keychain-Support.
- **Web/PWA**: Fallback für Browser-Installation (A2HS) mit Service Worker.
Der Build-Prozess integriert sich in das bestehende Vite-Setup (resources/js/admin). Nach `npm run build` wird `npx cap sync` ausgeführt, um die Web-Assets in native Projekte zu kopieren.
## Projektstruktur
```
apps/admin-pwa/ # React/Vite Source (Haupt-App)
├── src/ # Components, Pages, API
├── vite.config.ts # Framework7 + Capacitor-Integration
├── capacitor.config.ts # Native Config
├── android/ # Capacitor Android-Projekt (TWA oder App)
├── ios/ # Capacitor iOS-Projekt (Xcode)
└── package.json # Dependencies (framework7, @capacitor/*)
packages/mobile/ # Shared Native-Config (optional)
├── fastlane/ # iOS/Android Deployment
├── assetlinks.json # TWA Digital Asset Links
└── privacy-manifest.json # iOS Privacy
```
## Packaging und Build-Prozess
### 1. Vite + Framework7 Setup
- **Dependencies**: `framework7`, `framework7-react`, `@capacitor/core`, `@capacitor/cli`.
- **vite.config.ts** (Auszug):
```typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { framework7 } from 'framework7/vite';
export default defineConfig({
plugins: [react(), framework7()],
build: {
outDir: 'dist',
sourcemap: true,
},
define: {
__CAPACITOR__: true, // Enable Capacitor globals
},
});
```
- **Build-Skript** (package.json): `"build": "vite build && npx cap sync"`.
### 2. Capacitor Initialisierung
- **Befehle**:
```
npx cap init com.fotospiel.tenantadmin "Event Photo Admin"
npx cap add android
npx cap add ios
npx cap sync # Kopiert dist/ in native Projekte
```
- **capacitor.config.ts** (siehe settings-config.md für erweiterte Plugins).
### 3. Android Packaging (TWA bevorzugt)
- **Trusted Web Activity (TWA)**: Für store-ready Distribution ohne vollen Native-Wrapper.
- **Voraussetzungen**: App bound an `admin.fotospiel.app` (HTTPS); Digital Asset Links.
- **assetlinks.json** (public/.well-known/assetlinks.json):
```json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.fotospiel.tenantadmin",
"sha256_cert_fingerprints": ["DE:AD:BE:EF:..."] // Von Play Console
}
}]
```
- **Build**: `npx cap open android` → Android Studio → Generate Signed Bundle (AAB für Play Store).
- **Vorteile**: Kleiner Footprint, Web-Push via Chrome; Fallback zu Capacitor bei Bedarf (z.B. Biometrie).
- **TWA-Tools**: `npm i -g @bubblewrap/cli``bubblewrap init --manifest=https://admin.fotospiel.app/manifest.json`.
- **Vollständiger Capacitor-Build** (falls Native-Plugins benötigt):
- `npx cap sync android`
- `npx cap open android` → Build APK/AAB mit ProGuard.
### 4. iOS Packaging (Capacitor)
- **Xcode-Setup**: `npx cap open ios` → Signing mit Apple Developer Account.
- **Privacy Manifest** (ios/App/App/PrivacyInfo.xcprivacy):
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string> <!-- Preferences für Settings -->
</array>
</dict>
<!-- Push, Camera, etc. -->
</array>
</dict>
</plist>
```
- **Build**: Archive in Xcode → Upload zu App Store Connect.
- **Entitlements**: Push-Notifications (APNs), Background App Refresh für Sync.
### 5. Distribution und CI/CD
- **Google Play**:
- **TWA**: Internal Testing Track; Target SDK 34+; Feature: Installable.
- **Fastlane**: `packages/mobile/fastlane/android` mit `supply` für Metadata/Screenshots.
- **Versioning**: Align mit Backend (z.B. 1.0.0); Feature Flags via API.
- **Apple App Store**:
- **Capacitor**: Review-Guidelines beachten (HTTPS-only, No Crash-Reporting ohne Consent).
- **Fastlane**: `packages/mobile/fastlane/ios` mit `deliver` für Upload.
- **Privacy**: Usage Descriptions in Info.plist (z.B. "Kamera für QR-Scans").
- **PWA-Fallback** (Web):
- **manifest.json**: `start_url: '/admin/'`, `display: 'standalone'`.
- **Service Worker**: Caching von Assets; Background Sync für Mutations.
- **Distribution**: Hosting auf `admin.fotospiel.app` mit A2HS-Prompt.
### Native Features (Erweiterung zu settings-config.md)
- **Push-Notifications**:
- **Android**: FCM-Integration; Token an Backend (`POST /tenant/push-tokens`).
- **iOS**: APNs; Silent-Pushes für Sync-Triggers.
- **Cross-Platform**: Capacitor Push-Plugin handhabt Plattform-Unterschiede.
- **Secure Storage**:
- **Tokens**: Capacitor Preferences mit Auto-Encryption.
- **Offline Data**: IndexedDB (Web) + Native Storage; Encryption via Web Crypto API.
- **Background Processing**:
- **Sync**: Capacitor App-State-Listener für Foreground/Background; Queue Mutations.
- **iOS**: Background Fetch (via Plugin); Android: WorkManager für periodische Syncs.
- **Biometrie (optional)**: `@capacitor-community/biometric-auth` für App-Lock.
### Testing und Deployment
- **E2E-Tests**: Cypress für Web; Detox/Appium für Native (z.B. Push-Handling).
- **CI/CD**: GitHub Actions oder Gogs-Integration (siehe docs/prp/11-ops-ci-cd.md).
- Steps: Build → Test → Cap Sync → Fastlane Deploy (Staging → Production).
- **Version Alignment**: App-Version matcht Backend-API-Version; Changelog in Store-Listing.
### Constraints & Red-Lines
- **GDPR**: Keine implizite Tracking; Explizite Consent für Push/Camera.
- **Security**: HTTPS-only; Token-Rotation alle 24h; No Jailbreak-Detection.
- **Performance**: Bundle-Size < 10MB (Web-Assets komprimiert); Lazy-Loading.
Diese Setup ergänzt die funktionalen Specs und UI-Beschreibungen. Für Repo-Integration siehe ADR-0006.

View File

@@ -0,0 +1,100 @@
# Funktionale Spezifikationen für die Tenant Admin App
## Status
- **Version**: 1.0.0 (2025-09-13)
- **Supersedes**: Teile von docs/prp/06-tenant-admin-pwa.md und docs/prp-addendum-2025-09-08-tenant-admin-pwa.md
## Deliverables
Die Tenant Admin App muss folgende Kernfunktionen bereitstellen:
- **Event-Management**: CRUD-Operationen für Events (Erstellen, Bearbeiten, Archivieren, Veröffentlichen).
- **Gallery-Management**: Hochladen, Moderieren, Featured-Setten von Photos; Thumbnail-Generierung.
- **Member-Management**: Hinzufügen/Entfernen von Event-Mitgliedern; Rollen (Admin, Member).
- **Task & Emotion Management**: Zuweisen von Tasks und Emotions zu Events; Overrides für Tenant-spezifische Bibliotheken.
- **Settings-Management**: Tenant-spezifische Einstellungen (Theme, Limits, Legal Pages).
- **Billing & Purchases**: Kaufen von Event-Credits; Ledger-Übersicht; Integration mit Stripe.
- **Notifications**: Push-Benachrichtigungen für neue Photos, Event-Updates, niedrigen Credit-Balance.
- **Offline-Support**: Caching von Events und Photos; Queuing von Uploads/Mutations mit Sync bei Online-Wiederkehr.
- **Audit & Compliance**: Logging kritischer Aktionen; ETag-basierte Conflict-Resolution; GDPR-konforme Datenlöschung.
Die App ist API-first und interagiert ausschließlich über den Backend-API-Endpunkt `/api/v1/tenant/*`.
## Capabilities
### Authentifizierung & Autorisierung
- **OAuth2 Flow**: Authorization Code + PKCE für sichere Token-Erfassung.
- **Token-Management**: Refresh-Tokens mit automatischer Rotation; Secure Storage (Keychain/Keystore).
- **Tenant-Scoping**: Alle Requests enthalten `tenant_id` aus Token; Backend-Policies enforcen Isolation.
- **Roles**: Unterstützung für `tenant_admin` (volle CRUD), `member` (read-only + Uploads).
### Core Features
- **Event Lifecycle**:
- Erstellen: Erfordert mind. 1 Event-Credit; Slug-Generierung (unique pro Tenant).
- Bearbeiten: Update von Datum, Ort, Tasks, Emotions, Join-Link.
- Veröffentlichen: Generiert QR-Code und Share-Link; aktiviert Guest-PWA-Zugriff.
- Archivieren: Soft-Delete mit Retention-Periode (GDPR); Credit-Rückerstattung optional.
- **Photo Management**:
- Upload: Signed URLs für direkte S3-Uploads; automatisierte Thumbnail-Generierung.
- Moderation: Approve/Reject/Feature; Bulk-Operations.
- Analytics: Stats zu Likes, Views, Uploads pro Event.
- **Task & Emotion System**:
- Bibliothek: Globale + Tenant-spezifische Tasks/Emotions.
- Zuweisung: Drag-and-Drop zu Events; Fortschritts-Tracking.
- **Billing Integration**:
- Credit-Balance: Anzeige und Kauf von Packs (z.B. 5 Events für 29€).
- Ledger: Historie von Käufen, Consumptions, Refunds.
- Stripe-Checkout: Server-side Intent-Erstellung; Webhook-Handling für Confirmation.
### Offline & Sync
- **Service Worker**: Cache von App-Shell, Events, Photos (Cache-Control: max-age=5min für dynamische Daten).
- **Background Sync**: Queued Mutations (z.B. Photo-Approvals) syncen bei Connectivity.
- **Conflict Resolution**: ETag/If-Match Headers; Optimistic Updates mit Rollback bei Conflicts.
### Error Handling & UX
- **Rate Limits**: 429-Responses handhaben mit Retry-Logic und User-Feedback ("Zu viele Anfragen, versuche es später").
- **Offline Mode**: Degradiertes UI (Read-Only); Sync-Status-Indikator.
- **i18n**: Unterstützung für de/en; Locale aus User-Profile.
## API-Integration
Die App konsumiert den API-Contract aus docs/prp/03-api.md. Schlüssel-Endpunkte:
### Auth
- `POST /oauth/token`: PKCE-Code für Access/Refresh-Token.
- `POST /oauth/token/refresh`: Token-Rotation.
### Events
- `GET /tenant/events`: Liste (paginiert, filterbar nach Status/Datum).
- `POST /tenant/events`: Erstellen (validiert Credit-Balance).
- `GET /tenant/events/{id}`: Details inkl. Tasks, Stats.
- `PATCH /tenant/events/{id}`: Update (ETag für Concurrency).
- `DELETE /tenant/events/{id}`: Archivieren.
### Photos
- `GET /tenant/events/{event_id}/photos?since={cursor}`: Inkrementelle Liste.
- `POST /tenant/events/{event_id}/photos`: Metadata + signed Upload-URL.
- `PATCH /tenant/photos/{id}`: Moderation (approve, feature).
- `DELETE /tenant/photos/{id}`: Löschung mit Audit.
### Tasks & Emotions
- `GET /tenant/tasks`: Tenant-Overrides + globale Bibliothek.
- `POST /tenant/events/{id}/tasks`: Zuweisung.
- Ähnlich für Emotions.
### Settings
- `GET /tenant/settings`: Tenant-Konfig (Theme, Limits, Legal-Links).
- `PATCH /tenant/settings`: Update.
### Billing
- `GET /tenant/ledger`: Credit-Historie.
- `POST /tenant/purchases/intent`: Stripe-Checkout-Session erstellen.
- `GET /tenant/credits/balance`: Aktueller Stand.
### Pagination & Errors
- Standard: `page`, `per_page` (max 50 für Mobile).
- Errors: Parsen von `{ error: { code, message } }`; User-freundliche Messages (z.B. "Nicht genug Credits").
## Non-Functional Requirements
- **Performance**: Ladezeiten < 2s; Lazy-Loading für Galleries.
- **Sicherheit**: Kein PII-Logging; Token-Expiration-Handling; CSRF-Schutz via PKCE.
- **Accessibility**: Framework7 ARIA-Support; Dark Mode (System-Preference).
- **Testing**: Unit-Tests für API-Calls; E2E-Tests für Flows (Cypress).
Für detaillierte UI-Seiten siehe pages-ui.md; für Settings siehe settings-config.md.

View File

@@ -0,0 +1,186 @@
# Seiten und UI-Design für die Tenant Admin App
## Status
- **Version**: 1.0.0 (2025-09-13)
- **Fokus**: Mobile-First Design mit Framework7 v8+ für native iOS/Android-Look & Feel.
## Allgemeines UI-Prinzipien
- **Framework7-Komponenten**: Toolbar (Navigation), List (Datenlisten), Card (Karten für Events/Photos), Modal (Details/Actions), Pull-to-Refresh (Sync), Infinite-Scroll (Pagination).
- **Theming**: System-Dark-Mode-Support; Tenant-spezifische Farben (Primary/Secondary aus Settings).
- **Navigation**: Tabbar unten (Dashboard, Events, Photos, Settings); Side-Menu für Profile/Logout.
- **Offline-Indikator**: Banner oben ("Offline-Modus: Änderungen werden synchronisiert").
- **Loading**: Spinner für API-Calls; Skeleton-Screens für Listen.
- **i18n**: Rechts-nach-Links für de/en; Icons von Framework7-Icons (Material).
## Benötigte Seiten und Komponenten
### 1. Auth-Seiten (OAuth-Flow)
#### Login-Seite
- **Zweck**: OAuth-Authorization (PKCE-Challenge generieren, Redirect zu /oauth/authorize).
- **Layout**:
- Zentrale Card mit Logo, App-Name, "Mit Google/Email anmelden"-Buttons.
- Footer: "Noch kein Account? Registrieren" (Redirect zu Register).
- **Framework7-Komponenten**:
- `f7-page` mit `f7-navbar` (Titel: "Willkommen zur Event Photo App").
- `f7-block` für Content; `f7-button` (large, filled) für OAuth-Start.
- `f7-preloader` während Redirect.
- **Wireframe-Beschreibung**:
```
[Navbar: Logo | Willkommen]
[Block: App-Beschreibung]
[Button: Anmelden mit Google]
[Button: Anmelden mit Email]
[Footer: Registrieren-Link]
```
- **API**: Kein direkter Call; Browser-Redirect zu Backend.
#### Register-Seite (ähnlich Login)
- **Unterschiede**: Form für Email/Password + Terms-Checkbox; Submit zu `/oauth/register`.
### 2. Dashboard (Home)
- **Zweck**: Übersicht über aktive Events, Stats, schnelle Actions.
- **Layout**:
- Top: Willkommens-Banner mit Tenant-Name, Credit-Balance.
- Stats-Cards: Aktive Events, Ungeprüfte Photos, Tasks-Fortschritt.
- Quick-Actions: "Neues Event erstellen", "Photos moderieren".
- **Framework7-Komponenten**:
- `f7-toolbar` unten: Tabs (Home, Events, Photos, Settings).
- `f7-card` für Stats (mit `f7-icon` und Zahlen).
- `f7-list` für Quick-Actions (link mit Arrow).
- `f7-pull-to-refresh` für Sync.
- **Wireframe-Beschreibung**:
```
[Toolbar: Home | Events | Photos | Settings]
[Banner: Hallo [Name]! 3 Credits übrig]
[Row: Card(Events: 2 aktiv) | Card(Photos: 15 neu) | Card(Tasks: 80%)]
[List: > Neues Event | > Moderieren | > Einstellungen]
```
- **API**: `GET /tenant/dashboard` (Stats); `GET /tenant/credits/balance`.
### 3. Events-Übersicht
- **Zweck**: Liste aller Events mit Filter (aktiv/archiviert), Suche.
- **Layout**:
- Navbar: Suche-Feld, Filter-Button (Dropdown: Status, Datum).
- Infinite-Scroll-Liste von Event-Cards (Titel, Datum, Status-Tag, Photo-Count).
- FAB: "+" für Neues Event.
- **Framework7-Komponenten**:
- `f7-searchbar` in Navbar.
- `f7-list` mit `f7-list-item` (Thumbnail, Title, Subtitle: Datum, Badge: Status).
- `f7-fab` (floating action button).
- `f7-segmented` für Filter-Tabs.
- **Wireframe-Beschreibung**:
```
[Navbar: Suche | Filter ▼]
[Segmented: Alle | Aktiv | Archiviert]
[List-Item: [Thumb] Hochzeit Müller (15.09.) [Tag: Aktiv] 45 Photos]
[List-Item: ...]
[+ FAB unten rechts]
```
- **API**: `GET /tenant/events?page=1&status=active` (paginiert).
### 4. Event-Details-Seite
- **Zweck**: Vollständige Event-Info, Edit-Modus, zugehörige Tasks/Photos.
- **Layout**:
- Tabs: Details, Tasks, Photos, Members, Stats.
- Edit-Button (öffnet Modal für Update).
- QR-Code-Section für Join-Link.
- **Framework7-Komponenten**:
- `f7-tabs` mit `f7-tab` für Subseiten.
- `f7-card` für Details (Datum, Ort, Beschreibung).
- `f7-qrcode` (via Plugin) für Share-Link.
- `f7-button` (edit-icon) → `f7-modal` mit Form.
- **Wireframe-Beschreibung**:
```
[Navbar: Event-Titel | Edit-Icon]
[Tabs: Details | Tasks | Photos | Members | Stats]
[Details-Tab: Card(Datum: 15.09., Ort: Berlin) | QR-Code: Scan zum Beitreten]
[Tasks-Tab: Checklist (Drag-to-reorder)]
[Photos-Tab: Grid von Thumbs]
```
- **API**: `GET /tenant/events/{id}`; `PATCH /tenant/events/{id}` (ETag).
### 5. Photo-Gallery-Seite (pro Event)
- **Zweck**: Moderation von Photos; Grid-View mit Lightbox.
- **Layout**:
- Filter: Neu/Ungeprüft/Featured; Sortierung (Datum, Likes).
- Masonry-Grid von Photo-Cards (Thumb, Timestamp, Like-Count).
- Long-Press: Multi-Select für Bulk-Actions (Approve/Delete).
- **Framework7-Komponenten**:
- `f7-photoset` oder custom Grid mit `f7-card` (small).
- `f7-lightbox` für Fullscreen-View.
- `f7-checkbox` für Multi-Select.
- `f7-popover` für Actions (Approve, Feature, Delete).
- **Wireframe-Beschreibung**:
```
[Navbar: Photos (Event) | Filter ▼]
[Grid: 3xN Cards [Thumb | Zeit | Likes] [Checkbox]]
[Lightbox: Fullscreen Photo mit Zoom, Actions]
[Bottom-Bar (bei Select): Approve All | Delete Selected]
```
- **API**: `GET /tenant/events/{id}/photos`; `PATCH /tenant/photos/{id}` (Batch).
### 6. Members-Seite
- **Zweck**: Verwalten von Event-Mitgliedern (Hinzufügen per Email, Rollen).
- **Layout**: Liste von User-Cards (Name, Rolle, Joined-At); Invite-Button.
- **Framework7-Komponenten**:
- `f7-list` mit `f7-list-item` (Avatar, Name, Badge: Rolle).
- `f7-modal` für Invite-Form (Email-Input, Send-Button).
- **Wireframe-Beschreibung**:
```
[Navbar: Members | + Invite]
[List: Anna (Admin) | Ben (Member) | ...]
[Modal: Email eingeben | Rolle wählen | Senden]
```
- **API**: `GET /tenant/events/{id}/members`; `POST /tenant/events/{id}/members`.
### 7. Tasks-Seite
- **Zweck**: Bibliothek verwalten, zu Events zuweisen, Fortschritt tracken.
- **Layout**: Tabs: Meine Tasks, Bibliothek; Drag-and-Drop zwischen Listen.
- **Framework7-Komponenten**:
- `f7-tabs`; `f7-sortable-list` für Drag-and-Drop.
- `f7-checkbox` für Zuweisung.
- **Wireframe-Beschreibung**:
```
[Tabs: Events | Bibliothek]
[Sortable List: Task1 [Checkbox] | Task2 ...]
[Drag: Von Bibliothek zu Event-Liste]
```
- **API**: `GET /tenant/tasks`; `POST /tenant/events/{id}/tasks`.
### 8. Settings-Seite
- **Zweck**: Tenant-Einstellungen bearbeiten (Theme, Limits, Legal).
- **Layout**: Accordion-Sections (Theme, Notifications, Legal, App).
- **Framework7-Komponenten**:
- `f7-accordion` für Sections.
- `f7-toggle`, `f7-select`, `f7-color-picker` für Optionen.
- **Wireframe-Beschreibung**:
```
[Navbar: Einstellungen]
[Accordion: Theme ▼ [Color-Picker] | Notifications [Toggle Push]]
[Legal: Impressum-Link bearbeiten]
[App: Logout-Button]
```
- **API**: `GET/PATCH /tenant/settings`. Details in settings-config.md.
### 9. Billing-Seite
- **Zweck**: Credit-Balance anzeigen, Packs kaufen.
- **Layout**: Balance-Card; Liste von Purchase-Options; Ledger-Historie.
- **Framework7-Komponenten**:
- `f7-card` für Balance (mit Warning bei niedrig).
- `f7-list` für Packs (Preis, Events-Count, Buy-Button).
- `f7-infinite-scroll` für Ledger.
- **Wireframe-Beschreibung**:
```
[Card: Balance: 3 Credits | [Warning: Niedrig!]]
[List: 5 Events (29€) [Buy] | 10 Events (49€) [Buy]]
[Ledger: Kauf 29.09. +5 | Event-Erstellung -1]
```
- **API**: `GET /tenant/credits/balance`; `POST /tenant/purchases/intent`.
## Zusätzliche UI-Elemente
- **Modals**: Confirm-Delete, Photo-Preview, Error-Alerts.
- **Notifications**: `f7-notification` für Sync-Erfolg, neue Photos.
- **Offline-Handling**: `f7-block` mit "Syncing..." Progress-Bar.
- **Accessibility**: ARIA-Labels für alle interaktiven Elemente; VoiceOver-Support.
Für Capacitor-spezifische UI-Anpassungen siehe capacitor-setup.md.

View File

@@ -0,0 +1,124 @@
# Settings und Konfiguration für die Tenant Admin App
## Status
- **Version**: 1.0.0 (2025-09-13)
- **Fokus**: App-interne und tenant-spezifische Einstellungen; Integration mit Capacitor-Plugins.
## App-interne Settings
Diese Settings werden lokal in der App gespeichert (via Capacitor Preferences oder IndexedDB) und beeinflussen das Verhalten der App:
### Core App Settings
- **language**: String (default: 'de') UI-Sprache; Sync mit User-Locale.
- **themeMode**: String ('system' | 'light' | 'dark') Dark Mode-Präferenz; Framework7-Theming.
- **offlineMode**: Boolean (default: true) Aktiviert Offline-Caching und Background-Sync.
- **pushNotifications**: Boolean (default: true) Erlaubt Push-Registrierung.
- **autoSyncInterval**: Number (default: 300) Sekunden bis automatischer Sync (min: 60).
- **maxPhotoCache**: Number (default: 100) Anzahl gecachter Photos pro Event.
### User-spezifische Settings
- **notificationPreferences**: Object
- `newPhotos`: Boolean Benachrichtigung bei neuen Uploads.
- `eventUpdates`: Boolean Bei Event-Änderungen (z.B. Veröffentlicht).
- `lowCredits`: Boolean Warnung bei < 2 Credits.
- **privacySettings**: Object
- `shareAnalytics`: Boolean (default: false) Anonyme Stats teilen.
- `dataRetention`: Number (default: 30) Tage bis Auto-Delete von Cache.
**Speicherung**: Capacitor Preferences API (`@capacitor/preferences`) für sichere, persistente Speicherung. Bei Web-Fallback: localStorage mit Encryption (via Crypto API).
## Tenant-spezifische Optionen
Diese werden über API (`GET/PATCH /tenant/settings`) geladen und überschreiben globale Defaults. Sie definieren das Verhalten für den Tenant und seine Events:
### Theme & Branding
- **primaryColor**: String (default: '#007AFF') Hauptfarbe (iOS-Blau); verwendet in Framework7 CSS-Vars (`--f7-primary`).
- **secondaryColor**: String (default: '#5856D6') Sekundärfarbe für Buttons/Accents.
- **logoUrl**: String Custom Logo für App-Banner (URL zu S3/CDN).
- **tenantName**: String Anzeigename (z.B. "Müller Hochzeit"); in Dashboard-Banner.
### Event Limits & Features
- **maxEventsPerMonth**: Number (default: 5) Limit pro Monat (enforced via Credits).
- **maxPhotosPerEvent**: Number (default: 1000) Soft-Limit; Warnung bei Überschreitung.
- **enableTasks**: Boolean (default: true) Tasks-System aktivieren.
- **enableEmotions**: Boolean (default: true) Emotions/Reactions erlauben.
- **autoApprovePhotos**: Boolean (default: false) Neue Uploads sofort freigeben (Risiko: Spam).
### Legal & Compliance
- **legalPages**: Object (von Backend geladen, siehe docs/prp/02-tenancy.md)
- `impressumUrl`: String Pflicht in DE; Customizable Link.
- `privacyUrl`: String Datenschutzerklärung.
- `agbUrl`: String Allgemeine Geschäftsbedingungen.
- **gdprRetentionDays**: Number (default: 30) Automatische Löschung alter Photos/Events.
- **contactEmail**: String Support-Email für In-App-Feedback.
### Notifications & Integration
- **pushEnabled**: Boolean Tenant-weit Push aktivieren (erfordert Plugin-Registrierung).
- **stripePublicKey**: String Für Client-side Checkout (aus Env, nicht persistent).
- **eventJoinDomain**: String (default: 'events.fotospiel.app') Custom Domain für Guest-PWA.
**API-Handling**: Laden bei Login; Cache mit ETag; Update triggert UI-Refresh (z.B. Theme-Wechsel). Validation: Backend enforct Limits (z.B. Colors als HEX).
## Capacitor-Plugins
Die App integriert folgende Plugins für native Features. Installation via `npx cap add` und Sync nach Build.
### Essentielle Plugins
1. **@capacitor/preferences** (v6+)
- **Zweck**: Sichere Speicherung von App- und User-Settings.
- **Usage**: `Preferences.set({ key: 'themeMode', value: 'dark' })`; Migrate von localStorage.
- **iOS/Android**: Keychain/Keystore für Encryption.
2. **@capacitor/push-notifications** (v6+)
- **Zweck**: Native Push für neue Photos, Event-Updates, Low-Credits.
- **Setup**: Registrierung bei App-Start (`PushNotifications.register()`); Token an Backend senden (`POST /tenant/devices`).
- **Events**: `registrationToken` (senden), `pushNotificationReceived` (In-App-Handling), `pushNotificationActionPerformed` (Tap-Actions).
- **Permissions**: Request bei erstem Login; Fallback zu Web Push in PWA/TWA.
3. **@capacitor/storage** (v6+)
- **Zweck**: Offline-Caching von Events/Photos (als Alternative zu IndexedDB).
- **Usage**: `Storage.set({ key: 'event-123', value: JSON.stringify(data) })`; Size-Limit beachten (50MB).
- **Fallback**: Für Web; Sync mit Background-Sync.
4. **@capacitor/camera** (v6+)
- **Zweck**: Direkte Kamera-Zugriff für Event-QR-Scans oder schnelle Photo-Uploads (Admin-Selfies).
- **Usage**: `Camera.getPhoto({ quality: 90, allowEditing: true })`; Upload via signed URL.
- **Permissions**: Camera/Mic; Privacy-Manifest für App Store.
5. **@capacitor/network** (v6+)
- **Zweck**: Connectivity-Status überwachen; Offline-Modus triggern.
- **Usage**: `Network.addListener('networkStatusChange', handleOffline)`; UI-Update bei 'offline'.
- **Integration**: Deaktiviere Sync-Buttons; Zeige Cached-Data.
### Optionale Plugins
- **@capacitor/haptics** (v6+): Vibration-Feedback bei Actions (z.B. Photo-Approve).
- **@capacitor/share** (v6+): Teilen von Event-QR-Codes via Native Share-Sheet.
- **@capacitor/device** (v6+): Geräte-Info (Model, OS) für Analytics; Token zu Backend.
### Plugin-Konfiguration (capacitor.config.ts)
```typescript
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.fotospiel.tenantadmin',
appName: 'Event Photo Admin',
webDir: 'dist',
bundledWebRuntime: false,
plugins: {
PushNotifications: {
presentationOptions: ["badge", "sound", "alert"]
},
Camera: {
permissions: {
camera: true
}
}
}
};
export default config;
```
### Security & Privacy
- **Plugin-Permissions**: Explizite Requests; Erkläre Zweck im Onboarding (z.B. "Push für neue Photos").
- **Data Storage**: Kein PII in Preferences; Tokens encrypted (siehe Auth-Specs).
- **App Store Compliance**: Privacy Manifest mit Usage-Descriptions (z.B. NSPhotoLibraryUsageDescription).
Für detailliertes Packaging siehe capacitor-setup.md; UI-Integration in pages-ui.md.