Email workspaceProduct import failed contract
Purpose/audience: $user, the importing user.
Implementation: App\Mail\InfoMail (Product Import Failed outcome). Baseline view: resources/views/emails/info.blade.php. Usage evidence: app/Http/Repository/ItemRepository.php:1085, processItemImport failure callback. These are static references, not proof of current production delivery.
Suggested subject: Product Import Failed. HTML title alone does not set the mail header.
Suggested preheader: Product import failed for {{business_name}}. Review progress before the issue.
Portable placeholder contract
| Token | Existing source / derivation | Requirement |
|---|---|---|
| {{logo_url}} | Existing Built app_name PNG | Optional deployment override; the concrete project HTTPS PNG is the default. Approved HTTPS or matching CID only. |
| {{preheader}} | Suggested text above, assembled only from already available values | Required escaped text; not a new backend record or processing guarantee. |
| {{first_name}} | $user->first_name in ItemRepository::processItemImport callback | Required escaped name of the importing user; not a field currently passed separately to InfoMail. |
| {{business_name}} | $business->name in that callback | Required escaped importing-business identity. |
| {{processed_count}} | $data['processed_items']; failure fallback: $chunkSize | Required supplied count; preserve zero. Current chunkSize is 100. |
| {{created_count}} | $data['created_items']; failure fallback: 0 | Required supplied count of newly created products; not a synonym for processed or submitted. |
| {{submitted_count}} | $data['total_items']; failure fallback: $total | Required supplied submission count; do not infer skipped, duplicate or failed counts. |
Content, states and integration decisions
- Explicitly label the three counts as progress before the issue. Failure does not prove that no products were created or that earlier work was rolled back. Do not state either.
- Preserve the instruction to try again or contact support if the issue persists. The source supplies no retry URL, exception text, detailed cause, retry timing guarantee or support address. No such values or button are added.
- When cache values are unavailable, the current callback uses chunkSize for processed count (100), 0 for newly created and the original $total for submitted count. The fallback state illustrates that exact policy; these numbers are best available caller values, not independently verified exact progress.
- States: partial progress, caller fallback counts and long identity/large counts. Keep success/failure as separate notification types even though they share InfoMail and emails.info. InfoMail implements ShouldQueue and the caller uses send().
Required import integration adapter
The structured fields above exist in ItemRepository’s callbacks, not as separately bound InfoMail view values. InfoMail currently accepts a title and a prebuilt message, then maps message to body; emails.info renders body as raw HTML. The title maps to the subject listed above, and the old body contains the same greeting/business/counts/instructions shown in this design.
Do not drop this HTML into emails.info and assume first_name/business_name/counts will appear. A later authorized integration must explicitly pass structured callback values into a dedicated import view/Mailable or a deliberate typed extension of the existing wrapper, preserving subject, recipient, queue behavior and all existing body content. Do not scrape HTML to recover counters, blindly replace generic InfoMail for unrelated uses or insert the old untrusted body as raw HTML.
No caller change is made in this design folder. This is a documented integration decision, not a claim of drop-in compatibility or new application fields. The markup escapes all dynamic strings; explicit numeric counts remain visible even when zero.
Portable HTML/text and safe handling
ready/product-import-failed.html and ready/product-import-failed.txt provide the portable extracts. Every populated state has matching .html and .txt files under previews/. All sample links are local design explanations; no real invitation, report or account action is executed. Placeholder {{names}} are not automatically Laravel Blade syntax. Convert each mapping explicitly using existing data and server-side URL generation; retain all required values and do not insert untrusted raw HTML.
Escape text and attribute values. Validate any application/CFO AI URL separately against approved HTTPS hosts before escaping it; preserve complete generated links and encrypted/signed parameters. Use the provided concrete public PNG or an approved HTTPS/CID override. There are no hidden optional content sections to populate here; required identities/counts should not be silently omitted. Unsupported data has not been invented.
All drafts use the approved compact transactional shell with 16px body text, a minimal Built footer and native light/dark CSS. Email-client rendering and blocked-image behavior remain separate tests. See integration-notes.html for shared provider/image limitations, and easy-wins-batch.md for the whole batch. No production template, sender, transport or application behavior is changed.