Overview of driver performance, salaries, weekly reports, and forecast cashflow
Loading...
My Profile
Personliga uppgifter, foto och dokument (GDPR)
Loading...
Settings
Manage integrations, backups and app settings
Google Calendar
Sync events with Google Calendar. Shifts and appointments appear automatically in your Google Calendar.
Loading...
Google Tasks
Sync tasks with Google Tasks. Anything you create in the app also shows in Gmail / Google Workspace.
Loading...
Excel Backup
Export data to .xlsx, edit offline and re-import changes. Optional automatic save to OneDrive.
Loading...
Database Backup
Automatic daily SQL dump of the database (cron) plus manual download anytime from here.
Loading...
Contract Presets
Ready-made contract templates (e.g. "Fulltime B-kat", "Substitute"). Pick a preset and all fields fill in automatically.
Loading...
Driver Messages
Messages / reminders shown on the drivers dashboard. Ideal for announcements and route changes.
Messages appear automatically to drivers when they log in.
View the app as another user
Pick a driver to see the app exactly as they see it - dashboard, achievements, profile, work hours, permissions. When done, click "Exit user view" in the top banner.
⚠️ You cannot impersonate another admin. All actions during impersonation are logged in the activity log.
Viewing as
No internet connection
Loading...
SOREVO App - Version History
v2.3.171 - Full English UI + version history
Translated all remaining non-English UI text to English: the Google Calendar/Tasks and Backup settings sections, and the entire version history below. The exported/extract files stay in Swedish on purpose.
v2.3.170 - Faster mobile load (lazy libraries)
The Calendar and Insights libraries now load only when an admin opens those tabs, so drivers no longer load them up front. Faster first load on phones.
Mobile badge timer is reset on re-init to avoid duplicate timers.
v2.3.169 - 🔒 CSP now enforcing
Promoted the Content-Security-Policy from report-only to enforcing after a live console check. Added the sorevo.se logo host to img-src (used by PDF export). The browser now actively blocks scripts/resources from unapproved origins — real XSS defense in depth.
v2.3.168 - 🧹 Removed debug console logging
Removed the two Google sync console.log statements from production. (Audit follow-up: hex colors are already validated server-side and outside-click listeners self-remove — no leaks.)
v2.3.167 - 📱 Readable shift times on the dashboard week strip
Bumped the "This week" day labels and shift times to a readable size (were 8–9px, hard to read on phones). Mobile audit now passes with 0 warnings on all screen sizes (320–430px).
Added a CSP in Report-Only mode (logs violations, blocks nothing) plus object-src/base-uri/frame-ancestors lockdown. Promote to enforcing once the console shows zero violations.
v2.3.165 - 🛡️ Hardening: no double-submit, atomic invoice edit, correct date
All forecast actions (Quick invoice, Generate invoices/salary, Link, Save entry, inline edit) are now guarded against double-click — no more duplicate financial entries.
Editing an invoice in the Timeline is now an atomic in-place update (no delete+recreate) — a failed save can no longer lose the entry.
"Today" now uses Europe/Stockholm consistently, fixing early-morning dates landing on the previous day (e.g. Quick invoice default cut date).
v2.3.164 - 🔒 Hide unpublished-week days from the accept-schedule nudge
The "changed days to accept" list no longer shows days from unpublished weeks. Combined with the hash fix, an unpublished month produces no accept card at all.
The driver dashboard now filters unpublished weeks in admin "User View" (impersonation) too, not just real driver sessions.
Insights tab is now admin-only and fully hidden from drivers and colleagues (it was visible next to General — a financial-data leak).
Unpublished weeks (admin hasn't ticked the box) are now hidden from drivers and colleagues everywhere: schedule grid, dashboard next shift, shift offers, today's team and the "accept your schedule" nudge — until the week is published.
v2.3.162 - 🧹 Compact Insights: smaller charts
Tightened the Insights tab — KPI cards, runway gauge and all charts are shorter and denser, so the page is no longer dominated by oversized graphs. Monthly obligations table unchanged.
v2.3.161 - 📊 Monthly obligations: profit margin per month
Added a Margin column to Monthly obligations — operating result as a % of turnover (ex-VAT) for each month, plus a total.
v2.3.160 - ✂️ Cut date editable per invoice in the Timeline
The inline Timeline editor now has a cut date (FAKTDATUM) field, so you can set/correct each invoice's revenue month directly on its row.
Fixed: editing an invoice inline was wiping the cut date (revenue fell back to the payment month) — it is now preserved, so Insights shows the correct month.
v2.3.159 - ✂️ Invoice cut date (FAKTDATUM) drives the revenue month
Quick invoice now has a separate cut date (FAKTDATUM) field next to the payment date. The cut date sets the revenue month exactly; the payment date drives cash flow.
The cut date is stored on the invoice and shown as a distinct chip in the Timeline, so you can see which month each invoice belongs to.
Available in the full Income entry form too; profit-per-month uses the real cut date instead of estimating it from the payment.
v2.3.158 - 🧹 No confirmation table: generated invoices are just forecast
Removed the "open invoices — correct amount" table. Generated weekly invoices are projected forecast (from scheduled hours) and need no confirmation.
To correct an invoice you actually cut, edit its amount inline in the Timeline — it then keeps its month and is protected from the next Generate run.
The Reconciliation panel shows again only when a CSV deposit needs linking.
v2.3.157 - 🎯 Accurate revenue month + editable invoice amounts
Each customer payment is now attributed to the invoice month by going back 22 calendar days (the observed payment lag) and snapping to that week's Monday — landing exactly on the invoice date (FAKTDATUM).
Fixes payments near a month boundary being counted in the previous month (the old 20-working-day rule drifted ~28 days). Profit-per-month is now accurate.
Correct a generated invoice's amount to the real cut amount in the Reconciliation panel — it keeps its FAKTDATUM month and is protected from the next Generate run.
No double counting: when an invoice already covers a week, the matching bank deposit defers to it.
New setting credit_calendar_days (default 22) if the credit terms change.
v2.3.156 - 🧹 Reconciliation shows only deposits with a matching invoice
The reconciliation panel no longer lists old, already-settled customer deposits. A deposit now appears only when there is an open generated invoice ~20 working days back to link it to.
Historical payments stay counted as revenue via the deposit-back rule — they need no manual linking. The panel hides itself entirely when there is nothing to reconcile.
v2.3.155 - 🔁 Invoices rebuilt: future-only generate + CSV-locked past (20 working days)
Generate invoices now creates forecast invoices ONLY for the current week (Mon–Sun) and forward — past weeks are never touched (they are locked by the bank CSV).
Each customer payment is matched to the invoice issued ~20 working days earlier (± window); linking locks it to the real CSV amount and keeps the work-week month.
Past/historical revenue comes straight from the customer's bank deposits, attributed back 20 working days to the correct work month. No double counting.
Leftover auto-generated invoices are now removed automatically (one-time DB cleanup) so the past forecast flow and the TODAY balance match the bank CSV.
Removed the temporary "Clear generated invoices" button and its action — the Forecast toolbar is back to its clean, simple state (Upload · Generate salary · Excel · Print).
Added a Clear generated invoices button in the Forecast toolbar to remove auto-invoices left over from the rolled-back trial, so the past forecast flow and the TODAY balance match the bank CSV exactly. Real bank rows and manual entries are untouched.
Removed Phase 2 auto-invoice generation and reconciliation (recoverable at tag stable-v2.3.151). The weekly invoice calculation (charge rates in the Weekly Report) stays.
Re-applied the charge-rate persistence fix: rates are cached in localStorage so they survive refresh / week-month navigation even if a server save fails (with a visible error message if it does).
v2.3.148 - 🔒 Security & language audit
Full security review of all 50 API endpoints: auth, CSRF, SQL injection, file access, uploads, sessions, cron keys — no critical issues found. Added HSTS header (.htaccess) as hardening.
Translated remaining Greek UI strings to English in Activity Log, Available Days and Work Hours (route picker groups).
v2.3.147 - ➡️ Charge rates carry forward to all following weeks
Charge rates entered in one month now apply to all following weeks/months automatically (carry-forward) until you change them. Empty rows no longer block the carry-forward.
v2.3.146 - 🐛 Fix: weekly charge rates lost on refresh
Charge rates entered in the Weekly Report were lost after a refresh (a lazy-load race overwrote typed values before they were saved). Rates are now loaded up-front with the month data, so they persist and the weekly invoice computes reliably.
v2.3.145 - 🧾 Weekly invoice: charge rates per hour type (Phase 1)
The Weekly Report (admin) now has charge rates per hour type (Base/OB-kväll/OB-helg/Röd). The weekly invoice + VAT is calculated live, with issue date (next Monday) and payment date (+20 days).
Rates are stored on the server per month and carried forward to the next month. They do not appear in the PDF/Excel export.
Next phase: automatic invoice generation in the forecast (correct month) + matching with bank deposits + forecasting future ones.
v2.3.144 - 📋 Insights: "Monthly obligations" table per month
New table in Insights: for each month it shows all income and all obligations that BELONG to the month - revenue (incl. VAT), salaries, employer contributions, operating costs, VAT - even if they are paid the next or following month. Net = operating result.
Backend: the per-month accrual analysis now returns gross revenue, salaries/AGA separately, gross operating costs and net VAT (sales minus expenses).
The automatic cleanup of exact duplicates (manual forecast vs real CSV transaction) did not catch Quick Invoice entries (source='quick_invoice'). Now it includes them - the manual one is deleted, the confirmed one stays. (Replay the latest CSV to remove existing ones.)
Insights tab: more compact (smaller charts in one row), all in English, new "What this means" panel with automatic takeaways, and a short explanation under each chart. The P&L cards & reclassify dropdown are also in English.
KPI cards upgraded to the style of top CFO dashboards: large number + delta vs previous month (arrow/color) + sparkline trend on each card.
Prominent Runway gauge (semicircle vs 90-day target, color-coded) and Resultatbrygga (waterfall): Omsättning to -Personal to -Drift to =Rörelseres. to +/-Finans to =Efter fin.
3-30-300 hierarchy: summary in 3 seconds, detail in the charts. Likviditetsprognos shown next to the gauge.
v2.3.141 - 📈 New "Insikter" tab with interactive charts (Chart.js)
New Insikter tab next to Forecast: KPI hero (saldo, runway, rörelseresultat, nettomarginal) + 4 interactive charts - likviditetsprognos (runway with threshold), monthly resultat+marginal, intäkter vs kostnader, cost-distribution donut.
Self-hosted Chart.js (not CDN). Hover tooltips, responsive. The Forecast tab also got a compact KPI strip at the top.
v2.3.140 - 📊 Correct profitability model (Swedish P&L)
The profit forecast was rewritten in the Swedish model: nettoomsättning (ex-moms) - salaries+AGA (from hours) - operating costs = rörelseresultat (+ rörelsemarginal), and resultat efter finansiella poster (+ nettomarginal).
Each transaction is classified into a P&L class (Försäljning/Personal/Drift/Finans/Moms/Ej-resultat). It stops the error of "every incoming /1.25 = turnover" and "every outgoing = cost" - loans, owner deposits, VAT, dividends, Skatteverket settlement are excluded (no more double counting).
Manual class correction per transaction via dropdown (orange = manual override). Reset to "auto" whenever you want.
v2.3.139 - 🧹 Auto-cleanup: exact duplicates (manual forecast vs real CSV transaction)
When a manual forecast has the exact same amount as a real bank transaction from the CSV and is within ±14 days, the manual entry is deleted automatically and only the confirmed one stays - no more orange "ghosts" next to the real one.
Runs on every CSV import/re-import. Only non-recurring manual entries; each bank transaction is "consumed" at most once.
v2.3.138 - 🔧 Fix: correct order of same-day transactions (running balance)
Transactions with the same date appeared in the wrong order (sorted by amount), so the adjacent balances did not line up (e.g. 574,386 instead of a continuous chain). The numbers were correct - only the order was wrong.
Now confirmed same-day transactions are sorted by the real bank balance chain (balance_after), so each row connects correctly to the previous one.
Confirmed entries for today appeared above the "TODAY" divider (in the future zone). Now they sit below "TODAY" (current), not as a forecast.
Forecast rule (orange): any entry that is not linked to a real bank transaction = forecast - regardless of status. Once confirmed from CSV, it counts as real.
v2.3.136 - 🔧 Fix: manual invoices (status "pending") were not counted in profit
When you add an invoice/entry, the default status is "pending". The profit card counted only "projected", so manual invoices (future income) were ignored. Now "pending" entries are also counted (like the rest of the app) and appear in "If settled now" (orange).
v2.3.135 - 🧮 "Monthly closing" (accrual): real profit per month
New card: each month "closes" with the month's revenue minus all costs that belong to it (salaries + employer contributions computed from THAT month's hours, regardless of when they are paid, + other expenses). VAT & dividends are excluded.
Net profit + margin % of monthly turnover per month, and year total (closed months).
Removes the timing "noise" for the most accurate profitability picture. The cash views (confirmed / settle-now) remain alongside.
The card now also includes projected forecast items (not confirmed from CSV) - they appear orange until confirmed by a bank transaction.
Two numbers: Confirmed (bank) = real, and "If settled now" = if you paid off all obligations now - with total profit & percentage.
Bars & cost breakdown show confirmed + projected separately.
v2.3.133 - 📊 Profit card: fiscal year start month + cost breakdown by category
New setting "Fiscal year start month" (default January = calendar) - you choose the start month of the fiscal year.
The card no longer excludes any category; it shows costs grouped (Salaries / Skatteverket / Dividends / Other) with amount and % - a full picture of where the money goes.
v2.3.132 - 🔧 Fix: text overlap on scroll (Forecast timeline header)
The Forecast table's sticky header had a background only on <thead> (not the cells) - with border-collapse the rows showed through and the text overlapped on scroll. Fixed (solid background + sticky on th).
v2.3.131 - 📈 "Net profit (fiscal year)" card in Forecast
New card: net profit YTD + full-year forecast (calendar), profit margin % (excl. VAT), and month-over-month trend (±amount & %) with a mini chart.
Cash-based: income - costs (excluding dividends/utdelning & transfers). Real from bank transactions + projected for the remaining months - updates with every new data point.
The withheld tax dropped from 24% (conservative) to 19% - the real average effective rate from your filings (tax / gross). This brings the net salary + the Skatteverket amount closer to reality.
Together with the correct gross (V2.3.129) and the 31.42% contributions, the forecast Skatteverket matches the real one (VAT remains the most variable part).
v2.3.129 - 💵 More accurate gross salary in the forecast (OB + holiday)
From analysis of real lönespecifikationer: the gross is ~30% above the base (hours × hourly rate), due to OB supplements + 12% holiday allowance.
New setting "Gross uplift %" (default 30%) applied to the forecast gross calculation for more accurate contributions + tax.
Calibration: a month's real gross = Contributions (from Skatteverket) / 0.3142. Adjust the uplift to match.
v2.3.128 - 📅 Publish schedule per week (checkbox on V##)
Every week number (V26…) in Work Hours now has a checkbox (admin only). Checked = the whole week (pass + hours) is shown to drivers; unchecked = drivers see a completely empty schedule.
Default: all weeks hidden - you work on them and publish when ready. Replaces the old automatic 7-day rule.
Initial setup: weeks up to and including 26/2026 are checked.
v2.3.127 - Skatteverket: all together on the 10th + fix "Delete all"
Fixed "Delete all" on grouped entries (it could not find the group - an index of the groups is now kept).
VAT is again together with payroll taxes in the same Skatteverket entry (on the 10th), as you actually pay it. E.g. June 10 = May payroll taxes + moms on April invoices (2 months earlier, so they have been collected).
Setting "VAT invoices: months before payment" (2) - configurable.
v2.3.126 - 💸 VAT on the correct date (separate from payroll taxes)
VAT (moms) no longer goes together with payroll taxes on the 10th. It is a separate entry with its own date: the 12th, +2 months after the income (monthly filing, turnover ≤40M). E.g. June income to VAT on August 12.
Cash method (turnover <3M): VAT is calculated from the income collected during the month.
New settings: "VAT payment day" (12) and "VAT paid N months after income" (2) - configurable if your accountant says otherwise.
v2.3.125 - Forecast: "Delete all" on grouped entries
Grouped rows (salaries, Skatteverket, generic groups) got a "Delete all" button in the header - it deletes all entries in the group in one action, in addition to deleting a single one.
v2.3.124 - 🔒 Forecast fix (the feature was inactive) + closing salary/SSN leaks
Critical: V2.3.123 was written in the wrong file. "Generate salary" uses lib/forecast_release.php (not lib/forecast.php) - so the new rates/Skatteverket were NOT applied. The logic was moved to the correct file; it now works (net salary on the 25th + Skatteverket on the 10th, and in the cron).
Security:user_profile.php returned salaries/bank details/SSN to a colleague viewing an assigned driver. Sensitive fields are now stripped for non-admin/non-self.
Security:users.php returned the SSN (personnummer) to every logged-in user. Now only admin (or the user themselves).
New configurable rates in Forecast settings: employee withheld tax (24%), employer contributions (31.42%), VAT/moms (25%), Skatteverket payment day (10), VAT month offset.
"Generate salary" now produces a net salary on the 25th (gross - 24%) and one Skatteverket entry on the 10th of the next month = employer contributions + withheld tax + VAT.
The Skatteverket entry opens on click and shows the breakdown (3 lines), like salaries. All as an estimate until the accountant's final number.
VAT is calculated from the month's income invoices (25% - for now only output VAT).
v2.3.122 - 📱 Mobile audit: readability improvements on small screens
Driver name: correct truncation (previously cut to "L..." and pushed the Available button) - added flex-1 min-w-0.
Statistics footer (Total/OBkväl/OBhelg/Röd): the long Swedish summary no longer overflows at 320px.
4 stats: smaller font on very small screens (≤359px, iPhone SE) so the numbers are not cut off.
Verified: week strip, top tabs, available-days modal, pending offers & changes table adapt correctly at 320-430px.
v2.3.121 - 🔴 Fix: broken mobile layout (content cut off on the right)
The inline position:relative added in V2.3.117 overrode (as an inline style) the sidebar's mobile position:fixed, leaving it in the flow - it pushed content to the right and cut it off on iPhone/Android.
The collapse feature is now strictly desktop-only; on mobile the sidebar returned to drawer mode.
v2.3.120 - Collapse button: panel-toggle icon at the top right of the sidebar
The collapse button changed to a classic panel-toggle icon, placed at the top right of the sidebar (instead of the arrow on the edge).
Where the route is hidden (beyond 7 days) it now shows "PASS" instead of blank - the driver knows they have a route that day, it just is not shown yet. Applies in Work Hours, My Schedule, Dashboard week strip & next shift.
The collapse button on the bar became larger and more visible (blue with white border, double arrow).
v2.3.118 - Schedule review: the driver sees what changed before re-accepting
When status = "changed", a table shows the days that changed after the last acceptance (route + hours), before the driver clicks Accept.
The route applies the 7-day rule (hidden for far-future days).
Days removed entirely are shown as "Day removed".
v2.3.117 - Sidebar collapse + route fix for User View
Fix: The route filter (7 days) was not applied in "User View" (impersonation) - fixed. Now the admin sees exactly what the driver sees.
Fix: Reference write to a stdClass did not write back reliably - direct property assignment is now used.
New: The sidebar can collapse to icons-only with the ◀ button on its right. The choice is saved in localStorage.
v2.3.116 - Drivers see the pass only 7 days ahead
Employees see their schedule (working day + start time) but the specific pass appears only within 7 days from today.
Days beyond the 7-day window: the day shows as working + start time, without the pass.
Past: fully visible (the driver saw/worked their shifts).
Exceptions (always visible): ? (shift offers), ✓/✗ (accept/reject), Sem (holiday).
Applies everywhere: Work Hours table, mobile views, Dashboard week strip, greeting card.
v2.3.115 - Hours rounding consistency
Stored work-hours now use the same per-side 15-min rounding as the on-screen totals (HoursCalc) — fixes up to 0.25h/day mismatch in the Excel payroll export for off-grid times
CSRF protection added to 8 write endpoints (forecast, available_days, schedule_ack, monthly_settings, daily_bus_counts, driver_nudges, contract_save, contract_presets) — now consistent with the other 22
Forecast CSV/PDF upload switched to API.upload so the CSRF token is attached
Impersonation actions now annotated in the activity log with the real admin's name
v2.3.113 - Performance & Visual Polish
Tailwind CDN script replaced with precompiled self-hosted CSS (44KB vs ~400KB runtime JS)
FontAwesome loads non-blocking - no longer blocks the render
Tab switches: fade-in animation (panelFadeIn) for smoother navigation
Sidebar: left accent border on the active nav item
Cards: elevated shadow on hover, smooth transitions
Buttons: snappy press animation (scale 0.97), blue button glow
Skeleton loader component (.skeleton) for loading states
Custom scrollbar (thin, elegant)
Input focus ring (subtle blue glow instead of browser default)
Modal backdrop blur
Font anti-aliasing + text rendering optimization
prefers-reduced-motion support
v2.3.112 - "Available Days" button hidden for admin
Admin does not need to declare their own availability - they already have "Availability mode" in the table. The button now appears only for drivers/colleagues.
v2.3.111 - 🔗 One shared hours calculation everywhere (single source of truth)
New js/hours_calc.js -> HoursCalc.monthTotal(): the SINGLE calculation for all client totals.
The Work Hours table (desktop, mobile summary, inline, modal, export) and the Dashboard "This month" ALL go through the same function - always the same number, the same way.
Fixed an old internal inconsistency too: the mobile summary used raw minutes, while desktop rounded to 15 min - now everything is 15 min.
The PHP wh_month_total() remains a mirror for server text (nudges), kept in sync with the JS.
v2.3.110 - 🔢 Fix: Dashboard "This month" hours did not match the Work Hours total
The Dashboard showed raw SUM(hours) without lunch deduction (e.g. 218.1h), while the Work Hours table deducts lunch (203.3h). Difference = lunch deduction.
Created a shared helper wh_month_total() that reproduces EXACTLY the Work Hours tab logic (15-min rounding, skip Sem, lunch deduction for days ≥7h).
Fixed the same issue in the "Dina timmar i [month]" nudge (driver_nudges) - now all places show the same number.
v2.3.109 - "Available" button also in the Dashboard hero for quick access
Added an "Available" button at the top right of the Dashboard greeting hero - users declare availability with one tap from the landing page.
v2.3.108 - "Available Days" button returned to Work Hours (desktop) for all users
After V2.3.96 (removal from the Tasks tab), the own-row Available button existed ONLY in the mobile views of Work Hours. On desktop, users had no way to declare availability.
Added an "Available Days" button in the desktop Work Hours header, visible to everyone - it opens your own availability.
Mobile stays as it was (own-row button in the inline/card view).
v2.3.107 - 🔴 Critical: route disappeared AGAIN after an end_time/note edit (drivers)
The V2.3.100 fix was neutralized for non-admin users. The backend always put the route field in the payload of every driver edit (even when the client sent only end_time/note), with an empty value - so the UPDATE wiped the existing route to NULL.
Now the route is normalized/inserted ONLY when the client actually sends it (accept/reject pass). The end_time & note edits no longer touch the route.
It affected all drivers/employees - not just the original reporter.
Dead achievement processing removed from dashboard.php: it ran ~200 queries (catalog seed + per-user progress for 100+ achievements + cross-driver feed) on EVERY dashboard load - but the data was no longer used after V2.3.98 (moved to the Awards tab).
achievements_seed static cache: runs only once per request (previously it could run 2-3 times)
Award re-evaluation: now runs only when you open the Awards tab (not on every dashboard)
Expected: dashboard load from ~200+ queries down to ~25-30 queries
v2.3.105 - "This week" strip now fits on all phones
Bug: 7 day cells × min-width 58px = 406px, did not fit in 360px viewports - needed a horizontal swipe
Fix mobile (≤639px): `flex: 1 1 0` + `min-width: 0` so the cells share the width equally
Tighter font/padding inside week cells on mobile
Extra-tight tweak at ≤359px (iPhone SE 1) - everything looks comfortable
v2.3.104 - Dashboard compaction (info-dense, less clutter)
Card padding: 1rem to 0.625-0.875rem (30% less)
Spacing between cards: 0.875rem to 0.5rem (mobile 0.4rem)
Stat card numbers: 30px to 22px (smaller but still prominent)
Greeting card: compact padding, heading 18px instead of 22px, avatar 44px instead of 56px
Section headers inside cards: 10px font (from 12)
Week strip day cells: padding tighter, min-width 50px
Todays team chips: pad 4x8px instead of 6x10px
All the polish from V2.3.102/103 is kept (Inter font, rarity glows, gradient accents)
v2.3.103 - 🎨 Professional platform mode (custom font + redesigned cards)
Inter font (Google Fonts) enabled across the whole app - the biggest visual change. All text feels more "premium product".
Stat cards redesign: Documents/Profile/Month summary - new 30px bold numbers (from 18px), gradient top accent line, blue border on hover, lift-up effect
Notification bell: soft pulse animation when there are unread
Copy-day button: fade-in on hover with scale
Greeting avatar: soft blue glow shadow
Sidebar user block: subtle hover
v2.3.100 - 🔴 Critical: route disappeared after an end_time edit
Bug: user Ismail (and others) reported that after changing end_time, the pass (D1, D2, D3) disappeared from the cell.
Root cause: the backend did INSERT...ON DUPLICATE KEY UPDATE with ALL fields every time - prone to race conditions and opaque.
Fix: dynamic UPDATE that touches only the fields that arrive in the body. You send end_time and only end_time + hours + audit are updated. Route/start/note unchanged.
v2.3.99 - Version label visible to everyone + mobile audit fixes
Version button bottom right: more visible (frosted background pill), z-index 35, larger touch target (28px), safe-area inset on iPhone notch devices
Mobile top tabs: if they do not fit (admin with 9 tabs at 360px), they now scroll horizontally with a hidden scrollbar - instead of squashed chips
Pending offers buttons: on very small screens (≤374px) smaller font + min-width 48px
Horizontal page scroll prevention: `overflow-x: hidden` on html/body - catches any stray overflows
Modal close button: `flex-shrink: 0` so it is not compressed when the title is long
Achievement card rarity chip smaller on iPhone SE 1st (320px)
v2.3.98 - Achievements + activity feed moved to the Awards tab
Dashboard lightened - keeps only functional info for work (schedule, offers, team, docs/profile/summary, tip, earnings, nudges)
Added at the top of the Awards tab: Recent unlocks, Closest to unlock, Achievement activity feed (with Cheer button)
Achievement data fetched directly from `/api/achievements.php?action=feed` - does not rely on the dashboard response
Summary cards show only when you view your own achievements (not when viewing another driver)
v2.3.97 - Mobile polish + modern micro-interactions (14 touches)
iPhone safe-area insets: content is no longer cut off by the home indicator
Premium tap feedback: removed Androids ugly blue flash, custom subtle gray overlay
Overscroll bounce off: stops the bouncy scroll that looks like a "browser app"
Mobile typography: 15px body, better line-height, more compact card padding
Brand bar gradient: the blue top bar now has a subtle gradient + bottom border
Active tab indicator: indigo underline that slides with animation
Card press effect: subtle scale (0.98) on tap - tactile feel
Softer dividers: gray-100/200 to slate tones
Modal drag handle: subtle widening when you touch it
Bottom-sheet inner highlight: glass-like 1px white inset
Skeleton class: shimmer animation for loading states (.skeleton)
Disabled buttons: saturate(0.6) - they look genuinely inert
Hover lift: avatars/pills in feeds do a tiny lift
Mobile bottom padding: the last card has breathing room
v2.3.96 - Removed "Available" button from the Tasks tab
The `tasks-avail-btn` appeared for employees/colleagues in the Tasks header - it had no reason to exist.
Availability is now managed only from: (a) Dashboard nudge "Mark days", (b) Work Hours own-row button.
Removed: the `
v2.3.95 - "Available" button: admin-only for other users, self for everyone
Before: colleagues with `can_view_full_schedule` saw the "Available" button next to EVERY driver - the POST failed with 403 anyway.
Now: "Available" appears only for admin (for all drivers) or for a user viewing their OWN row.
The backend already enforced it (V2.3.74 audit) - this is pure UX cleanup.
v2.3.94 - Visual polish layer (more professional appearance)
12 subtle touches that turn the app from a "template" into a "platform" feel - all CSS only, no changes to JS/HTML
Antialiased text, ligatures, tabular numbers in tables/inputs (aligned digits)
Soft layered shadows instead of default tailwind shadows
Primary buttons do a subtle lift (-1px) on hover with a smooth shadow shift
Custom focus rings instead of ugly browser default outlines
Sidebar active nav with indigo accent corner + glow
Smooth panel-in animation (280ms) every time you change tab
Modal sheet slides up more elegantly (320ms cubic-bezier)
Headings with tighter letter-spacing for a premium feel
Custom WebKit scrollbar (thin, slate-tinted)
Selection color matched with brand blue
v2.3.93 - Mobile fix: panel-header is NO LONGER fixed (stops covering content)
Root cause: In V2.3.86 we made mobile-nav `position: relative` (scrolls naturally). But `.panel-header` was still `position: fixed; top: 5rem` - it stayed static at 5rem from the viewport while the blue bar scrolled away, creating gaps and hiding content.
Fix: panel-header is now `position: relative` on mobile. Everything scrolls together naturally.
Removed the `[data-panel] padding-top: 10rem` reservation that created a big white gap at the top.
Mobile audit verified: 0 critical layout issues remaining. Achievement cards have `flex-wrap` for titles, modals have `max-height: 92vh`, all touch targets ≥34px.
v2.3.92 - Fix: hover on a note showed hours instead of the note
When a cell had BOTH a note AND filled hours, two `onmouseenter` attributes were attached to the same `
` - the day-hours handler always won
Now the note has priority: if there is a note, hover shows the note. If not, hover shows the hours as before.
v2.3.91 - 102 achievements, all legendary secret (name visible, hint hidden)
+27 new achievements: total now 102 (from 75). Emphasis on hard/long-term ones.
30 legendary, all secret: the name and icon show normally, but the description becomes "🔒 Hidden - keep driving to discover" and there is no progress bar (the discovery IS the challenge).
New hard ones: Decade Driver (10 years), Bus Sage (20000h), Three Thousand Days, D1 Grandmaster (500), D-Route Legend (1000), Ghost Shift (full night), Holiday Hero+ (30 red days), Christmas/NewYear/Midsummer/Leap Day/Friday-13, Perfectionist (24 consecutive schedules), Birthday Saint (50 wishes), Mythic Unicorn (75), Completionist (all unlocked).
Catalog auto-updates in the database via achievements_seed (INSERT...ON DUPLICATE KEY UPDATE) - when the dashboard or the achievements tab loads, new ones are added automatically.
v2.3.90 - Fix "list.map is not a function" in Pick a colleague
When admin selected a colleague from the dropdown, the backend returned `list` (array) AND `unlocked` (count INT)
Frontend used `unlocked` as an array - `.map is not a function`
Fix: prefer `list` if it is an array, else `unlocked` if it is an array, else empty
Counts are recomputed from the list array - consistent across all views (own / admin-views-other / colleague-views-other)
Session contamination fix: when an admin is impersonating a user and changes the impersonated users profile, it no longer overwrites the admins session fields (display_name, color, role, etc.). The users-table row is updated normally.
Achievements on mobile: new 🏆 "Awards" button in the mobile top tab bar (previously visible only in the desktop sidebar).
Audit completed for all of todays work - 75 achievements, 52 metrics, all PHP/JS syntax clean, no dead references.
Profile photo fix: the onerror callback did not work. New approach - a circle with initials behind the img; if the img fails it becomes hidden and the initials show
New Achievements tab: 47 achievements in 9 categories (milestones, hours, days, route, time, weekend, discipline, profile, social, funny, meta)
4 rarity levels: common / rare / epic / legendary
Hidden achievements: secrets (🔒) are revealed only after unlocking
Dashboard card: recent unlocks + closest-to-unlock with progress bars
Activity feed: what colleagues unlocked recently, with a 👏 Cheer button
Birthday wishes: 💌 Wish button on the birthday card - unlocks social achievements
Auto re-evaluation every time the dashboard loads
Removed blue "Read-only view" footer bar (permissions unchanged)
DB: 4 new tables (achievements, user_achievements, achievement_congrats, birthday_wishes)
v2.3.82 - Dashboard v2: 6 new sections + English everywhere
Smart greeting: time-of-day greeting, profile photo, today's shift or day-off celebration, birthday banner, next day-off countdown
Colleague birthday alert: a separate card when a colleague has a birthday - with avatar and a prompt to send a wish
Pending shift offers: a list of all "?" passes + inline Accept/Reject buttons
This week strip: Mon-Sun 7-day strip with route + hours per day, today highlighted
Today's team: small avatars of the colleagues working today
Tip of the day: rotating UX hints (8 tips)
All UI text converted to English (greetings, nudges, popup, profile labels)
Backend: 5 new fields in dashboard.php (week_schedule, pending_offers, today_team, birthdays_today, me)
v2.3.81 - Personal dashboard returns to the Work Hours tab
The dashboard was hidden after V2.3.79 because it lived inside the Calendar tab (which we hid). Now it appears at the top of the Work Hours panel for all non-admin users.
Greeting + next shift, Schedule status (View/Accept + warning for schedule changes), Document status (OK/expiring/expired), Profile completion %, Month summary, Admin messages (popup + cards)
Refresh on every visit to the Work Hours tab - always fresh data
Admins do not see the dashboard in Work Hours (they have their own view)
v2.3.80 - Work Hours above Tasks in the navigation
Order changed: Work Hours now appears above Tasks for all users
Sidebar nav, mobile top tabs, mobile footer tabs - all 3 updated
Mobile shift accept/reject + document upload: fresh CSRF token fetched per upload, embedded in both header AND FormData body (survives proxy header stripping)
iOS HEIC camera photos now accepted (MIME whitelist + accept="image/*" triggers iOS auto-conversion)
Shift offer flow audit: drivers can only respond on own row, only on ?/✓/✗ cells, double-tap protection, correct year/month wiring
Search inputs debounced (300ms) — no more server requests on every keystroke
Notification badge polling deduplicated — mobile_driver.js listens to event instead of polling independently
Migration flag stored in DB (_schema_version) — survives server restarts
forecast_settings and excel_backup_settings cached per request (static cache)
v2.3.59 - Anställningsavtal HTML preview + driver signing
Admin opens Anställningsavtal as editable HTML form — no more PDF coordinate issues
Admin signature PNG displayed in the contract before sending
Click "Skicka till förare" → contract saved to DB, driver gets notification in profile
Driver opens contract, signs with drawn canvas signature, saved to DB
Print / Save as PDF available at any time from both admin and driver view
v2.3.58 - Bus count badge on work hours calendar
Admin can manually set a bus-count number in the corner of each day's date header
Click the badge to set/change the number; leave empty or 0 to clear
Stored in localStorage per year/month/day — persists across sessions
Badge hidden on mobile (≤768px) to keep the view clean
v2.3.57 - PERSONALKORT single-page guarantee
Screen: Preview card now sized to exact A4 proportions with a visible page boundary
Print: Auto-scales content to fit exactly one page — JS measures height before printing and applies zoom factor if needed
v2.3.56 - PERSONALKORT HTML preview form
Personalkort: Replaced FPDI overlay with an editable HTML form — click "Generera Personalkort" to open a pre-filled form, edit any field, then print to PDF
New endpoint: api/personalkort_preview.php
Anställningsavtal generation is unchanged
v2.3.55 - PERSONALKORT Timlön row fix
PERSONALKORT: Removed erroneous row skip — section divider is a visual line only (no height), Timlön and all rows below now align correctly
v2.3.54 - PERSONALKORT row alignment fix
PERSONALKORT: Fixed 1-row drift from Timlön onwards — added skip for template section divider between Kollektivavtal and Timlön rows
v2.3.53 - Bank name field
Bank name: New "Bank Name" field added to Personal tab under Bank Details
PDF: Bank name shown together with clearing number and account in PERSONALKORT
v2.3.52 - PDF overlay coordinate fix
PERSONALKORT: Fixed row spacing (dy 12.5→11.0mm) so all 17 fields align with template grid
ANSTÄLLNINGSAVTAL: Shifted all sections up 3-5mm to match actual form boxes
All sections corrected: Personal info, salary, employment form marks/dates, terms, and signatures
v2.3.51 - Automatic Database Backup
Daily auto-backup: Cron job creates compressed SQL dump every day at 03:00
Manual backup: "Create Backup Now" button in Settings with instant download
Retention: Keeps last 30 backups, auto-deletes older ones
Pure PHP: No shell_exec needed — works on all shared hosting (cPanel)
v2.3.50 - Work Hours cell notes
Notes: Admin can add notes to any shift cell via the route picker popup (Note / Edit Note button)
Badge: Cells with notes show a small yellow message icon in the top-left corner
Hover preview: Hovering over a cell with a note for 1 second shows the note text in a tooltip
Mobile: Notes visible in driver detail modal with inline edit, and note dot in monthly overview grid
Copy/paste: Notes are included when copying and pasting shift days
v2.3.49 - Duplicate generation fix + company info + signing