diff --git a/Client/ShiftScheduler.Client.csproj b/Client/ShiftScheduler.Client.csproj index ccd019e..852ce95 100644 --- a/Client/ShiftScheduler.Client.csproj +++ b/Client/ShiftScheduler.Client.csproj @@ -11,40 +11,4 @@ - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - diff --git a/Client/wwwroot/icons/BB.png b/Client/wwwroot/icons/BB.png deleted file mode 100644 index 8df1888..0000000 Binary files a/Client/wwwroot/icons/BB.png and /dev/null differ diff --git a/Client/wwwroot/icons/Bg.png b/Client/wwwroot/icons/Bg.png deleted file mode 100644 index a1b032e..0000000 Binary files a/Client/wwwroot/icons/Bg.png and /dev/null differ diff --git a/Client/wwwroot/icons/F1.png b/Client/wwwroot/icons/F1.png deleted file mode 100644 index ced0130..0000000 Binary files a/Client/wwwroot/icons/F1.png and /dev/null differ diff --git a/Client/wwwroot/icons/F1_PF.png b/Client/wwwroot/icons/F1_PF.png deleted file mode 100644 index 9a77978..0000000 Binary files a/Client/wwwroot/icons/F1_PF.png and /dev/null differ diff --git a/Client/wwwroot/icons/G1.png b/Client/wwwroot/icons/G1.png deleted file mode 100644 index 2dbc8c1..0000000 Binary files a/Client/wwwroot/icons/G1.png and /dev/null differ diff --git a/Client/wwwroot/icons/G1TS.png b/Client/wwwroot/icons/G1TS.png deleted file mode 100644 index 478a2ba..0000000 Binary files a/Client/wwwroot/icons/G1TS.png and /dev/null differ diff --git a/Client/wwwroot/icons/G2.png b/Client/wwwroot/icons/G2.png deleted file mode 100644 index df0a6f7..0000000 Binary files a/Client/wwwroot/icons/G2.png and /dev/null differ diff --git a/Client/wwwroot/icons/G2TS.png b/Client/wwwroot/icons/G2TS.png deleted file mode 100644 index c40ed23..0000000 Binary files a/Client/wwwroot/icons/G2TS.png and /dev/null differ diff --git a/Client/wwwroot/icons/Kompensation.png b/Client/wwwroot/icons/Kompensation.png deleted file mode 100644 index a005d55..0000000 Binary files a/Client/wwwroot/icons/Kompensation.png and /dev/null differ diff --git a/Client/wwwroot/icons/S1.png b/Client/wwwroot/icons/S1.png deleted file mode 100644 index 3fa5d21..0000000 Binary files a/Client/wwwroot/icons/S1.png and /dev/null differ diff --git a/Client/wwwroot/icons/break-icon.png b/Client/wwwroot/icons/break-icon.png deleted file mode 100644 index d478627..0000000 Binary files a/Client/wwwroot/icons/break-icon.png and /dev/null differ diff --git a/README.md b/README.md index 4a78f76..e8f1b55 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,59 @@ # ShiftScheduler -A modern Blazor WebAssembly application for managing work shift schedules with calendar export capabilities and transport integration. +A Blazor WebAssembly application for managing work shift schedules with calendar export and transport integration. ## Overview ShiftScheduler is a web-based shift management system that allows users to: -- Plan and visualize work shifts using an intuitive calendar interface -- Export schedules to industry-standard formats (ICS and PDF) -- Calculate optimal transport connections for work commutes -- Manage multiple shift types with customizable icons and time periods +- Plan and visualize work shifts using a monthly calendar interface +- Export schedules to ICS and PDF formats +- Sync shifts directly to Google Calendar or Nextcloud Calendar +- View optimal transport connections for work commutes - Secure access through Google OAuth authentication -Perfect for individuals or small teams who need to organize shift work, track schedules, and integrate with existing calendar systems. - ## Key Features -### πŸ“… **Calendar-Based Shift Management** -- Interactive monthly calendar view with easy shift selection -- Switch between current and next month -- Visual shift indicators with customizable icons and colors -- Responsive design for desktop and mobile devices +### Calendar-Based Shift Management +- Monthly calendar view with day-by-day shift assignment +- Toggle between current and next month +- Mobile-friendly week layout alongside the desktop grid view -### πŸ”„ **Export Capabilities** -- **ICS Export**: Import schedules directly into Google Calendar, Outlook, or any calendar application -- **PDF Export**: Generate printable shift schedules with professional formatting -- **Google Calendar Sync**: Direct synchronization with Google Calendar (NEW!) - - Automatic calendar selection for multiple calendars - - Smart event management (updates existing app-created events) - - Respects existing calendar events from other sources -- One-click export functionality +### Export & Sync +- **ICS Export**: Download a calendar file compatible with any calendar application +- **PDF Export**: Generate a printable monthly schedule +- **Google Calendar Sync**: Direct sync to a selected Google Calendar; existing app-managed events are updated, other events are untouched +- **Nextcloud Calendar Sync**: CalDAV-based sync to a selected Nextcloud calendar -### πŸš† **Transport Integration** -- Automatic calculation of travel times between configurable stations -- Integration with Swiss public transport API (transport.opendata.ch) -- Safety buffer calculations for reliable commute planning -- Display of optimal departure and arrival times +### Transport Integration +- Calculates optimal departure and arrival times for work commutes +- Integrates with the Swiss public transport API (`transport.opendata.ch`) +- Displays morning and afternoon connections alongside each shift +- Configurable safety buffers and break durations -### πŸ” **Secure Authentication** -- Google OAuth integration for secure access -- Configurable authorized email addresses -- Session management with proper login/logout functionality +### Authentication +- Google OAuth 2.0 with a configurable list of authorized email addresses -### βš™οΈ **Flexible Configuration** -- **Shift Types**: Fully customizable shift definitions with: - - Custom names and icons (emoji or PNG images) - - Morning and afternoon time periods - - Flexible time format support -- **Transport Settings**: Configurable start/end stations, API parameters, and timing buffers -- **Persistent Configuration**: Settings survive application restarts and container rebuilds +### Configuration +- Shift definitions (name, emoji icon, morning/afternoon time ranges) managed via the in-app configuration dialog +- Transport settings (stations, API URL, timing buffers) configurable in-app +- Configuration can be exported and imported as JSON for backup -### 🐳 **Docker Ready** -- Container-friendly with persistent external configuration -- Easy deployment with Docker Compose -- Configuration backup and version control support +## Default Shift Types -## Available Shift Types (Default Configuration) +| Icon | Name | Morning Time | Afternoon Time | +|------|------|--------------|----------------| +| ⚫ | Frei | β€” | β€” | +| 🌴 | Urlaub | β€” | β€” | +| πŸŒ… | FrΓΌh | 06:00–14:00 | β€” | +| πŸŒ† | SpΓ€t | β€” | 14:00–22:00 | +| β˜€οΈ | Tag | 08:00–12:00 | 13:00–17:00 | +| (icon) | Pause | 10:00–10:15 | 15:00–15:15 | -| Icon | Name | Morning Time | Afternoon Time | Description | -|------|------|--------------|----------------|-------------| -| ⚫ | Frei | - | - | Free day / Day off | -| 🌴 | Urlaub | - | - | Vacation day | -| πŸ›‘ | Pause | 10:00-10:15 | 15:00-15:15 | Break periods | -| πŸŒ… | FrΓΌh | 06:00-14:00 | - | Early shift | -| πŸŒ† | SpΓ€t | - | 14:00-22:00 | Late shift | -| β˜€οΈ | Tag | 08:00-12:00 | 13:00-17:00 | Day shift | +All shift types are configurable through the in-app settings dialog. -*All shift types are fully configurable through the application interface.* +## Configuration -## Configuration Options - -### Authentication Settings +### Authentication (`Server/appsettings.json`) ```json { "Authentication": { @@ -78,14 +62,24 @@ Perfect for individuals or small teams who need to organize shift work, track sc "ClientSecret": "your-google-client-secret" }, "AuthorizedEmails": [ - "user1@gmail.com", - "user2@example.com" + "user@gmail.com" ] } } ``` -### Transport Configuration +### Nextcloud CalDAV +```json +{ + "Nextcloud": { + "BaseUrl": "https://your-nextcloud-instance.example.com", + "Username": "your-username", + "AppPassword": "your-app-password" + } +} +``` + +### Transport ```json { "Transport": { @@ -101,26 +95,11 @@ Perfect for individuals or small teams who need to organize shift work, track sc } ``` -### Shift Definitions -```json -{ - "Shifts": [ - { - "Name": "Custom Shift", - "Icon": "🎯", - "MorningTime": "09:00-13:00", - "AfternoonTime": "14:00-18:00" - } - ] -} -``` - ## Installation & Setup ### Prerequisites - .NET 9.0 SDK -- Modern web browser -- Google Cloud Console account (for authentication) +- Google Cloud Console account (for OAuth) ### Quick Start @@ -135,10 +114,9 @@ Perfect for individuals or small teams who need to organize shift work, track sc dotnet restore ``` -3. **Configure authentication** (see [Authentication Setup Guide](authentication-setup.md)) - - Create Google OAuth application - - Update `Server/appsettings.json` with your credentials - - Add authorized email addresses +3. **Configure authentication** + - See [Authentication Setup Guide](authentication-setup.md) + - Update `Server/appsettings.json` with your Google OAuth credentials and authorized emails 4. **Build and run** ```bash @@ -147,84 +125,15 @@ Perfect for individuals or small teams who need to organize shift work, track sc dotnet run ``` -5. **Access the application** +5. **Open the app** - Navigate to `http://localhost:5000` - Sign in with your Google account - - Start planning your shifts! - -### Docker Deployment - -```yaml -version: '3.8' -services: - shiftscheduler: - build: . - ports: - - "5000:5000" - volumes: - - ./config:/app/config # Persistent configuration - environment: - - ASPNETCORE_ENVIRONMENT=Production -``` - -See [Docker Configuration Guide](DOCKER_CONFIG.md) for detailed deployment instructions. - -## Usage - -### Planning Shifts -1. Navigate between months using the month selection buttons -2. Click on any day to cycle through available shift types -3. Shifts are automatically saved and persist across sessions -4. Hover over shifts to see detailed time information - -### Exporting Schedules -- **ICS Export**: Click "Export to ICS" to download a calendar file compatible with all major calendar applications -- **Google Calendar Sync**: Click "Sync to Google Calendar" to directly sync shifts to your Google Calendar - - If you have multiple calendars, choose which one to sync to - - Existing shift events will be updated automatically - - Other calendar events remain untouched -- **PDF Export**: Click "Export to PDF" to generate a printable schedule document - -### Managing Configuration -1. Click the "βš™οΈ Configuration" button to access settings -2. **Shift Management**: Add, edit, or remove shift types -3. **Transport Settings**: Configure stations and timing preferences -4. **Import/Export**: Backup and restore configuration settings - -### Transport Integration -When transport is configured, the application automatically: -- Calculates optimal departure times for work commutes -- Displays train connections and travel duration -- Applies safety buffers for reliable planning -- Shows both morning and afternoon journey options - -## Technical Architecture - -- **Frontend**: Blazor WebAssembly (.NET 9.0) -- **Backend**: ASP.NET Core Web API (.NET 9.0) -- **Authentication**: Google OAuth 2.0 -- **Export**: QuestPDF for PDF generation, custom ICS implementation -- **Transport**: Swiss Public Transport API integration -- **Configuration**: JSON-based with external persistence support ## Documentation -- [πŸ“š Authentication Setup Guide](authentication-setup.md) - Complete Google OAuth configuration -- [🐳 Docker Configuration Guide](DOCKER_CONFIG.md) - Containerization and deployment -- [πŸ“„ License](LICENSE) - Apache 2.0 License - -## Contributing - -This project follows standard .NET development practices: -- Uses .NET 9.0 target framework -- Implements nullable reference types -- Follows established architectural patterns -- Includes comprehensive configuration options - -## Support - -For issues, questions, or feature requests, please use the GitHub issue tracker. +- [Authentication Setup Guide](authentication-setup.md) β€” Google OAuth configuration +- [Docker Configuration Guide](DOCKER_CONFIG.md) β€” Containerized deployment ## License -This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. +Apache License 2.0 β€” see the [LICENSE](LICENSE) file for details. diff --git a/Server/Program.cs b/Server/Program.cs index 8ea29ab..5856ff2 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -124,6 +124,14 @@ app.UseHttpsRedirection(); app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); +var iconsPath = Path.Combine(Directory.GetCurrentDirectory(), "config", "icons"); +Directory.CreateDirectory(iconsPath); +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(iconsPath), + RequestPath = "/icons" +}); + app.UseRouting(); app.UseAuthentication(); diff --git a/docs/superpowers/plans/2026-04-14-configurable-icons.md b/docs/superpowers/plans/2026-04-14-configurable-icons.md new file mode 100644 index 0000000..2ceac97 --- /dev/null +++ b/docs/superpowers/plans/2026-04-14-configurable-icons.md @@ -0,0 +1,174 @@ +# Configurable Icons from Docker Share 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:** Serve shift icons from `config/icons/` on the Docker share instead of bundling them in the repository. + +**Architecture:** Add a second `UseStaticFiles` middleware in `Server/Program.cs` with a `PhysicalFileProvider` pointing to `config/icons/`, served at the `/icons` URL path. Remove all PNG icon files and their `.csproj` entries from the repo. No frontend changes needed β€” `` continues to work unchanged. + +**Tech Stack:** ASP.NET Core static files middleware, `Microsoft.Extensions.FileProviders.PhysicalFileProvider` (already in the ASP.NET Core SDK, no new package required) + +--- + +## Files Changed + +| Action | File | What changes | +|--------|------|--------------| +| Modify | `Server/Program.cs` | Add second `UseStaticFiles` for `config/icons/` | +| Modify | `Client/ShiftScheduler.Client.csproj` | Remove all `` and `` entries | +| Delete | `Client/wwwroot/icons/` | Remove entire directory and all PNGs | +| Delete | `Client/wwwroot/break-icon.png` (if present) | Remove stray root-level copy | + +--- + +### Task 1: Add static file middleware for `config/icons/` + +**Files:** +- Modify: `Server/Program.cs:125` (after `app.UseStaticFiles();`) + +- [ ] **Step 1: Add the icons static file middleware** + +In `Server/Program.cs`, replace: + +```csharp +app.UseBlazorFrameworkFiles(); +app.UseStaticFiles(); +``` + +with: + +```csharp +app.UseBlazorFrameworkFiles(); +app.UseStaticFiles(); + +var iconsPath = Path.Combine(Directory.GetCurrentDirectory(), "config", "icons"); +Directory.CreateDirectory(iconsPath); +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(iconsPath), + RequestPath = "/icons" +}); +``` + +- [ ] **Step 2: Verify the app builds** + +```bash +dotnet build +``` + +Expected: Build succeeded with 0 errors (pre-existing warnings about QuestPDF and null reference are OK). + +- [ ] **Step 3: Commit** + +```bash +git checkout -b feature/configurable-icons +git add Server/Program.cs +git commit -m "feat: serve icons from config/icons/ Docker share via static file middleware" +``` + +--- + +### Task 2: Remove icon files and clean up the .csproj + +**Files:** +- Modify: `Client/ShiftScheduler.Client.csproj` β€” remove `` with icon `` entries +- Delete: `Client/wwwroot/icons/` directory +- Delete: `Client/wwwroot/break-icon.png` (stray root-level copy listed in .csproj) + +- [ ] **Step 1: Remove the entire icons ItemGroup from the .csproj** + +In `Client/ShiftScheduler.Client.csproj`, remove the entire `` block that contains the icon entries (lines 14–48 in the current file), so the file becomes: + +```xml + + + + + + + + + + + + + + +``` + +- [ ] **Step 2: Delete the icon files from the repo** + +```bash +rm -rf Client/wwwroot/icons +rm -f Client/wwwroot/break-icon.png +``` + +- [ ] **Step 3: Verify the app still builds** + +```bash +dotnet build +``` + +Expected: Build succeeded with 0 errors. + +- [ ] **Step 4: Commit** + +```bash +git add Client/ShiftScheduler.Client.csproj +git add -u Client/wwwroot/ +git commit -m "chore: remove bundled icon files from repo β€” icons now served from Docker share" +``` + +--- + +### Task 3: Smoke test the icons endpoint + +This task verifies the middleware works end-to-end before the branch is merged. + +- [ ] **Step 1: Copy a test icon to the config/icons directory** + +```bash +mkdir -p config/icons +# Copy any PNG you have on hand, e.g. the app favicon +cp Client/wwwroot/favicon.png config/icons/test.png +``` + +- [ ] **Step 2: Start the server** + +```bash +cd Server && dotnet run +``` + +Expected: App starts at `http://localhost:5000`. + +- [ ] **Step 3: Verify the icon is served** + +```bash +curl -o /dev/null -w "%{http_code}" http://localhost:5000/icons/test.png +``` + +Expected output: `200` + +- [ ] **Step 4: Verify a missing icon returns 404** + +```bash +curl -o /dev/null -w "%{http_code}" http://localhost:5000/icons/nonexistent.png +``` + +Expected output: `404` + +- [ ] **Step 5: Stop the server and clean up the test file** + +```bash +rm config/icons/test.png +``` + +- [ ] **Step 6: Merge to main** + +```bash +cd .. +git checkout main +git merge --squash feature/configurable-icons +git commit -m "feat: serve icons from config/icons/ Docker share" +git branch -d feature/configurable-icons +``` diff --git a/docs/superpowers/specs/2026-04-14-configurable-icons-design.md b/docs/superpowers/specs/2026-04-14-configurable-icons-design.md new file mode 100644 index 0000000..4379678 --- /dev/null +++ b/docs/superpowers/specs/2026-04-14-configurable-icons-design.md @@ -0,0 +1,59 @@ +# Configurable Icons from Docker Share + +**Date:** 2026-04-14 +**Status:** Approved + +## Problem + +Icons for shifts are currently PNG files bundled in the repository under `Client/wwwroot/icons/`. This means adding or changing an icon requires a code change and a redeployment. The user wants icons to be configurable at runtime, placed on the same Docker-mounted volume as monthly schedule files and other configuration. + +## Solution + +Serve icon files from `config/icons/` (a subfolder of the existing Docker share) using ASP.NET Core's static file middleware with a `PhysicalFileProvider`. No frontend changes are required β€” the URL path `/icons/filename.png` stays identical. + +## Architecture + +### Server change (`Server/Program.cs`) + +Add a second `UseStaticFiles` call after the existing one: + +```csharp +var iconsPath = Path.Combine(Directory.GetCurrentDirectory(), "config", "icons"); +Directory.CreateDirectory(iconsPath); +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new PhysicalFileProvider(iconsPath), + RequestPath = "/icons" +}); +``` + +- `config/icons/` is created automatically on first run if absent. +- Files placed there are served at `/icons/{filename}` immediately, no restart needed. +- Built-in ASP.NET Core static file serving provides ETags, cache headers, and range request support. + +### Cleanup + +- Delete `Client/wwwroot/icons/` and all PNG files within it. +- Remove all `` entries from `Client/ShiftScheduler.Client.csproj`. + +### No changes needed + +- `Shift.IsPngIcon` computed property +- `Shift.Icon` field +- `` in `Index.razor` +- `ConfigurationDialog.razor` +- Any other frontend or shared model code + +## Data Flow + +1. User places `myicon.png` in `config/icons/` on the Docker share. +2. In `appsettings.json` (or `config/shifts.json`), the shift's `Icon` field is set to `myicon.png`. +3. The Blazor client fetches shift config from `GET /api/shift/shifts`. +4. `Shift.IsPngIcon` returns `true`, and the UI renders ``. +5. The browser GETs `/icons/myicon.png` β€” served by the `PhysicalFileProvider` from `config/icons/myicon.png`. + +## Out of Scope + +- Emoji icons are unaffected. +- No upload UI for icons β€” files are placed on the share directly (consistent with how schedule files work). +- No fallback to repo-bundled icons (repo icons are removed entirely).