Skip to content

Image-columns config in template editor + Categories tab last in sidebar#21

Merged
ddon merged 13 commits into
BeamLabEU:mainfrom
timujinne:feature/image-columns-config
May 21, 2026
Merged

Image-columns config in template editor + Categories tab last in sidebar#21
ddon merged 13 commits into
BeamLabEU:mainfrom
timujinne:feature/image-columns-config

Conversation

@timujinne
Copy link
Copy Markdown
Contributor

Two changes on top of PR #20 (feature/image-columns).

1. columns field in the image_list variable config (template editor)

Lets template authors set a default number of columns (1..4) per image_list variable, matching the existing max_count and separator fields. The selected value is read by the consumer (Andi's order document picker) and ends up in image_params[slot]["columns"], which the existing google_docs_client.ex already dispatches on (cols >= 2 -> two-phase table insertion, cols == 1 -> inline).

  • variable.ex: default_image_config(:image_list) now includes columns: 1.
  • documents.ex: coerce_config/1 parses and clamps columns to 1..4 (mirrors max_count). image_slots_for_template/1 now returns %{name, kind, config} so consumers can read columns/max_count/etc. without re-querying the template. The merged config is normalised to string keys to avoid mixed atom/string-key maps leaking to callers.
  • web/components/variable_config_form.ex: a Columns <select> (1..4) is rendered for :image_list variables, sharing the visual style of the existing fields.

2. Sidebar: Categories tab moved last

Child tabs are sorted by ascending priority. The Categories tab had priority: 647, which placed it before Documents (648) and Templates (649). Bumped to 651 so it now sorts after both.

Notes

timujinne added 13 commits May 19, 2026 00:51
Composer now writes a caller-supplied :data opt into the Document
changeset, and schema_to_file_map/1 exposes the data field. This lets
the host app store the template/image selection ("recipe") that
produced a document so it can be re-created later.
Replace boolean categories_trash/types_trash with string status_mode
assigns (active/trashed); replace toggle_categories_trash and
toggle_types_trash with a single switch_status event using
phx-value-target and phx-value-mode, matching DocumentsLive.

Extract status_subtabs/1 component shared by Categories and Types
columns; render in the DocumentsLive visual style (border-b-2 with
border-primary for Active and border-error for Trash).

Auto-hide the entire sub-tab row when the Trash list is empty and
the current mode is active. Track trashed_categories_count and
trashed_types_count, reusing the loaded list as count when viewing
trash to avoid a second query.
Two related soft-delete UX improvements for Documents/Templates:

1. Deletion metadata is now stamped into the existing JSONB data field
   on Document and Template (no migration). On soft-delete,
   data["deleted"] = %{"at" => ISO-8601, "by_uuid" => actor_uuid} is
   merged via PostgreSQL || operator so existing keys (composer
   recipe etc.) are preserved. On restore, data - 'deleted' removes
   only the deletion key. A new "Deleted" column appears only on
   the trash tab (both list and cards views, Documents and Templates),
   showing formatted timestamp + display name resolved via a single
   PhoenixKit.Users.Auth.get_users_by_uuids/1 batch lookup.

2. When the underlying Google Drive file is missing (HTTP 404 from
   the Drive parents/move endpoint), restore no longer fails with
   the generic 'try again' message. do_move_file/2 distinguishes
   404 and returns {:error, :drive_file_not_found}, propagated
   through restore_document/2 and restore_template/2 to the
   LiveView, which renders a yellow dismissible warning:
   'File is missing in Google Drive — it cannot be restored. You
   can permanently delete this record.'

Coverage:
- 80 existing unit tests still pass.
- New tests in test/errors_test.exs (error message) and
  test/integration/drive_bound_actions_test.exs (stamp on delete
  with and without actor, preserve other data keys, clear on
  restore, Drive 404 on GET-parents and PATCH-move, both Document
  and Template paths).
…, empty-uuid short-circuit

- Wrap data column with COALESCE(data, '{}'::jsonb) in both fragments
  of stamp_deleted_data/2 and clear_deleted_data/1, so a row with a
  NULL data column still gets the deleted metadata written / cleared
  correctly. The data column is nullable in the migration.
- Extend restore_document/2 and restore_template/2 tests to include a
  sibling 'recipe' key in data alongside 'deleted', and assert it
  survives restore — previously only the delete path verified
  preservation.
- Skip Auth.get_users_by_uuids/1 when the trashed UUID list is empty
  (active tab, or trash tab with no by_uuid stamped rows).
Brings in:
- composed-document data persistence (1643a97)
- Google Docs page/column width helpers + table-based image insertion (ee0b312, 5d13816)
- Unified Active/Trash sub-tabs on Categories page (064c0d2)
- _phoenix_kit_sources.css @source directive regen (91605f2)
- mix format + scale_height unit comment (8f0c3a7)
- columns-aware orchestrator with two-phase table insertion (182de63)
- Deletion metadata in data['deleted'] JSONB + Drive 404 restore warning (fc69fa1)
- COALESCE-safe JSONB ops + restore preservation test + empty-uuid short-circuit (6944db2)
Bump the :admin_document_creator_categories Tab priority from 647 to
651 so it sorts after Documents (648) and Templates (649). Children
of an admin module tab are ordered by ascending priority, so Categories
previously appeared first; now it appears last as intended.
Adds a 'columns' field (1..4) to image_list variable configs in the
Document Creator template editor and propagates the saved config to
consumers via image_slots_for_template/1.

- variable.ex: default_image_config(:image_list) now includes
  columns: 1 so new variables get a sensible default.
- documents.ex: coerce_config/1 parses and clamps 'columns' to 1..4
  (mirrors the existing max_count handling). image_slots_for_template/1
  now returns %{name, kind, config} — config is merged from the saved
  variable.config with defaults, normalised to string keys so
  consumers do not need to handle both atom and string forms.
- variable_config_form.ex: adds a Columns select (1..4) to the
  :image_list variant of the form, matching the existing separator/
  max_count fields visually.
- _phoenix_kit_sources.css: regenerated by the compile step.
@ddon ddon merged commit d2e0798 into BeamLabEU:main May 21, 2026
ddon added a commit that referenced this pull request May 21, 2026
- google_docs_client: make Phase-2 table identification drift-proof.
  `match_new_tables/3` matches new tables by document order instead of a
  startIndex set-difference, so pre-existing tables located after a
  multi-column placeholder no longer trip the count guard and leave grids
  empty. Extract `fill_matched_tables/5`. Adds 5 unit tests.
- documents_live: resolve trashed-by display names when the trashed lists
  change (load/sync/patch) instead of on every render via assign_files.
- taxonomy: add count_categories/1 and count_types_for_category/2; use them
  for the trash badges instead of length(list_*(status: "deleted")).
- documents: stamp/clear data["deleted"] only on the schema that owns the
  google_doc_id (derived from folder_key/type), not both tables.
- tests: fix stale assertions that never run in the sandbox (no local DB) —
  image_slots_for_template now returns %{name, kind, config}; image
  object sizes are emitted in PT, not EMU.
- docs: add dev_docs/pull_requests entries for #20 and #21 with review and
  follow-up notes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants