A small planning tool can be useful before it needs a database. For a product photography shot list, the output is a file someone can hand to a photographer or reviewer: one row per required image, tied to an actual SKU and variant.
Disclosure: this is a CartVisual implementation note. The free shot list planner is our tool. Planning and CSV download need no account; image generation is a separate paid workflow.
Start with the record someone will use
Our row has thirteen fields:
Shot ID, SKU, Product, Variant, Required view, Purpose, Background, Aspect ratio, Source / reference, Acceptance check, Planned filename, Decision, Reviewer / date The important relationship is SKU + variant + view → source → decision. A filename alone does not explain which source the reviewer should compare or whether the candidate passed.
For example, MUG-01 / Blue / Front and MUG-01 / White / Front need different rows. A generated back view is not a substitute for a missing back reference. That is a workflow decision, so it belongs in the acceptance check rather than in an optimistic export status.
Keep the initial version bounded
This planner accepts one to five variants and eight optional view types, with at most forty rows. Those are interface limits, not rules about how many photos a shop needs. Validate them in the model as well as in the form.
Store the generated rows in a page-local array. Render user text with textContent or input values. Keep planner fields out of page analytics. In our implementation the planner does not call an upload endpoint or browser storage, so a reload loses the draft. The page says that near the download action.
Treat changed inputs as a stale export
There are two kinds of changes. Editing a row's reference or decision updates that row. Changing product, variant or required views invalidates the generated list.
When an input changes, disable download and print until the user rebuilds. Explain that rebuilding replaces row edits. This prevents a file named for a new SKU from silently containing the previous SKU's rows.
Quote CSV fields and consider spreadsheet formulas separately
Escaping double quotes handles CSV syntax:
function quotedCell(value) { return '"' + String(value ?? '').replaceAll('"', '""') + '"'; } It does not, by itself, make spreadsheet software treat =... as plain text. OWASP's CSV Injection guidance describes the separate formula interpretation problem.
For this bounded worksheet we prefix formula-like text and then quote each field. This is a precaution, not a universal guarantee across spreadsheet applications or later save-and-reopen behavior. Review exports in the destination app, especially if the format changes.
Use a UTF-8 byte-order mark if your chosen import workflow needs it, keep a fixed column order, and verify commas, double quotes, line breaks and non-Latin product names with an actual downloaded file. A unit test of the serializer cannot prove the browser download contains the row edits.
Download the edited rows, then release the temporary URL
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'product-photography-shot-list.csv'; link.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); The filename should describe the artifact. The success message should describe the download, without suggesting the product images have been approved.
Check the handoff, not just the row count
Our acceptance check uses two variants and three required views. Edit a reference, mark a row approved, download through the real browser button, and parse the file. It must contain six data rows, thirteen columns, the edited source and the selected decision. Then change a form input and verify export becomes unavailable until rebuild.
For print, mirror editable values into text elements shown only by the print stylesheet. Input controls can clip longer reference names. On a phone, keep the form usable and let the labeled table scroll horizontally rather than shrinking nine review columns into unreadable text.
You can try the shot list planner. The useful output is a clear handoff file; approval still comes from comparing the actual source and candidate image.
Written with AI and checked against the implementation and an actual browser CSV download.
