A beautiful Progressive Web App (PWA) for tracking Work From Home days for two users.
- Week calendar — Mon–Fri view, navigate week by week
- Monthly calendar — full-month overview, toggleable from the header (desktop)
- Three statuses per day: At office · At home · Travelling
✈️ - Two users — man (blue) and woman (pink), each with their own color theme
- French public holidays — auto-fetched from data.gouv.fr on startup, greyed out as "Férié / Day Off" (
FRENCHDAYOFF=true) - Multi-device sync — SQLite backend via Flask REST API
- 30-day sliding sessions — stay logged in across all devices, session renews on every use
- Profile page — change email, language and password
- French / English — per-user language preference
- Conflict notifications — email alert when both users work from home the same day
- ICS export — download the other user's week as a calendar file
- PWA — installable on iOS and Android, works offline
- iOS safe area — supports Dynamic Island and home bar
| Layer | Technology |
|---|---|
| Backend | Python / Flask / SQLite |
| Frontend | Vanilla JS / Bootstrap 5 / Font Awesome 7 |
| Auth | httpOnly cookie (secrets.token_hex, 30-day sliding expiry) |
| PWA | Service Worker, Web App Manifest |
# 1. Clone and enter the project
cd WFH
# 2. Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Configure
cp .env.example .env
# Edit .env to set user names, URL, SMTP, etc.
# 5. Run
python server.pyOn first run the database is seeded and randomly generated passwords are printed to the console — save them.
Open http://localhost:<PORT> in your browser (default: http://localhost:8001).
All settings live in .env:
| Variable | Default | Description |
|---|---|---|
HOST |
0.0.0.0 |
Bind address |
PORT |
8001 |
HTTP port |
DEBUG |
false |
Flask debug mode |
APP_URL |
(port) | Public URL — included in notification emails |
USER1_ID |
julien |
Login username for user 1 |
USER1_USERNAME |
julien |
Same as ID (can differ) |
USER1_NAME |
Julien |
Display name |
USER2_ID |
mallorie |
Login username for user 2 |
USER2_USERNAME |
mallorie |
Same as ID (can differ) |
USER2_NAME |
Mallorie |
Display name |
FRENCHDAYOFF |
false |
Fetch French public holidays from data.gouv.fr and display as "Férié" |
EMAIL_DELAY |
900 |
Seconds to wait before sending a conflict email (default 15 min) |
SMTP_HOST |
SMTP server — leave empty to disable emails | |
SMTP_PORT |
587 |
SMTP port |
SMTP_USER |
SMTP login | |
SMTP_PASSWORD |
SMTP password | |
SMTP_FROM |
Sender address |
Passwords are never stored in .env — they are randomly generated (12 chars) on first startup.
When both users are working from home on the same day, the other user receives an email in their own language. To avoid noise from rapid status changes, the email is queued for EMAIL_DELAY seconds and only sent if the conflict still exists when the timer fires. Toggling away from "home" before the timer expires cancels the email.
Example (French):
[Télétravail] Attention Conflit ! Julien sera également en télétravail le lundi 15 mars 2026. Lien vers le site : https://wfh.mousqueton.io
Example (English):
[WFH] Attention Conflict! Julien will also work from home on Monday, March 15, 2026. Link to the site: https://wfh.mousqueton.io
Emails are only sent if the recipient has set an email address in their profile and SMTP_HOST is configured.
WFH/
├── server.py # Flask backend (API + static serving)
├── requirements.txt # Python dependencies
├── .env.example # Configuration template
├── wfh.example.com.conf # nginx virtual host template
├── INSTALL.md # Production deployment guide
└── static/ # Public web assets
├── index.html # Single-page app shell
├── app.js # Frontend logic (i18n, calendar, auth)
├── styles.css # Glassmorphism dark theme
├── sw.js # Service worker (offline cache)
├── manifest.json # PWA manifest
├── favicon.svg # App favicon (matches logo gradient)
├── robots.txt # Disallow all crawlers
└── icons/ # PWA icons (192px, 512px)
See INSTALL.md for full instructions including systemd service setup and nginx with Let's Encrypt SSL.
To change user names or force new passwords, stop the server, delete wfh.db, and restart:
rm wfh.db
python server.py