Repository navigation
feat: report style presets, inspector credentials, email attachment fix - #260
Merged
important-new merged 16 commits intoJul 21, 2026
Merged
important-new merged 16 commits into
important-new merged 16 commits into
Conversation
Only Resend read args.attachments; the other three silently dropped report-PDF / evidence-pack / .ics attachments. SendGrid + Postmark add the attachment array to their JSON payload; Mailgun switches to multipart FormData when attachments are present (binary cannot ride urlencoded) and keeps the urlencoded path otherwise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…njector Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…gine Rename tenant_configs.report_theme -> default_profile_id and inspections.report_theme_override -> profile_override; add per-inspection badge_layout_override/report_photo_columns tweaks and templates.default_profile_id. Wire resolveProfile into the report service (three-tier: inspection override -> template default -> tenant default -> signature) and emit styleProfile in the report payload (replaces the modern|classic|minimal theme string). Remove the stale workspace theme radio (rebuilt as a profile picker in Plan 1b). Bump RENDER_VERSION r7->r8. Migration 0018 uses ADD+DROP for the tenant_configs default change (not FK-referenced -> D1-safe) and RENAME/ADD for inspections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
… dead-export) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…cker & live preview Inject presetTokens at the ReportView root and repoint the report title, section headings, section band, defect-card radius, and typed signature to var(--report-*) so the three built-ins render distinctly (Meridian uppercase+navy, Terra serif, Signature sans). Shared REPORT_HEADING_STYLE const. Activate the existing per-section alwaysPageBreak flag. Add the Workspace 'Report style' section: a gallery picker (each card previews its own typography) beside a live mini-report preview reacting to the draft profile + brand colour. App-side profile mirror in profiles-client.ts. Bump RENDER_VERSION r8->r9; ratchet file-size baseline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…on override Completes the three-tier appearance resolution. Template editor gains a 'Default report appearance' select in the metadata bar (threaded through the template PUT validation + updateTemplate service to templates.default_profile_id, exposed by the loader). InspectionSettingsSheet gains a per-inspection profileOverride select behind a default-collapsed <details> (progressive disclosure — the default path never touches it); '' maps to null via the existing settings-patch/crud path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
… for template defaultProfileId Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…d-export) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
Per-inspector self-asserted credentials (label, member number, uploaded image). No expiry field by design (Spec B §5). No .references() (app-layer tenant+user scoping). Migration 0019 is a pure CREATE TABLE + two indexes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…y/serving Extract the duplicated brand-asset regex into isServableBrandAsset (single source for the route + its guard test), widened to serve tenant-rooted credential images under the credentials/ prefix. Add r2Keys.credentialImage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
Tenant+user scoped credential CRUD (fail-closed queries), a multipart image upload route (replace purges the old R2 object), wired into DI + mounted at /api/credentials. Owner scope = the signed-in inspector's own credentials. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
Settings -> Profile credentials editor (upload-first; label/member# behind a collapsed details; no expiry) wired through the profile route (loader fetch + 4 action intents, per-module hono client). CredentialBadges renders image + text credentials; the report service snapshots the inspector's active credentials into the payload and ReportView mounts the badge strip on the cover, arranged by the resolved styleProfile.badgeLayout. Bump RENDER_VERSION r9->r10. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…ypes Add the 'credentials' tag to VALID_TAGS, give the update/delete summaries 4-12 words, describe every request field/param (>=50 chars), and drop the unused CreateCredentialInput/UpdateCredentialInput exports (knip). Refresh the snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
The suggestions <ul> was position:absolute inside a modal's overflow-y-auto box, so opening it grew the modal a scrollbar. Render it via createPortal to <body> with position:fixed, positioned from the input's getBoundingClientRect and re-measured on scroll (capture:true, to catch the modal's own scroll) + resize. li onMouseDown+preventDefault still beats the input's 120ms blur-close across the portal. SSR-safe: the portal only renders once pos is measured client-side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
…, booking Follow-up to Spec B: surface each inspector's active credentials on the three remaining render surfaces that were deferred from the core PR. - ReportSignatureBlock: small credential badge image under the license line (ReportView passes the first credential with an image). - inspector-signature email footer: renders credential badge images (host-absolutized) plus every credential as text in BOTH html and plain-text variants, so a blocked image never drops the credential. - booking.service sigInspector: loads the inspector's active credentials (tenant + user scoped, sorted) into the signature payload. - New message key pca_signature_credential_alt; paraglide recompiled. - 2 new unit tests for the email-footer credential rendering (9/9 pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45
important-new
added a commit
to important-new/OpenInspection
that referenced
this pull request
Aug 23, 2026
…ix (InspectorHub#260) * fix(email): SendGrid/Postmark/Mailgun adapters forward attachments Only Resend read args.attachments; the other three silently dropped report-PDF / evidence-pack / .ics attachments. SendGrid + Postmark add the attachment array to their JSON payload; Mailgun switches to multipart FormData when attachments are present (binary cannot ride urlencoded) and keeps the urlencoded path otherwise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(report-style): profile constants, resolveProfile, presetTokens injector Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(report-style): repurpose report_theme into appearance-profile engine Rename tenant_configs.report_theme -> default_profile_id and inspections.report_theme_override -> profile_override; add per-inspection badge_layout_override/report_photo_columns tweaks and templates.default_profile_id. Wire resolveProfile into the report service (three-tier: inspection override -> template default -> tenant default -> signature) and emit styleProfile in the report payload (replaces the modern|classic|minimal theme string). Remove the stale workspace theme radio (rebuilt as a profile picker in Plan 1b). Bump RENDER_VERSION r7->r8. Migration 0018 uses ADD+DROP for the tenant_configs default change (not FK-referenced -> D1-safe) and RENAME/ADD for inspections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * chore(report-style): make PROFILE_SCHEMA_VERSION module-private (knip dead-export) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(report-style): consume styleProfile in ReportView + workspace picker & live preview Inject presetTokens at the ReportView root and repoint the report title, section headings, section band, defect-card radius, and typed signature to var(--report-*) so the three built-ins render distinctly (Meridian uppercase+navy, Terra serif, Signature sans). Shared REPORT_HEADING_STYLE const. Activate the existing per-section alwaysPageBreak flag. Add the Workspace 'Report style' section: a gallery picker (each card previews its own typography) beside a live mini-report preview reacting to the draft profile + brand colour. App-side profile mirror in profiles-client.ts. Bump RENDER_VERSION r8->r9; ratchet file-size baseline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(report-style): template-default binding + collapsed per-inspection override Completes the three-tier appearance resolution. Template editor gains a 'Default report appearance' select in the metadata bar (threaded through the template PUT validation + updateTemplate service to templates.default_profile_id, exposed by the loader). InspectionSettingsSheet gains a per-inspection profileOverride select behind a default-collapsed <details> (progressive disclosure — the default path never touches it); '' maps to null via the existing settings-patch/crud path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * chore(report-style): refresh openapi snapshot + tenant-scope baseline for template defaultProfileId Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * chore(report-style): make StyleProfileClient module-private (knip dead-export) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(credentials): inspector_credentials table + migration Per-inspector self-asserted credentials (label, member number, uploaded image). No expiry field by design (Spec B §5). No .references() (app-layer tenant+user scoping). Migration 0019 is a pure CREATE TABLE + two indexes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(credentials): shared brand-asset predicate + credential image key/serving Extract the duplicated brand-asset regex into isServableBrandAsset (single source for the route + its guard test), widened to serve tenant-rooted credential images under the credentials/ prefix. Add r2Keys.credentialImage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(credentials): CRUD service + API + image upload Tenant+user scoped credential CRUD (fail-closed queries), a multipart image upload route (replace purges the old R2 object), wired into DI + mounted at /api/credentials. Owner scope = the signed-in inspector's own credentials. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(credentials): editor + CredentialBadges + report cover render Settings -> Profile credentials editor (upload-first; label/member# behind a collapsed details; no expiry) wired through the profile route (loader fetch + 4 action intents, per-module hono client). CredentialBadges renders image + text credentials; the report service snapshots the inspector's active credentials into the payload and ReportView mounts the badge strip on the cover, arranged by the resolved styleProfile.badgeLayout. Bump RENDER_VERSION r9->r10. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * chore(credentials): refresh openapi snapshot for /api/credentials routes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * chore(credentials): satisfy route-metadata gate + drop unused input types Add the 'credentials' tag to VALID_TAGS, give the update/delete summaries 4-12 words, describe every request field/param (>=50 chars), and drop the unused CreateCredentialInput/UpdateCredentialInput exports (knip). Refresh the snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * fix(address): portal the autocomplete dropdown so it floats above modals The suggestions <ul> was position:absolute inside a modal's overflow-y-auto box, so opening it grew the modal a scrollbar. Render it via createPortal to <body> with position:fixed, positioned from the input's getBoundingClientRect and re-measured on scroll (capture:true, to catch the modal's own scroll) + resize. li onMouseDown+preventDefault still beats the input's 120ms blur-close across the portal. SSR-safe: the portal only renders once pos is measured client-side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 * feat(report): render inspector credentials on signature, email footer, booking Follow-up to Spec B: surface each inspector's active credentials on the three remaining render surfaces that were deferred from the core PR. - ReportSignatureBlock: small credential badge image under the license line (ReportView passes the first credential with an image). - inspector-signature email footer: renders credential badge images (host-absolutized) plus every credential as text in BOTH html and plain-text variants, so a blocked image never drops the credential. - booking.service sigInspector: loads the inspector's active credentials (tenant + user scoped, sorted) into the signature payload. - New message key pca_signature_credential_alt; paraglide recompiled. - 2 new unit tests for the email-footer credential rendering (9/9 pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DtriEUniSuDJwgKQpmhm45 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds two inspection features, an email defect fix, and a small UI polish.
Report style presets
Repurposes the inert
report_themefield into a data-driven appearance-profile engine. Three built-in profiles (Signature / Meridian / Terra) resolve three-tier (per-inspection override → template default → tenant default → signature) and render viavar(--report-*)CSS variables. The colour axis (tenant brand) stays independent of the typography/layout axis (preset). Workspace gallery picker with a live mini-report preview, a template-default binding, and a collapsed per-inspection override. Migration0018(D1-safe ADD+DROP for the tenant default, RENAME/ADD for inspections).Inspector credentials & association badges
Per-inspector self-asserted credentials (label, member number, uploaded badge image; no expiry field). The engine ships no association trademark assets — inspectors upload their own images. Upload-first profile editor (label/member# behind a collapsed disclosure), tenant+user-scoped CRUD API with image upload, and a
CredentialBadgesrenderer. The report service snapshots the inspector's active credentials into the payload; the cover renders the badge strip arranged by the resolved profile's badge layout. Migration0019(new table).Email attachment fix
The SendGrid, Postmark, and Mailgun adapters now forward
args.attachments(previously only Resend did), so report-PDF / evidence-pack /.icsattachments actually send on every provider. Mailgun switches to multipart form-data when attachments are present.Address autocomplete polish
The suggestions dropdown is portaled to
<body>asposition: fixed, so opening it floats above a modal instead of growing the modal a scrollbar.RENDER_VERSIONr7→r10 (report DOM changed). Full unit + web suite green; migrations are D1-safe (append-at-end / RENAME COLUMN / new table).🤖 Generated with Claude Code