Changelog - IndieDesk Version History
[v1.4.0] - 2026-08-22
Added
- Google Calendar — automatic event sync — new module-wide integration replacing the "click the button every time" workflow: events are now created, updated and deleted on the user's primary Google Calendar automatically as records change. Connect once from Settings → Integrations ("Connect" button, OAuth consent), tokens are stored encrypted in the new
google_calendar_settingstable and the access token is refreshed transparently from the refresh token. Built withlaravel/socialitefor the OAuth handshake plus a thin in-house REST client (GoogleCalendarClient,Httpfacade) rather thangoogle/apiclientorspatie/laravel-google-calendar— the former is heavy, the latter authenticates from a locally generatedtoken.jsonfile, which doesn't fit a "Connect" button on a remote web app. All API traffic is outbound only, so it works behind an IP-restricted proxy. When Google Calendar isn't connected, every sync call is a silent no-op — the app behaves exactly as before - Six entities sync to Google Calendar — client follow-ups, project deadlines, tasks, meetings, payments (payment date or due date) and tax deadlines. Each gained a nullable
google_event_idcolumn linking the record to its calendar event, so later edits update that same event instead of creating a second one. Sync is triggered on create/update/delete and on the state toggles (follow-up marked done, task done/undone, meeting completed/cancelled); archiving a project removes its event and restoring it recreates it - Duplicate protection for pre-existing manual events — before creating an event, the sync searches that day for one whose title matches the record's stable title and adopts it (storing its id) instead of adding a second copy — so follow-ups that were added to Calendar by hand before this release don't end up duplicated the first time they're touched. The sequence number and the done/pending marker are deliberately kept out of that matching key (
calendarTitleBody(), new method on theCalendarEventablecontract), otherwise every renumbering would break the match - Follow-ups —
linkedincontact type — LinkedIn messages are a real first-contact channel for leads; added to the enum, the type filter, the form and the icon set (brand logo).meetingandnotewere removed from the enum in the same pass — neither describes a contact attempt, which is what the follow-up log tracks - Follow-ups — "done / to do" state (
completed) — a follow-up can now be logged as not yet done, which turns it into a future reminder: schedule the next contact, let the Google Calendar alert fire, then mark it done. The create form auto-unchecks it when a future date is picked. One-click toggle directly in the follow-up list (no modal), plus a green/amber status badge per row. All follow-up counters and filters count completed follow-ups only, so a scheduled-but-not-yet-done reminder never inflates the "contact attempts" number - Follow-ups — contact sequence number on the calendar event — the event title carries
#N(📞 #2 Follow-up: Client — Call) and the description aContact no.line, where N is the chronological position among that client's follow-ups. Adding, deleting or re-dating a follow-up shifts the others' position, so the whole client's set is re-synced on those actions rather than just the edited record - Clients index — five follow-up stat cards — never contacted / 1st contact made / 2nd contact made / exhausted (3+) / contacted today, computed by the new
ClientFollowupStatsQueryover lead and prospect clients only (active and archived clients aren't part of the pipeline). The buckets mirror the existingfollowup_statusfilter exactly, so a card's number and the filtered list always agree - Clients index — "contacted today" and "follow-up date" filters — the first narrows to clients contacted today, the second to clients with a completed follow-up on a specific calendar day
Improved
- Tasks index — Status column merged under Title ("Title/Status") and project show Tasks tab — Priority merged under Status ("Status/Priority"), same horizontal-space treatment already applied to other tables in v1.1.0
- Meetings table — row actions stacked vertically instead of side by side, so the calendar/edit/delete controls stop competing for width with the rest of the row
- "Total Clients" stat card no longer counts archived clients — it summed
active + archived; an archived client isn't a client any more, so the card now countsactiveonly
Fixed
- App timezone was hardcoded to UTC —
config/app.phpshipped'timezone' => 'UTC'and silently ignoredAPP_TIMEZONEin.env. Everytoday()/now()comparison in the app ran on UTC, so between midnight and 01:00/02:00 Italian time "today" still meant yesterday. Now readsenv('APP_TIMEZONE', 'UTC'). Found while building the "contacted today" counter, but the bug affected date logic app-wide - Dates in edit modals came back one day earlier than saved —
toFormPayload()handed the rawCarbondate tojson_encode, which serializes as UTC; with a non-UTC app timezone (see above) local midnight becomes 22:00 of the previous day, and the modal's date input then showed the wrong day.Project,Payment,TaxandCostnow format their date fields asY-m-dstrings before serializing (Taskand follow-ups already did) - Fatal error on
/taxesand/statistics—TaximplementsCalendarEventableand was not updated when the contract gainedcalendarTitleBody(), so the class could not be loaded at all and every page referencing it 500'd - 405 on saving Business Settings — the Google Calendar "Disconnect" control was a
<form>nested inside the main settings<form>(invalid HTML); browsers hoist it out and the outer form loses itsPUTmethod spoofing. Moved out of the form and submitted by id, the same pattern already used by "delete logo" - 500 when updating a meeting —
duration_minuteshad no cast and reachedCarbon::addMinutes()as a string, which recent Carbon versions reject (must be of type int|float, string given); the failure surfaced throughMeeting::getEndTime()during the calendar sync. Cast tointegeron the model, fixing it for every caller and not just the calendar path - Meetings synced as all-day events — the payload builder ignored the DTO's
isAllDayflag and always sent adaterange, dropping the meeting's actual time. Timed events now senddateTime+timeZonewith the real start/end, andlocationis forwarded when present
Maintenance
- composer require —
laravel/socialite(^5.30)
[v1.3.1] - 2026-08-16
Added
- Clients — Administration email (
email_fatturazione) — new nullable field for invoicing correspondence when it differs from the client's main email, distinct from the existing PEC; editable in the client form's Billing tab, shown as "Email Amministrazione" on the client show page, and included in the Excel export - Documents — XML uploads —
.xmladded to the accepted file types inStoreDocumentRequest, alongside PDF/JPG/PNG/WEBP/ZIP/7Z/RAR/DOC/DOCX/XLS/XLSX, still capped at 30MB
Improved
- Clients — email no longer required — the
emailcolumn is now nullable (previouslyNOT NULL UNIQUE) and the field is optional on both create and edit; a model mutator onClientstores blank input asnullrather than an empty string so the unique constraint doesn't clash across multiple clients without an email - Client show page — contact fields labeled — Email, PEC, and Email Amministrazione each show a small label above the value (
<x-email-link>gained an optionallabelprop) instead of unlabeled mailto links, so it's clear which address is which
[v1.3.0] - 2026-08-14
Added
- Tax estimation engine (regime forfettario) — new
PaymentTaxCalculator(app/Services/Taxes) estimates INPS (Gestione Separata) contributions and imposta sostitutiva on paid payments, progressively on the cumulative taxable income of the fiscal year rather than per payment in isolation, so the annual INPS ceiling and the tax base (net of INPS due so far) are handled correctly. Rates come fromBusinessSettings(profitability_coefficient, newinps_rate,substitute_tax_rate, optionalinps_ceiling) — no hardcoded fallback rate; if any is left unset, no estimate is shown anywhere rather than guessing. Specific to the Italian regime forfettario and documented as such — a different tax regime needs dedicated development, not a config change - "Da accantonare" (set aside) shown wherever a payment amount is shown — inline under the amount (not a separate table column — first design, changed after review) on the project show Payments tab, the global Payments index, the client show Recent Payments card, and the Dashboard Recent Payments list, all via one shared Blade partial
- Net profit (after estimated tax) wherever profit is reported business-wide: Dashboard "Profit This Month" card, Statistics summary card, monthly breakdown table and PDF export — net of estimated tax when the fiscal rates are configured, gross otherwise, computed once per query (
display_profit) instead of duplicated in every view. Deliberately left gross on the per-project Profit tab and the "Top 10 Most Profitable Projects" table, where a project's share of the cumulative/progressive tax isn't a well-defined number - Tax Fund tracker — new
TaxFundMovementmodel/table +TaxFundService; a card on the Statistics page compares the balance of a dedicated tax savings account (manually logged deposits/withdrawals) against the current year's estimated liability, net of whatever's already been paid through the Taxes module (matched byreference_year, not payment date, so a late payment settling a past year's liability never distorts the current year's target). Marking aTaxrecord as paid auto-creates/updates/removes the matching withdrawal (TaxFundService::syncFromTax()) — no double data entry between the two modules; auto-generated movements can't be deleted directly, only by unmarking the source tax as unpaid
Fixed
- Two pre-existing migrations broke the SQLite test database (unrelated to the tax feature, found while adding tests for it) —
costs.recurringneeded an explicitdropIndexbeforedropColumn, and a MySQL-onlyALTER TABLE ... ENUM(...)onclients.statushad no SQLite equivalent. Fixed by guarding the latter onDB::getDriverName(); the test suite now actually boots and runs on SQLite (it silently failed on every single test before this)
Maintenance
- composer update — laravel/framework v12.65.0→v12.66.0, laravel/mcp v0.9.2→v0.9.3, laravel/pint v1.30.4→v1.30.5, laravel/sail v1.65.0→v1.66.0, league/commonmark 2.9.0→2.10.0, myclabs/deep-copy 1.13.4→1.14.0, stripe/stripe-php v21.1.1→v21.2.0
- npm update — alpinejs 3.15.12→3.16.1, laravel-vite-plugin 3.1.3→3.2.0, rolldown 1.2.3→1.2.4 (+ all platform bindings), @oxc-project/types 0.143.0→0.144.0, ansi-regex 6.2.2→6.3.0, baseline-browser-mapping 2.11.12→2.11.14, electron-to-chromium 1.5.402→1.5.406, update-browserslist-db 1.3.0→1.3.1
[v1.2.0] - 2026-08-08
Added
- Stripe integration — automatic payment recording from your own SaaS products — new module (
docs/modules/stripe);POST /stripe/webhookreceivesinvoice.paidevents and createsPaymentrecords automatically, no manual entry. Project routing is metadata-based: tag each Stripe Product withindiedesk_project_id(in the Stripe Dashboard, no local mapping table) — supports SaaS setups with one Product per pricing tier, all tagged with the same project id. Client matching, in order:stripe_customer_id(once known) → email (reliable at payment time — the SaaS tenant already exists) → name (fallback for prospects still on a placeholder email) → create new. Any match or creation sets the client's status toactiveregardless of prior status. Idempotent viapayments.reference(the Stripe invoice id) — safe against Stripe's webhook retries. Built withstripe/stripe-php, verified with real Stripe CLI test-mode payments during development (not just synthetic fixtures) clients.stripe_customer_id— nullable, unique column; newClient::getStripeDashboardUrl()(test/live aware) andClient::totalProjectsCount()(direct + SaaS-pivot projects)- Client show page — "Connected to Stripe" badge — shown in the header when
stripe_customer_idis set, links directly to that customer's record in the Stripe Dashboard
Fixed
- Client show header project count wrong for SaaS-only clients — read
$client->projects->count()(direct relation only); a client linked to a project solely through the SaaS pivot showed 0. Now usesClient::totalProjectsCount()
Maintenance
- composer require —
stripe/stripe-php(^21.1)
[v1.1.0] - 2026-08-08
Added
- Project type:
saas— multi-client projects — a project can now betype = 'saas'and linked to multiple clients instead of the single legacyclient_id; newclient_projectpivot table (Project::clients()/Client::saasProjects()belongsToMany), synced onstore/updateonly whentype === 'saas'(cleared viasync([])when a project's type changes away fromsaas, so no stale links survive a type switch);Project::getClientLabel()centralizes "which client(s) to show" (single client, comma-joined SaaS clients, or null for internal) and is used everywhere a project's client is displayed instead of readingproject->clientdirectly - Payments — per-payment client attribution — nullable
client_idonpayments(Payment::client()); on SaaS projects the payment form shows a dropdown restricted to that project's linked clients ("this payment belongs to...");Payment::getClientLabel()prefers the payment's own client, falling back to the project's — used in dashboard lists and statistics tables so a SaaS payment attributed to a specific client shows that client, not a blank field - Project modal — client picker switches with project type — non-SaaS types keep the existing single-client typeahead (
clientSearch.js); selectingsaasswaps it for a new multi-client picker (clientMultiSearch.js) that accumulates selections as removable chips and submitsclient_ids[], instead of replacing a single value - Documents — Word/Excel inline preview —
.docx/.xlsx/.xlsuploads (previously rejected outright by validation) are now accepted and, on preview, converted to PDF on the fly by the newOfficeDocumentConverterservice (phpoffice/phpword+phpoffice/phpspreadsheet, rendered through the existing Dompdf dependency — no external binary, no LibreOffice to install/maintain) so they render inline like a native PDF instead of forcing a download; converted PDFs are cached instorage/app/previews/and purged when the source document is deleted. Shared by project Documents, Task Documents, and Business Documents. Legacy.docis accepted but not convertible (no reliable PHP reader for the old binary format); PowerPoint was evaluated and deliberately left unsupported — no PHP library produces a reliable PDF export from.pptx - Clients — Excel export — "Export Excel" button on the clients index, honors whatever filters are currently applied (reuses
ClientIndexQuery::query(), extracted fromhandle()for this purpose); built with the samephpoffice/phpspreadsheetdependency as the Word/Excel preview above; headers, sheet title, filename and cell values are all resolved via__()— follows the requesting user's locale, not hardcoded to Italian
Improved
- Project show page — vertical tab navigation — the horizontal tab bar (top of the content area) is now a vertical nav in the sidebar (
_tabs-nav.blade.php);activeTabmoved to a sharedx-dataon the common parent grid so the sidebar nav and the tab content (now siblings, not nested) stay in sync without an event bus - Project show page — Overview tab reorganized — client info card now sits alone at the top (previously shared a 3-column row with Quick Info and Quick Links, which didn't scale); Quick Info and Quick Links moved below Description/Notes; the SaaS client card itself is a scrollable name-only list linking to each client's show page (tested with 200 linked clients) instead of the full
<x-client-summary>contact card repeated per client - Clients table — "Creato il" column merged under Nome — same treatment applied consistently elsewhere this release (Tasks: Tipo under Titolo; Payments: Metodo under Importo) to reclaim horizontal space
- Project client filter/search now SaaS-aware —
Project::scopeForClient()andProjectIndexQuery's search now also match via theclientspivot, not just the directclient_id
Fixed
- Client show page missing SaaS-linked projects —
ClientShowQuerycomputed the client's project set from$client->projects()only (the directclient_idrelation); a client linked to a project solely through the SaaS pivot showed zero projects, and by extension zero tasks/payments/costs/documents from that project. Now merges in$client->saasProjects()->pluck('projects.id') /clients?client_id=...filter was a no-op —Project::scopeForClient()existed butProjectIndexQuerynever called it; the "view all projects" link from a client's show page silently ignored the filter. Now wired viawhen(request('client_id'), ...)
Maintenance
- composer require —
phpoffice/phpspreadsheet(^5.9),phpoffice/phpword(^1.4), plus transitivecomposer/semver3.4.4,maennchen/zipstream-php3.2.2,markbaker/complex3.0.2,markbaker/matrix3.0.1,phpoffice/math0.3.0 - composer update — barryvdh/laravel-debugbar v4.4.0→v4.4.1, guzzlehttp/guzzle 7.15.2→7.15.3, guzzlehttp/promises 2.5.1→2.5.2, laravel/boost v2.4.13→v2.5.3, laravel/framework v12.64.0→v12.65.0, laravel/mcp v0.9.1→v0.9.2, laravel/pint v1.30.3→v1.30.4, laravel/prompts v0.3.21→v0.3.22, laravel/roster v0.5.1→v1.0.0, laravel/sail v1.64.0→v1.65.0, league/commonmark 2.8.3→2.9.0, nesbot/carbon 3.13.1→3.13.2, pestphp/pest v4.7.7→v4.7.8, symfony/console v7.4.15→v7.4.16, symfony/http-foundation v7.4.15→v7.4.16, symfony/http-kernel v7.4.15→v7.4.16, symfony/mime v7.4.15→v7.4.16, symfony/translation v8.1.1→v8.1.4
- npm update — vite 8.2.0→8.2.1, vue 3.5.40→3.5.41, rolldown 1.2.2→1.2.3, postcss 8.5.25→8.5.26, browserslist 4.28.7→4.28.8, dompurify 3.4.12→3.4.13, nanoid 3.3.17→3.3.18, and other transitive browser-compat/build tooling packages
[v1.0.19] - 2026-04-19
Maintenance
-
composer update
- Upgrading symfony/polyfill-mbstring (v1.33.0 => v1.36.0): Extracting archive
- Upgrading php-debugbar/php-debugbar (v3.7.0 => v3.7.4): Extracting archive
- Upgrading voku/portable-ascii (2.0.3 => 2.1.0): Extracting archive
- Upgrading symfony/polyfill-php80 (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-ctype (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-uuid (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-php85 (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-php84 (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-php83 (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-intl-normalizer (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-intl-idn (v1.33.0 => v1.36.0): Extracting archive
- Upgrading symfony/polyfill-intl-grapheme (v1.33.0 => v1.36.0): Extracting archive
- Upgrading nesbot/carbon (3.11.3 => 3.11.4): Extracting archive
- Upgrading laravel/serializable-closure (v2.0.10 => v2.0.12): Extracting archive
- Upgrading barryvdh/laravel-debugbar (v4.2.4 => v4.2.6): Extracting archive
- Upgrading sebastian/environment (8.0.4 => 8.1.0): Extracting archive
- Upgrading sebastian/comparator (7.1.4 => 7.1.6): Extracting archive
- Upgrading phpunit/php-code-coverage (12.5.3 => 12.5.6): Extracting archive
- Upgrading phpunit/phpunit (12.5.16 => 12.5.23): Extracting archive
- Upgrading laravel/mcp (v0.6.5 => v0.6.7): Extracting archive
- Upgrading laravel/boost (v2.4.1 => v2.4.4): Extracting archive
- Upgrading laravel/sail (v1.56.0 => v1.57.0): Extracting archive
- Upgrading nunomaduro/collision (v8.9.2 => v8.9.3): Extracting archive
- Upgrading webmozart/assert (2.1.6 => 2.3.0): Extracting archive
- Upgrading pestphp/pest-plugin-arch (v4.0.0 => v4.0.2): Extracting archive
- Upgrading pestphp/pest (v4.4.5 => v4.6.3): Extracting archive
[v1.0.18] - 2026-04-06
Improved
- AtecoCode —
StoreAtecoCodeRequestForm Request — inline validation moved fromAtecoCodeController::store()to a dedicatedStoreAtecoCodeRequest; controller now calls$request->validated()
Maintenance
- composer update — bacon/bacon-qr-code v3.0.4→v3.1.1, barryvdh/laravel-debugbar v4.2.1→v4.2.4, laravel/mcp v0.6.4→v0.6.5, laravel/sail v1.55.0→v1.56.0, nunomaduro/collision v8.9.1→v8.9.2, pestphp/pest v4.4.3→v4.4.5, php-debugbar/php-debugbar v3.6.1→v3.7.0, phpunit/phpunit 12.5.14→12.5.16, symfony/clock v8.0.0→v8.0.8, symfony/console v7.4.7→v7.4.8, symfony/css-selector v8.0.6→v8.0.8, symfony/error-handler v7.4.4→v7.4.8, symfony/event-dispatcher v8.0.4→v8.0.8, symfony/finder v7.4.6→v7.4.8, symfony/http-foundation v7.4.7→v7.4.8, symfony/http-kernel v7.4.7→v7.4.8, symfony/mailer v7.4.6→v7.4.8, symfony/mime v7.4.7→v7.4.8, symfony/process v7.4.5→v7.4.8, symfony/routing v7.4.6→v7.4.8, symfony/string v8.0.6→v8.0.8, symfony/translation v8.0.6→v8.0.8, symfony/uid v7.4.4→v7.4.8, symfony/var-dumper v7.4.6→v7.4.8, symfony/yaml v8.0.6→v8.0.8
[v1.0.17] - 2026-03-31
Added
- Business Settings — Billing Tool URL — new
billing_tool_urlfield in the Integrations tab; stores the URL of the invoicing tool used to issue bills; migration adds nullablebilling_tool_urlcolumn tobusiness_settings; validated asnullable|url|max:255; translations added to all 13 language files - Payments — Billing Tool button — row action button in the project payments table; opens
billing_tool_urlin a new tab; visible only when the URL is configured; injected via View Composer inAppServiceProvideras$billingToolUrl(no extra DB query, reuses the existingBusinessSettings::current()singleton) - Task type:
maintenance— new type for package/software updates and dependency maintenance; added toTask::TYPESand to all 13lang/*/tasks.phpfiles
Improved
- Payments — Google Calendar button for pending payments — now visible on any payment with
paid_atordue_date; usespaid_atif collected, falls back todue_datefor pending - Payments — row actions stacked vertically — Google Calendar, Billing Tool, Edit, and Delete icons now in a vertical column for a more compact layout
- Payments —
referenceandnotesplaceholders — updated in all 13 languages to reflect actual use (e.g. "Server consulting, Feature development..." / "Description of work performed...");noteslabel updated to "Note / Descrizione" in Italian
Maintenance
- composer update — barryvdh/laravel-debugbar v4.1.3→v4.2.1, brianium/paratest v7.19.2→v7.20.0, php-debugbar/php-debugbar v3.5.1→v3.6.1
[v1.0.16] - 2026-03-29
Improved
- Client name links to client show page — in
<x-client-summary>, the client name is now a clickable link toclients.show; applies everywhere the component is used (project show page, etc.) - Task row actions stacked vertically — Google Calendar, edit, and delete icons in the task table are now in a vertical column instead of horizontal row for a more compact layout
Maintenance
- composer update — brianium/paratest v7.19.0→v7.19.2, laravel/boost v2.3.4→v2.4.1, laravel/framework v12.55.1→v12.56.0, laravel/mcp v0.6.3→v0.6.4, laravel/prompts v0.3.15→v0.3.16, laravel/sail v1.54.0→v1.55.0, league/commonmark 2.8.1→2.8.2, league/flysystem 3.32.0→3.33.0, pestphp/pest v4.4.2→v4.4.3, phpunit/phpunit 12.5.12→12.5.14, psy/psysh v0.12.21→v0.12.22
[v1.0.15] - 2026-03-19
Added
- Task Documents sub-module — file attachments for tasks;
task_documentstable withtask_id,name,file_path,uploaded_at,notes; full upload/preview/download/delete lifecycle viaTaskDocumentControllerandTaskDocumentService(both underTasks/namespace);StoreTaskDocumentRequestvalidates file (30MB max, pdf/jpg/jpeg/png/webp/zip/7z/rar);Task::taskDocuments()hasMany relationship;toFormPayload()extended to include documents array; translations in all 13 languages (lang/*/task_documents.php) - Document indicators on task table rows — both project show and global task index now show: eye + download icons for single attachment, paperclip with numeric badge for multiple attachments;
TaskIndexQueryandProjectShowQueryeager-loadtaskDocuments - Cost types:
hardwareandmeal—hardwarefor equipment purchases (keyboard, mouse, monitor, etc.),mealfor business meals;<x-costs.type-badge>updated with slate badge (🖥️) and lime badge (🍽️); translations added to all 13 language files - Preview/download on client show cards — payments, costs, tasks, and documents cards on the client show page now display inline preview and download buttons when an attachment is present (
hasInvoice(),hasReceipt(),taskDocuments);ClientShowQueryeager-loadstaskDocumentsto prevent N+1; task rows follow the same single/multiple document pattern as the task index table - Preview/download on dashboard list cards — same inline preview/download buttons added to recent payments, recent costs, and tasks due soon cards;
DashboardListsQueryeager-loadstaskDocumentsongetTasksDueSoon() - Google Calendar embed URL via env —
GOOGLE_CALENDAR_EMBED_URLenv variable replaces the hardcodedsrcincalendar/index.blade.php; added to.env.example; configuration docs updated
Maintenance
- composer update — bacon/bacon-qr-code v3.0.3→v3.0.4, barryvdh/laravel-dompdf v3.1.1→v3.1.2, laravel/boost v2.3.1→v2.3.4, laravel/framework v12.54.1→v12.55.1, laravel/mcp v0.6.2→v0.6.3, laravel/prompts v0.3.14→v0.3.15, laravel/sail v1.53.0→v1.54.0, league/uri 7.8.0→7.8.1, league/uri-interfaces 7.8.0→7.8.1, livewire/blaze v1.0.8→v1.0.10, phpdocumentor/reflection-docblock 6.0.2→6.0.3, pragmarx/google2fa-laravel v2.3.0→v2.3.1
- npm update — 12 packages updated
[v1.0.14] - 2026-03-16
Added
- Top 10 most profitable projects on statistics page — annual view only; table shows project, client, income, costs and profit ranked by profit descending; powered by new
TopProjectsQuerysub-query; each project name links toprojects.show; included in PDF export as well - Dashboard list rows clickable — all 5 quick lists (tasks due soon, upcoming meetings, recent payments, recent costs, overdue payments) are now fully clickable links to the relevant project tab (
projects.show?tab=*) - Dashboard list badges — tasks show
<x-tasks.type-badge>, meetings show<x-meetings.status-badge>, payments show<x-payments.method-badge>, costs show<x-costs.type-badge>; no inline badge logic in Blade - Client show list rows clickable — all 5 lists (tasks, meetings, payments, costs, documents) on the client show page now link to the relevant project tab
Fixed
payments.method_not_settranslation — default label was "Da incassare" (wrong semantic); corrected to "Non specificato" / "Not specified" across all 13 languages
[v1.0.13] - 2026-03-15
Added
- Monthly detail tables on statistics page — when a specific month is selected, two tables appear below the summary cards: costs (date, project/client, type, amount) and payments (date, project/client, amount); projects without a client show "Internal"; powered by new
MonthlyDetailQuerysub-query - Monthly detail in PDF export — same two tables included in the PDF when exporting a single month, followed by the month profit line; all values come from the backend (
$stats['summary']), no logic in the view - Task types:
hardwareanddocumentation— added toTask::TYPESand to all 13lang/*/tasks.phpfiles - Cost type
travelrelabeled — now shown as "Trasporti/Viaggi" to cover travel, commute, and transit subscriptions
Maintenance
- npm update — 2 packages updated
- composer update —
livewire/blazev1.0.7 → v1.0.8,sebastian/environmentv8.0.3 → v8.0.4
[v1.0.12] - 2026-03-14
Added
- Lead Follow-up module — full CRUD for follow-up logs on lead clients;
client_followupstable withtype(call, email, whatsapp, meeting, note),note,contacted_at;ClientFollowupmodel implementsCalendarEventable - Follow-up section on client show page — amber-bordered section visible only when
$client->isLead(); includes quick action buttons (call, WhatsApp, email) and a chronological follow-up log; disappears automatically once the client is converted to active clientFollowupModalAlpine.js component —resources/js/components/clientFollowupModal.js; single modal handles create and edit; registered viaAlpine.data()inapp.js; usesdata-action/data-payloadpattern (no inline JS in Blade)<x-followup-type-icon>Blade component — accepts:typeprop (call,email,whatsapp,meeting,note); replaces@switch/@caseblocks with a PHP array map; reused in follow-up list rows and quick-action buttons- Google Calendar integration for follow-ups — each follow-up row has an "Add to Calendar" link; pre-filled as an all-day event on
contacted_atviaClientFollowup::googleCalendarUrl() Client::isLead()helper — returnstrueifstatus === 'lead'; single source of truth for conditional UI in Blade- Follow-up translations —
followupkey group added to all 13lang/*/clients.phpfiles (type labels, action labels, modal titles, validation messages)
Improved
Client::whatsappUrl()model method — WhatsApp URL building logic (phone_prefix+phone→wa.me/URL) moved from Blade component to the model; single source of truth used by<x-whatsapp-link>,_quick-actions.blade.php, and any other consumer<x-whatsapp-link>refactored — now acceptshref(pre-built URL) and slot text instead ofphone+prefixprops; component is pure display with no URL logicClientStatsQuery— leads excluded from total — total client count now covers onlyactiveandarchivedstatuses; leads are prospects, not yet clients; calculation moved entirely insideClientStatsQuery::handle(), removed from the controller
[v1.0.11] - 2026-03-13
Added
- Inline field editing — Project overview tab — description and notes fields on the project show page are now editable inline without opening the modal; a dedicated
PATCH /projects/{project}/fieldendpoint handles the update viaPatchProjectFieldRequest(allowlist:description,notes); thepatchField()method was added directly toProjectController inlineFieldAlpine.js component — newresources/js/components/inlineField.jsregistered asAlpine.data('inlineField', inlineField)inapp.js; acceptspatchUrl,fieldName,initialValue,msgSaved,msgErroras parameters; translated messages are passed from Blade to avoid hardcoding strings in JSui.saved/ui.error_savingtranslation keys — added to all 13 language files (lang/*/ui.php)- Task types:
marketingandimprovement— added toTask::TYPESconstant and to all 13lang/*/tasks.phpfiles tasks.due_todaytranslation key — added to all 13lang/*/tasks.phpfiles; shown on the due date badge when the task deadline is today
Improved
- Inline text newline preservation — description and notes display divs use
whitespace-pre-wrapso saved newlines render correctly withoutnl2br - Project show tab limit raised to 50 —
ProjectShowQuery::$limitincreased from 10 to 50; all "View all" threshold checks in_tab-tasks,_tab-meetings,_tab-payments,_tab-costs,_tab-documentsupdated from> 10to> 50 - Clickable project name in project index — project name in
_row-name.blade.phpis now a link toprojects.show - Clickable client name in client cells —
x-client-name-cellcomponent andx-projects.client-badgecomponent now link toclients.show - Clickable project column in task / meeting / payment / cost / document indexes —
_row-project.blade.phpin all five modules now links directly toprojects.showwith the relevant tab pre-selected (tab=tasks,tab=meetings, etc.)
Fixed
- Task due date badge — overdue vs. today —
due-date.blade.phpnow checksisToday()beforeisPast(); tasks due today show "Da fare oggi" / "Due today" instead of "Overdue" - Trix dark mode link dialog —
.trix-dialog,.trix-dialog input, and.trix-dialog .trix-buttonnow have explicit dark-mode overrides inapp.css; the dialog is no longer invisible on dark backgrounds
[v1.0.10] - 2026-03-12
Added
- Projects — Editor tab — new rich text editor tab on the project show page powered by Trix; supports bold, italic, links, headings, lists, quotes and inline image upload (PNG, JPG, GIF, max 20MB); content saved as HTML in the new
editor_notescolumn (separate from the plain-textnotesfield) - Private image storage for editor — images uploaded via the editor are stored privately (
localdisk,editor-images/{project_id}/); served through an authenticated route (GET /projects/{project}/editor/images/{filename}) withCache-Control: private ProjectEditorController—update,uploadImage,serveImagemethods;UpdateEditorRequest+StoreEditorImageRequestfor validation- Trix dark mode — full dark mode override for Trix toolbar and editor area; undo/redo group and file input hidden via CSS
- Tab navigation overflow fix —
_content-tabs.blade.phpnav wrapper now usesoverflow-x-auto+min-w-maxto prevent overflow at lower browser zoom levels
Updated
- nesbot/carbon (3.11.1 → 3.11.3)
- laravel/framework (v12.54.0 → v12.54.1)
- prism-php/prism (v0.99.21 → v0.99.22)
- laravel/mcp (v0.6.1 → v0.6.2)
- laravel/boost (v2.2.3 → v2.3.1)
- laravel/breeze (v2.4.0 → v2.4.1)
- laravel/pint (v1.27.1 → v1.29.0)
- livewire/blaze (v1.0.4 → v1.0.7)
- pestphp/pest (v4.4.1 → v4.4.2)
[v1.0.9] - 2026-03-11
Added
- Taxes module — full CRUD for tax payment tracking: amount, description, due date, paid date, reference year, optional notes, and document attachment (upload / preview / download); four stat cards (total all time, this year, unpaid amount, count this year); filters by year, paid status, and free-text search
- Tax document attachment — upload PDF/image documents per tax record; stored locally with a locale-aware filename prefix; supports preview, download and delete
- Google Calendar integration for taxes — each unpaid tax shows a calendar icon next to the due date; clicking it opens Google Calendar with the event pre-filled (title includes date, description and reference year; body includes all tax details and notes)
- Taxes translations — full
taxes.phplang file in all 13 supported languages (it, en, de, fr, es, pt, nl, pl, ro, ru, uk, da, zh)
Improved
- Project type badge —
<x-projects.type-badge>now displayed in the project column of all index tables: tasks, payments, costs, meetings, timesheets, documents - Timesheets index — project column now shows client name when a project is linked to a client (was missing)
Fixed
- Statistics MySQL compatibility —
MonthlyBreakdownQuerywas using SQLite-onlystrftime(); replaced with adateExpr()helper that outputsDATE_FORMATfor MySQL andstrftimefor SQLite
[v1.0.8] - 2026-03-10
Fixed
- MySQL compatibility — dashboard and statistics charts now work correctly with both SQLite and MySQL
Updated
- barryvdh/laravel-debugbar (v4.1.1 → v4.1.3)
- guzzlehttp/psr7 (2.8.0 → 2.9.0)
- laravel/breeze (v2.3.8 → v2.4.0)
- laravel/framework (v12.53.0 → v12.54.0)
- laravel/mcp (v0.6.0 → v0.6.1)
- laravel/prompts (v0.3.13 → v0.3.14)
[v1.0.7] - 2026-03-09
Added
- GitHub Integration — Repository tab — each project with a GitHub
repo_urlnow shows a dedicated Repository tab with: commit activity heatmap (52 weeks × 7 days), recent commits list (author, relative date, SHA linked to GitHub), and repo info badges (stars, forks, default branch); data is lazy-loaded via a JSON endpoint (GET /projects/{project}/repository) and cached 10 minutes server-side - GitHubService — new
app/Services/GitHub/GitHubService.phpwrapping the GitHub REST API (commit_activity,commits, repo info endpoints) with per-repo cache and silent failure handling - Business Settings — Integrations tab — new tab in Business Settings for third-party integrations; first integration: GitHub PAT (
github_patfield) with required scopes info box repositoryTabAlpine.js component — registered inapp.js, handles fetch, loading/error states, heatmap color logic, month label computation and relative date formatting
[v1.0.6] - 2026-03-08
Added
- Dashboard — Recent Costs list — new
_recent-costs.blade.phpcard showing the 5 most recent registered costs (amount in red with-, project name, type badge, date); addedgetRecentCosts()toDashboardListsQueryand included costs inhydrateProjects()batch loading to prevent N+1 queries
Fixed
- Statistics PDF styles — aligned base styles (font stack, units, line-height) with the invoice PDF for consistent typography across all PDF exports
Updated
- Dashboard lists layout — reorganised from a flat 2×2 grid to 3 rows: tasks+meetings (2 cols), recent payments+recent costs (2 cols), overdue payments (full width)
- barryvdh/laravel-debugbar (v4.0.10 → v4.1.1)
- dompdf/dompdf (v3.1.4 → v3.1.5)
- laravel/boost (v2.2.1 → v2.2.3)
- laravel/mcp (v0.5.9 → v0.6.0)
- laravel/roster (v0.5.0 → v0.5.1)
- league/commonmark (2.8.0 → 2.8.1)
- livewire/blaze (v1.0.3 → v1.0.4)
- php-debugbar/php-debugbar (v3.4.1 → v3.5.1)
- phpdocumentor/reflection-docblock (6.0.1 → 6.0.2)
- prism-php/prism (v0.99.20 → v0.99.21)
- psy/psysh (v0.12.20 → v0.12.21)
- sabberworm/php-css-parser (v9.2.0 → v9.3.0)
- symfony/console (v7.4.6 → v7.4.7)
- symfony/http-foundation (v7.4.6 → v7.4.7)
- symfony/http-kernel (v7.4.6 → v7.4.7)
- symfony/mime (v7.4.6 → v7.4.7)
[v1.0.5] - 2026-03-02
Added
- Timesheet PDF report — download a monthly worked hours report as PDF directly from the project timesheet tab; includes business info, project/client details, worked days table, and total earnings; filename is locale-aware (
{report-title-slug}-{project-slug}-{mm}-{yyyy}.pdf) - Blaze — enabled
livewire/blazefunction compiler on all anonymous Blade components (resources/views/components/) for faster rendering pipeline
[v1.0.4] - 2026-03-01
Added
- Timesheets module — monthly worked hours tracking per project with daily hour grid, hourly rate snapshot, and reactive earnings calculation (Alpine.js)
- Timesheets global index — paginated list with filters (project name, month, year), four stat cards (hours/earnings this month and this year), and direct links to the correct month on the project show page
[v1.0.3] - 2026-02-28
Fixed
- Clients / Projects index — stat cards showed filtered counts when filters were active; stats now always reflect the real unfiltered totals
- Invoice PDF — bold text (
<strong>,.invoice-number,.description-amount, etc.) was not rendering as bold because the@font-facebold declaration pointed to the same Regular TTF file
Updated
- Invoice PDF font — switched primary font from Noto Sans CJK to DejaVu Sans for cleaner, more professional typography; Noto Sans CJK retained as fallback for CJK characters
- barryvdh/laravel-debugbar (v4.0.9 → v4.0.10)
- laravel/boost (v2.1.7 → v2.2.1)
- laravel/framework (v12.52.0 → v12.53.0)
- laravel/roster (v0.4.0 → v0.5.0)
- laravel/serializable-closure (v2.0.9 → v2.0.10)
- league/flysystem (3.31.0 → 3.32.0)
- livewire/blaze (v1.0.1 → v1.0.3)
- nette/schema (v1.3.4 → v1.3.5)
- pestphp/pest-plugin-laravel (v4.0.0 → v4.1.0)
- php-debugbar/php-debugbar (v3.4.0 → v3.4.1)
- phpdocumentor/reflection-docblock (5.6.6 → 6.0.1)
- phpdocumentor/type-resolver (1.12.0 → 2.0.0)
- prism-php/prism (v0.99.19 → v0.99.20)
- sabberworm/php-css-parser (v9.1.0 → v9.2.0)
- symfony/console (v7.4.4 → v7.4.6)
- symfony/css-selector (v8.0.0 → v8.0.6)
- symfony/finder (v7.4.5 → v7.4.6)
- symfony/http-foundation (v7.4.5 → v7.4.6)
- symfony/http-kernel (v7.4.5 → v7.4.6)
- symfony/mailer (v7.4.4 → v7.4.6)
- symfony/mime (v7.4.5 → v7.4.6)
- symfony/routing (v7.4.4 → v7.4.6)
- symfony/string (v8.0.4 → v8.0.6)
- symfony/translation (v8.0.4 → v8.0.6)
- symfony/var-dumper (v7.4.4 → v7.4.6)
- symfony/yaml (v8.0.1 → v8.0.6)
- webmozart/assert (2.1.5 → 2.1.6)
[v1.0.2] - 2026-02-18
Updated
- barryvdh/laravel-debugbar (v4.0.8 → v4.0.9)
- brianium/paratest (v7.17.0 → v7.19.0)
- laravel/boost (v2.1.4 → v2.1.7)
- laravel/framework (v12.51.0 → v12.52.0)
- laravel/mcp (v0.5.7 → v0.5.9)
- nunomaduro/collision (v8.8.3 → v8.9.1)
- nunomaduro/termwind (v2.3.3 → v2.4.0)
- pestphp/pest (v4.3.2 → v4.4.1)
- phpunit/phpunit (12.5.8 → 12.5.12)
- ta-tikoma/phpunit-architecture-test (0.8.6 → 0.8.7)
- webmozart/assert (2.1.3 → 2.1.5)
[v1.0.1] - 2026-02-16
Updated
- barryvdh/laravel-debugbar (v4.0.7 → v4.0.8)
- laravel/boost (v2.1.3 → v2.1.4)
- laravel/mcp (v0.5.6 → v0.5.7)
- nette/utils (v4.1.2 → v4.1.3)
- php-debugbar/php-debugbar (v3.3.1 → v3.4.0)
- thecodingmachine/safe (v3.3.0 → v3.4.0)
- webmozart/assert (2.1.2 → 2.1.3)
[v1.0.0] - 2026-02-11
Added
- Initial documentation structure
- Architecture
- Released modules (backend/frontend)
- 2FA
- AI
- Calendar
- Clients
- Company
- Costs
- Dashboard
- Documents
- Invoice
- Labels
- Meetings
- Payments
- Projects
- Receipts
- Stats
- Tasks