<{* Kundeinput (produkt-personalisering): felter kunden selv udfylder — fx et navn til gravering, et ja/nej-valg, en liste eller en fil-upload. Scalar fields carry data-bundle-variant so app.js submit() collects and required-validates them automatically; file inputs carry data-customer-file and are sent as multipart FormData. Values follow the order line. *}> <{$ca_profile = $product->getProfile()}> <{if $ca_profile}> <{$customer_attrs = []}> <{foreach $ca_profile->getAttributeList() as $ca}> <{if $ca->isCustomerInput()}> <{$customer_attrs[] = $ca}> <{/if}> <{/foreach}> <{if $customer_attrs}> <{* x-fragment lader app.js' replaceFragments() bytte de server-renderede valideringsfejl ind i siden efter en afvist add-to-cart. Uden den følger fetch'en redirecten, flashen bliver læst af et svar ingen kigger på, og en fejlet tilføjelse ser ud som en succes. Navnet er per produkt, så to blokke for forskellige produkter på samme side aldrig overskriver hinanden. *}> <{* Kun ét produkt ad gangen har en flash, og redirecten peger på #attribute_error. Ankeret sættes på det FØRSTE felt med fejl, så browseren lander ved fejlen i stedet for i toppen af siden. *}> <{$ca_anchor_used = false}>
<{foreach $customer_attrs as $ca}> <{$ca_id = $ca->getAttributeId()}> <{$ca_error = ''}> <{if $attribute_errors && $attribute_errors.product_id == $product->getProductId() && isset($attribute_errors.errors[$ca_id])}> <{$ca_error = $attribute_errors.errors[$ca_id]}> <{/if}> <{* Genudfyldning: flashen hører til ÉT produkt, så samme product_id-tjek som ovenfor — ellers ville en værdi lække over på et produktkort på samme side. values indeholder kun skalarer (en fil kan ikke genudfyldes) og mangler helt i en gammel flash fra før deploy, derfor isset(). *}> <{$ca_value = ''}> <{if $attribute_errors && $attribute_errors.product_id == $product->getProductId() && isset($attribute_errors.values[$ca_id])}> <{$ca_value = $attribute_errors.values[$ca_id]}> <{/if}>
id="attribute_error"<{$ca_anchor_used = true}><{/if}> style="margin-bottom:0.75rem;"> <{if $ca->getDataType() == 'boolean'}> <{elseif $ca->getDataType() == 'valuelist'}> <{elseif $ca->getDataType() == 'file'}> getIsRequired()}> required<{/if}> style="width:100%;font-size:0.875rem;">

<{t}>Maks. 10 MB — JPG, PNG, GIF, WEBP eller PDF<{/t}>

<{* En fil kan ikke genudfyldes af browseren, så kunden skal vælge den igen *}> <{if $ca_error}>

<{t}>Vælg filen igen<{/t}>

<{/if}> <{elseif $ca->getDataType() == 'integer'}> value="<{$ca_value|escape}>"<{/if}><{if $ca->getIsRequired()}> required<{/if}><{if $ca->getPlaceholder()}> placeholder="<{$ca->getPlaceholder()|escape}>"<{/if}> class="form-input" style="width:100%;padding:0.5rem;border:1px solid #d1d5db;border-radius:0.375rem;"> <{else}> value="<{$ca_value|escape}>"<{/if}><{if $ca->getIsRequired()}> required<{/if}><{if $ca->getMaxLength()}> maxlength="<{$ca->getMaxLength()}>"<{/if}><{if $ca->getPlaceholder()}> placeholder="<{$ca->getPlaceholder()|escape}>"<{/if}> class="form-input" style="width:100%;padding:0.5rem;border:1px solid #d1d5db;border-radius:0.375rem;"> <{/if}> <{if $ca_error}>

<{if $ca_error == 'required' || $ca_error == 'no_file'}><{t}>Feltet skal udfyldes<{/t}> <{elseif $ca_error == 'too_long'}><{t}>Teksten er for lang<{/t}> <{elseif $ca_error == 'too_large'}><{t}>Filen er for stor (maks. 10 MB)<{/t}> <{elseif $ca_error == 'invalid_type'}><{t}>Filtypen understøttes ikke — brug JPG, PNG, GIF, WEBP eller PDF<{/t}> <{else}><{t}>Ugyldig værdi<{/t}><{/if}>

<{/if}>
<{/foreach}>
<{/if}> <{/if}>