CluPilotCloud/app/Services
nexxo c354ad1463 Make mails readable on a phone, and let an operator look at one
Three things the owner reported, and one sentence that promised something
this product does not do.

**The mails had a fixed width.** components/mail/layout.blade.php set
width="600" and width:600px, so every phone showed a 600px canvas in a
390px window and one had to scroll sideways to read a sentence. Now the
table is width="100%" with max-width:600px — fluid, with 600 as a ceiling
rather than a floor. A mail cannot fix this with a media query (Outlook
renders with Word and drops <style> blocks), which is why the whole file
is inline attributes and why the shrinking has to be built in. Side
padding went 40px -> 24px in the layout and in all nine mail views:
40 each side leaves 278px of a 390px screen to read in.

**There was no way to look at a mail.** An invoice mail needs an invoice,
a maintenance announcement needs a window, and "register an account to
see whether the confirmation reads well" is not a workflow — which is how
the fixed width survived for months. App\Services\Mail\MailPreviews
builds all nine from sample data, every record with make() and never
create(), so a preview leaves no invoice, order or window behind and
draws no number from a series. Two ways to see one: in a tab as the
document itself, or sent to the signed-in operator's own address. There
is no recipient field — that would be a form for mailing strangers.

sendNow(), not send(): every mailable here implements ShouldQueue, and
send() defers to the queue for those, which would have put a failure in a
worker's log while the button reported success to somebody waiting for
the mail. The test caught that.

**The booking page had no shell.** Order declared layouts.portal — the
bare shell the sign-in pages use, no navigation and no padding — so the
page sat flush against the top and bottom of the window with no way back
to anything. layouts.portal-app, like every other portal page.

**The consent box said something untrue.** Its refusal read "otherwise we
can only build your cloud once the withdrawal period has ended", which
describes a path that does not exist: nothing here delivers fourteen days
later, and an unticked box simply means no order. Said plainly now, and
said on the page before somebody runs into the refusal.

Also: GrantedPlanPortalTest asserted assertDontSee('179'), which matches
any id containing those digits — and ids climb through a suite run
because a rolled-back transaction does not reset an auto-increment
counter. It passed alone and failed in company. Asserts on '179,00' now,
which is what a leaked price would actually look like.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 14:50:37 +02:00
..
Billing Discard an unconfirmed registration, and sweep the abandoned ones 2026-07-30 14:08:02 +02:00
Deployment Update on a schedule if the owner wants one, and say where incidents go 2026-07-29 15:01:54 +02:00
Devices Show where an account is signed in, and let it sign the other places out 2026-07-28 23:38:17 +02:00
Dns Fix nine defects in the provisioning pipelines 2026-07-30 01:34:55 +02:00
Domains Prove a custom domain before serving it, and keep proving it 2026-07-29 14:47:51 +02:00
Env Add a raw .env editor to the Integrations page, with a net under it 2026-07-29 01:51:27 +02:00
Mail Make mails readable on a phone, and let an operator look at one 2026-07-30 14:50:37 +02:00
Maintenance Keep the purpose mailer through the queue instead of losing it to the default 2026-07-28 03:50:02 +02:00
Monitoring Add the SSH identity to the vault, and give deployment config a console page 2026-07-29 00:52:44 +02:00
Provisioning Let the customer buy it themselves, and tab the integrations page 2026-07-29 19:42:28 +02:00
Proxmox Restart a machine, enforce the quota that was sold, end a route that ended 2026-07-29 18:28:28 +02:00
Secrets Read the support mailbox into the console 2026-07-29 21:41:29 +02:00
Ssh Make host onboarding survive the machine it runs on 2026-07-30 00:54:16 +02:00
Status Let an incident be deleted, and start measuring whether the hosts answer 2026-07-29 15:16:48 +02:00
Stripe Discard an unconfirmed registration, and sweep the abandoned ones 2026-07-30 14:08:02 +02:00
Tax Ask the EU register whether a VAT number is real 2026-07-29 22:42:29 +02:00
Traefik Serve the custom domain, not just announce it 2026-07-29 16:44:52 +02:00
Traffic feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
Wireguard Add the SSH identity to the vault, and give deployment config a console page 2026-07-29 00:52:44 +02:00