From 06668d5004afae0665f14555ab673b0ad06edbaa Mon Sep 17 00:00:00 2001 From: johnnyq Date: Tue, 27 Oct 2026 11:53:27 -0400 Subject: [PATCH] Get started: Do it later and Skip all at the top while a step is still to do - Do it later goes to the dashboard and leaves Finish setting up there, Skip all asks first then skips every step (done ones too, so one that comes undone stays off the dashboard), goes to the dashboard and is logged with what was left to do; Go to ITFlow once every step is done or skipped; the footer's Don't show it on the dashboard removed (the dashboard card's x still dismisses); setup_cli.php --skip-get-started leaves Get started in Admin instead of opening it on first sign-in or showing it on the dashboard; CHANGELOG updated --- CHANGELOG.md | 7 ++++--- admin/getting_started.php | 28 ++++++++++++++++++---------- admin/post/getting_started.php | 15 ++++++++++++++- css/itflow_custom.css | 6 ++++++ functions/getting_started.php | 30 ++++++++++++++++++++++++++++-- scripts/setup_cli.php | 15 +++++++++++++-- 6 files changed, 83 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3c4cb14..d886bc3de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1061,7 +1061,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Region starts on the currency of the company's country, a language of that country, your browser's where there is one (French (Canada) for a French browser in Canada, English (Canada) for others) and your browser's timezone - or the country's, when it has only one - and shows money and a date as they will look. - Finish says what to know before signing in - that sign-in needs HTTPS when the page came over http://, with the config.php line for a test install; your backup key, made there and then so it can be kept, with a copy button; and the address links in emails go to - then what to do once signed in: cron (its crontab line, to copy), mail, backups, starter content and your team. Telling the ITFlow project about the install is off unless switched on, and shows exactly what it sends. - Restore a backup: drop it on the page or choose it. One larger than the server takes through a browser is pointed at the command line before it is sent; otherwise the upload shows how far it has got, then that it is restoring, and a page says when it is done. - - `scripts/setup_cli.php` runs the wizard's checks and checks what it is given the same way, asks again for an answer that is wrong (the password twice, not shown), suggests the language, currency and timezone from the country, and ends with the backup key and the crontab line. `--non-interactive` asks nothing at all. New options: `--phone-country-code`, `--tax-id`, `--share-telemetry` and `--comments`. + - `scripts/setup_cli.php` runs the wizard's checks and checks what it is given the same way, asks again for an answer that is wrong (the password twice, not shown), suggests the language, currency and timezone from the country, and ends with the backup key and the crontab line. `--non-interactive` asks nothing at all. New options: `--phone-country-code`, `--tax-id`, `--share-telemetry`, `--comments` and `--skip-get-started`, which leaves Get started in Admin instead of opening it on first signing in (see Get started). - **Settings** (rebuilt) - Admin > Settings > Overview: every settings page as a tile, grouped - your company, work, email and notifications, sign-in and access, and system - each saying how it is set now ("Next INV-946 · reminders on · late fees off"). What needs looking at is at the top: mail sending off, email to ticket with no mailbox, a login key required but never set, no timezone. A page for a module that is off is shown faded, and left out of the menu. - Find a setting: type what you are after - late fee, logo, two-step, timezone - and it lists every setting that matches, with the page and section it is on. Picking one opens the page with that setting lit up and ready to change, on the right tab for Mail. @@ -1201,7 +1201,8 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Your team: everyone who signs in, and Add someone - a name, an email and a role. ITFlow makes their password: they are emailed their sign-in details, as Admin > Users does, or, with no email set up yet, it is shown to you once to give them. They change it once signed in. - Starter content: the packs from Maintenance > Starter Content, each with how much it adds, ticked to add - and Pick which under each, as on that page (see Starter Content, Pick which). Your first client: a name, website and contact, made as the Clients page makes one - or the demo data, to look around first. - The dashboard shows Finish setting up, with how far it has got, a chip for each step and Carry on, to admins until every step is done or skipped. Its × stops it showing there. Admin > Get started opens it any time, with how many steps are done or skipped beside it, and a summary at the end lists every step with where it stands. - - The setup wizard's last step says Get started comes next. + - Until every step is done or skipped, the top of Get started has Do it later and Skip all. Do it later goes to the dashboard and leaves each step as it is, for Finish setting up to carry on from. Skip all, once you say yes, skips every step still to do, and the dashboard stops showing Finish setting up - each step is still in Admin > Get started, and doing one ticks it off as ever. + - The setup wizard's last step says Get started comes next. An install set up with `setup_cli.php --skip-get-started` doesn't open it on signing in or show it on the dashboard - it is in Admin > Get started. - **Starter Content, Pick which** (new) - Under each pack on Maintenance > Starter Content and Get started's Starter content step, Pick which lists what the pack would add, to choose one by one. Every item starts ticked, so leaving it alone adds the whole pack as before; All and None tick or clear them, and the pack's count says how many are picked. What is here already isn't listed. - Each item shows as it will look: a tag as its pill, a category as its chip in its colour and icon, a product with its price and how it is billed - with what each is for. Categories are grouped by type, tags by what they tag and products by income category. @@ -2021,7 +2022,7 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Contract prepaid hours: database 3.4.4 adds to `contract_templates` and `contracts` `{prefix}_monthly_hours` and `{prefix}_hours_alert` decimal(8,2) (0.00), and `contracts.contract_hours_alerted_at` datetime, plus two tables. `contract_blocks`: `contract_block_contract_id` (ON DELETE CASCADE), `_hours`, `_price` (NULL for none), `_bought`, `_expires` (NULL for never), `_month` (char(7), for a month's included hours - UNIQUE with the contract, so a month is made once), `_invoice_id`, `_note`, `_created_by`, `_created_at`. `contract_block_uses`: `contract_block_use_block_id` (ON DELETE CASCADE), `_ticket_id`, `_hours`, `_full` (on the use that covered the rest of a ticket), `_created_by`, `_created_at`; a use of 0 hours keeps a ticket that was put back out of the draw. New `functions/contract_hours.php`: `contractHoursReady()`, `contractHoursText()`, `contractHoursWords()`, `contractBlocks()`, `contractHoursSummary()`, `contractHoursLeftFor()`, `contractHasHours()`, `contractClientHours()`, `contractHoursEnsureMonthly()`, `contractHoursSync()` (after a save, Make active, Renew or a countersignature that makes a draft active), `contractHoursTicketUses()`, `contractHoursUses()`, `contractHoursDraw()` (one at a time per client, under `GET_LOCK`), `contractHoursDoneOn()`, `contractHoursRecheck()`, `contractHoursPutBack()`, `contractHoursAlertReset()`, `contractHoursAlertCheck()`, `contractHoursAlertSend()`, `contractHoursFromInput()`, `contractHoursAdd()` (the invoice through `billingInvoiceCreate()` and `billingItemSave()`), `contractHoursBlockDelete()`, `contractBlockRow()` and `contractHoursNightly()`. `billingUnbilledSql()` leaves out a ticket with a full use; `billingUnbilledTickets()` adds `worked` (the hours the time logged makes) and `prepaid`, and `hours` is now what is left after them; `billingUnbilledByClient()` adds `prepaid`. `contractFromInput()` takes `monthly_hours` and `hours_alert`, `contractTermsSetSql()` writes them once 3.4.4 has run, and `contractUpdate()` logs them and clears `contract_hours_alerted_at` when the level changes. `contractsNightly()` returns `drawn` too, logged by `cron/nightly_tasks.php`. `ticketDeleteForGood()` deletes the ticket's uses. `CONTRACT_PLACEHOLDERS` adds Monthly Hours, and `contractDocumentHtml()` adds an Included hours row only when there are some, so documents without them hash as before. `agent/post/contract.php` adds `add_contract_hours` and `draw_contract_hours` (POST, with a `contract_id`, a `client_id` or a `ticket_id`) and `delete_contract_block` and `put_back_contract_hours` (GET with the token), all with Sales Modify. New `agent/modals/contract/contract_hours_add.php`. `emailPreviewContractCatalog()` adds `contract_hours_low`. New CSS: `.itflow-contract-hours-left` and `.itflow-hours-bar` (a ``, as the portal's CSP allows no inline styles). - Contract billing and SLAs: database 3.4.5 adds `contracts.contract_recurring_invoice_id` int (0, keyed) and `contract_recurring_stopped_at` datetime, set when the contract pauses its recurring invoice so it does so once, and sets a rate of 0.00 on contracts and templates to NULL (`contractFromInput()` saves 0 as NULL too). New `functions/contract_billing.php`: `contractBillingReady()`, `contractTermsContracts()` (a client's contracts that can be in force, kept for the request), `contractInForceOn()`, `contractTermsOn()` (the one started last that sets a rate, net terms or a priority's SLA), `contractsInForce()`, `contractSlaFor()`, `contractAfterHours()` (from `getSlaSettings()` and `getBusinessHolidays()`), `contractTicketRate()` (source after_hours, contract, client or default, and a note), `contractClientRate()`, `contractNetTerms()`, `contractNetTermsToday()`, `contractRecurringRow()`, `contractForRecurring()`, `contractRecurringChoices()`, `contractRecurringShouldRun()`, `contractRecurringAmountText()`, `contractRecurringStart()` (skips missed runs with `recurringInvoiceCatchUp()`), `contractRecurringStop()`, `contractRecurringFollow()`, `contractRecurringLink()`, `contractRecurringUnlink()`, `contractRecurringFromInput()`, `contractRecurringFirstDate()`, `contractRecurringCreate()` and `contractRecurringNightly()`. `getTicketSlaId()` asks `contractSlaFor()` first, and takes `$with_contract` (false for the client's own - `contractSlaRows()` uses it). `billingUnbilledTickets()` prices each ticket with `contractTicketRate()` and adds `rate_source` and `rate_note`; `billingUnbilledByClient()` adds `rates`, `rate_note` and `rate_source`; `billingTimeInvoice()` with no price bills each ticket at its own rate, and `combine` makes one line per rate. `billingInvoiceCreate()`, `quoteConvert()`, `recurringInvoiceGenerate()`, New Invoice and Copy in `agent/post/invoice.php` take `contractNetTerms()`. `contractsNightly()` returns `paused` too, logged by `cron/nightly_tasks.php`. `agent/post/contract.php` adds `make_contract_recurring` and `link_contract_recurring` (POST) and `unlink_contract_recurring` (GET with the token), all Sales Modify, and Make active, Renew, Cancel and a countersignature that makes a draft active call `contractRecurringFollow()`; deleting a recurring invoice unlinks its contract. New `agent/modals/contract/contract_recurring.php`. The prepaid hours handlers pass the agent's name unescaped, as the history and logs escape it. - Client portal invites and sign-ups: database 3.4.6 adds `portal_invites` - `invite_kind` (invite or signup), `invite_email`, `invite_name`, `invite_billing`, `invite_technical`, `invite_token_hash` (the SHA-256 of the link's token), `invite_expires_at`, `invite_sent_at`, `invite_sent_count`, `invite_created_by` (an agent), `invite_created_by_contact_id` (a primary contact), `invite_created_at`, `invite_accepted_at`, `invite_contact_id`, `invite_cancelled_at` and `invite_client_id` (ON DELETE CASCADE), keyed on the client and address and on the contact - plus `clients.client_portal_signup` and `settings.config_client_portal_signup` tinyint(1) (0). New `functions/portal_invite.php`: `PORTAL_SIGNUP_LINK_HOURS` (24), `PORTAL_INVITE_MAX` (50), `PORTAL_SIGNUP_IP_LIMIT` (20), `portalInvitesReady()` (keeps every page and the nightly job as they were before 3.4.6 has run), `portalInviteParse()`, `portalInviteWho()` (new, contact, member or elsewhere), `portalInviteRow()`, `portalInviteBy()`, `portalInviteState()`, `portalInviteUrl()`, `portalInviteNewToken()`, `portalInviteByLink()` (hash_equals), `portalInviteMail()`, `portalInviteSend()`, `portalInviteResend()`, `portalInviteCancel()`, `portalInvitesWaiting()`, `portalInviteStateLabel()`, `portalContactJoined()`, `portalInviteAccept()` (spends the link with one guarded UPDATE first, so a second press or a race does nothing), `portalMailSignupJoined()`, `portalMailInviteAccepted()`, `portalSignupOn()`, `portalSignupDomains()`, `portalSignupClients()`, `portalSignupOpen()`, `portalSignupClientFor()`, `portalSignupRequest()` (logged as `Client Login` Sign Up Requested, Sign Up Sent and Sign Up Refused), `portalMailSignupExists()`, `portalSignupSet()` and `portalInvitesNightly()`, which `cron/nightly_tasks.php` runs and logs. `portalContactAccess()` and `portalSignInMethods()` move from `client/functions.php` to `functions/portal.php`, as cron and the agent side use them now. `agent/post/contact.php` adds `send_portal_invites` (POST) and `resend_portal_invite` and `cancel_portal_invite` (GET with the token), and `agent/post/client.php` `set_client_portal_signup` (POST), all Client Modify. `agent/modals/contact/contact_invite.php`, an unused stub (Welcome Letter, Big Wig) with no handler, is replaced by the Invite people window. `agent/contacts.php` adds `role=invites`. `client/post.php` adds `portal_invites_send`, `portal_invite_resend` and `portal_invite_cancel` (the primary contact's). New `client/invite.php`, `client/join.php` (`?invite=&token=`, Referrer-Policy no-referrer) and `client/signup.php` (a form token in the session and a hidden field robots fill in). `edit_client_portal_settings` saves `config_client_portal_signup` once 3.4.6 has run. `emailPreviewInviteCatalog()` adds `portal_join_invite`, `portal_join_invite_colleague`, `portal_join_accepted`, `portal_signup`, `portal_signup_exists` and `portal_signup_joined`. `auditLogActionStyle()` adds Invite and the Sign Up actions. New CSS: `.itflow-join-box`. -- Get started: database 3.4.7 adds `settings.config_getting_started` tinyint(1) (1 in db.sql, so a new install has it; the update sets it to 0 for one already running), `config_getting_started_skipped` varchar(255) (the steps skipped, comma separated) and `config_getting_started_opened_at` datetime (when it was first opened - it opens by itself on signing in only until then). New `functions/getting_started.php`: `GETTING_STARTED_STEPS`, `GETTING_STARTED_SLAS` (priority => business minutes to respond; the SLAs `gettingStartedApplySlas()` makes have `sla_resolution_minutes` NULL, and it reuses one only with the same response time and none), `gettingStartedReady()` (nothing shows before 3.4.7 has run), `gettingStartedState()`, `gettingStartedUrl()`, `gettingStartedCronCommand()`, `gettingStartedBackupJob()`, `gettingStartedSteps()` (each step's done, skipped and status, from the settings), `gettingStartedProgress()`, `gettingStartedAfter()`, `gettingStartedSkip()`, `gettingStartedOpened()`, `gettingStartedDismiss()`, `gettingStartedFirstVisit()` (login.php takes the first admin there, after MFA enforcement and not instead of a `last_visited` page), `gettingStartedCard()` (the dashboard's card), `gettingStartedBackupSave()` (the backup job's row in `cron_jobs`, seeded from the registry when the dispatcher hasn't yet), `gettingStartedApplySlas()` and `gettingStartedDaysText()`. New `admin/getting_started.php` (a page of its own, in the setup wizard's frame; `?step=`, and `?cron_status=1` answers in JSON), its steps in `admin/getting_started/` and `admin/post/getting_started.php`: `getting_started_cron_on`, `getting_started_mail` (continue or test), `getting_started_backups`, `getting_started_billing`, `getting_started_support`, `getting_started_team`, `getting_started_content`, `getting_started_skip` and `getting_started_dismiss`. The first client posts `add_client` to `agent/post.php` and the demo data `load_demo_data`, both of which return to the step. Admin > Mail's check of a sending or receiving server moves from a closure in `admin/post/settings_mail.php` to `settingsMailServerCheck($server, $before, $post)` in `functions/settings.php`, which both use. In `admin/post/starter_content_model.php`, `starterContentLoad()` and each `starterLoad*()` take `$only`, the items to load by `starterItemKey()` (null for the whole pack, as Maintenance > Starter Content loads it), and new `starterContentRows()`, `starterItemKey()` (a category's or tag's type with its name), `starterItemName()`, `starterItemGroup()`, `starterOnly()` and `starterContentItems()` (what a pack would add, judged here as each loader judges it); `getting_started_content` and `load_starter_content` (for one pack) take `picked[pack]` and `items[pack][]`; `starterContentItems()` gives each item's library row. New `admin/includes/starter_content_pick.php` (both pages): `starterItemAbout()`, `starterItemLook()` (through `tagPill()`, `categoryChip()` and `productBilledText()`), `starterPreviewPacks()`, `starterItemPreview()`, `starterPickHtml($pack, $items, $form)` (the list, tied by `form=` to a table row's Add) and `starterPreviewModalHtml()`; new `admin/starter_content_preview.php` answers Preview in JSON (`?pack=&item=`, admins only, 404 for anything else); new `js/starter_content.js` (`data-starter-pick`, `data-starter-item`, `data-starter-pack-check`, `data-starter-count`, `data-starter-add`, `data-starter-preview`) takes over from the picking in `js/getting_started.js`; new CSS `.itflow-starter-*`. New `js/getting_started.js` (`data-gs-reveal`, `data-gs-show`, `data-gs-cron-poll`), and `js/app.js` only starts DataTables where it is loaded, so setup and Get started, which load app.js on its own, no longer log an error to the console. New CSS: `.itflow-gs-*`. +- Get started: database 3.4.7 adds `settings.config_getting_started` tinyint(1) (1 in db.sql, so a new install has it; the update sets it to 0 for one already running), `config_getting_started_skipped` varchar(255) (the steps skipped, comma separated) and `config_getting_started_opened_at` datetime (when it was first opened - it opens by itself on signing in only until then). New `functions/getting_started.php`: `GETTING_STARTED_STEPS`, `GETTING_STARTED_SLAS` (priority => business minutes to respond; the SLAs `gettingStartedApplySlas()` makes have `sla_resolution_minutes` NULL, and it reuses one only with the same response time and none), `gettingStartedReady()` (nothing shows before 3.4.7 has run), `gettingStartedState()`, `gettingStartedUrl()`, `gettingStartedCronCommand()`, `gettingStartedBackupJob()`, `gettingStartedSteps()` (each step's done, skipped and status, from the settings), `gettingStartedProgress()`, `gettingStartedAfter()`, `gettingStartedSkip()`, `gettingStartedSkipAll()` (every step's key into `config_getting_started_skipped`, done ones too, so one that comes undone later stays skipped and off the dashboard; returns the titles that were still to do, for the audit log), `gettingStartedOpened()`, `gettingStartedDismiss()` (also what `scripts/setup_cli.php --skip-get-started` calls), `gettingStartedFirstVisit()` (login.php takes the first admin there, after MFA enforcement and not instead of a `last_visited` page), `gettingStartedCard()` (the dashboard's card), `gettingStartedBackupSave()` (the backup job's row in `cron_jobs`, seeded from the registry when the dispatcher hasn't yet), `gettingStartedApplySlas()` and `gettingStartedDaysText()`. New `admin/getting_started.php` (a page of its own, in the setup wizard's frame; `?step=`, and `?cron_status=1` answers in JSON), its steps in `admin/getting_started/` and `admin/post/getting_started.php`: `getting_started_cron_on`, `getting_started_mail` (continue or test), `getting_started_backups`, `getting_started_billing`, `getting_started_support`, `getting_started_team`, `getting_started_content`, `getting_started_skip`, `getting_started_skip_all` (to the dashboard) and `getting_started_dismiss` (the dashboard card's ×; the page's footer no longer offers it - Do it later and Skip all, `data-gs-later` and `data-gs-skip-all`, are at the top, with Skip all asking through `js/confirm_modal.js`, which the page now loads with SweetAlert2). The first client posts `add_client` to `agent/post.php` and the demo data `load_demo_data`, both of which return to the step. Admin > Mail's check of a sending or receiving server moves from a closure in `admin/post/settings_mail.php` to `settingsMailServerCheck($server, $before, $post)` in `functions/settings.php`, which both use. In `admin/post/starter_content_model.php`, `starterContentLoad()` and each `starterLoad*()` take `$only`, the items to load by `starterItemKey()` (null for the whole pack, as Maintenance > Starter Content loads it), and new `starterContentRows()`, `starterItemKey()` (a category's or tag's type with its name), `starterItemName()`, `starterItemGroup()`, `starterOnly()` and `starterContentItems()` (what a pack would add, judged here as each loader judges it); `getting_started_content` and `load_starter_content` (for one pack) take `picked[pack]` and `items[pack][]`; `starterContentItems()` gives each item's library row. New `admin/includes/starter_content_pick.php` (both pages): `starterItemAbout()`, `starterItemLook()` (through `tagPill()`, `categoryChip()` and `productBilledText()`), `starterPreviewPacks()`, `starterItemPreview()`, `starterPickHtml($pack, $items, $form)` (the list, tied by `form=` to a table row's Add) and `starterPreviewModalHtml()`; new `admin/starter_content_preview.php` answers Preview in JSON (`?pack=&item=`, admins only, 404 for anything else); new `js/starter_content.js` (`data-starter-pick`, `data-starter-item`, `data-starter-pack-check`, `data-starter-count`, `data-starter-add`, `data-starter-preview`) takes over from the picking in `js/getting_started.js`; new CSS `.itflow-starter-*`. New `js/getting_started.js` (`data-gs-reveal`, `data-gs-show`, `data-gs-cron-poll`), and `js/app.js` only starts DataTables where it is loaded, so setup and Get started, which load app.js on its own, no longer log an error to the console. New CSS: `.itflow-gs-*`. - Tags' look: `.itflow-tag` draws `--itflow-tag-color` as a `color-mix()` wash and edge with the icon in it and the name in `--bs-body-color` (`--itflow-tag-ink`, 75% or 55% under `[data-bs-theme="dark"]`, is how much of the color the icon keeps), with neutral fallbacks before each `color-mix()`; `--itflow-tag-text` is no longer used by pills. `.itflow-search-tag-icon` and the new `.itflow-select-badge.is-soft` follow it. New `tagOptionLook($color, $icon)` in `functions/tags.php` (`selectOptionColor()` plus `data-soft="1"`) for a tag's `