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).