Skip to content

feat(labels): add view label action for individual entities - #283

Merged
roncodes merged 2 commits into
fleetbase:dev-v0.6.59from
janni1288:feat/single-entity-label-ui
Aug 3, 2026
Merged

feat(labels): add view label action for individual entities#283
roncodes merged 2 commits into
fleetbase:dev-v0.6.59from
janni1288:feat/single-entity-label-ui

Conversation

@janni1288

Copy link
Copy Markdown

What

Adds a "View Label" action for individual entities, using the existing labels/{id}?type=entity backend endpoint (fixed in #280).

Changes

  • entity-actions.js: new viewLabel(entity) action, mirrors the existing order-actions.js pattern
  • entity/card.hbs: new "View Label" button next to edit/delete
  • New modals/entity-label component (template + app re-export), based on modals/order-label
  • routes.php: registered the existing generic labels/{id} route also within the internal namespace - it was previously only reachable externally, so the console couldn't call it
  • New en-us translations for the new modal and error prompt

Testing

  • Verified locally: clicking "View Label" on an entity opens a modal showing only that entity's label (not other entities at the same destination)
  • Confirmed title/QR/barcode render correctly

Note on UI placement

I asked in the issue thread about preferred placement for the new action but haven't heard back yet - went with adding it next to the existing edit/delete buttons on the entity card for now. Happy to move it if you'd prefer a different location (e.g. a dedicated action menu).

Not covered

The acceptance criteria also mention "works consistently across multiple entity types" - I've only tested with a generic entity so far. Happy to verify with other entity types if there are known edge cases I should check, or if this should stay open until that's confirmed.

Part of #182 (backend fix in #280 already merged)

janni1288 and others added 2 commits August 2, 2026 17:52
…ompany

The entity label action reached for a new internal `labels/{id}` route wired
across into the public `Api\v1\LabelController`. The internal namespace already
exposes `orders/label/{id}` via `Internal\v1\OrderController@label`, which
already resolves `type=entity` through `findEntityLabelSubject()`, so no new
backend route is needed. `$type` also defaults to `strtok($publicId, '_')`, so
an `entity_*` public id resolves on its own and the query param can go.

- drop the added internal `labels` route group
- call `orders/label/{public_id}?format=base64`, matching `viewWaypointLabel`
- reuse `modals/order-label` instead of cloning it, as the waypoint action does,
  with an `@options.subject` fallback so the object alt still resolves
- fix the `Failed to load entity label.s` typo and add the two new keys to the
  six other locales that already carry the waypoint equivalents

Also scopes label subject resolution to the session company. The lookups matched
on identifier alone, so any authenticated user could render a label for any
order, waypoint or entity in another organization by supplying its public id.
The identifier match is grouped in a closure — appending the company constraint
to the existing chain would read as `public_id = ? OR (uuid = ? AND company_uuid
= ?)` and still leak. Resolution fails closed when there is no company session.
Applied to both the internal and public API paths, with regression coverage for
the foreign-company, precedence and no-session cases.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@roncodes

roncodes commented Aug 3, 2026

Copy link
Copy Markdown
Member

Thanks for this @janni1288 — and for finding the template bug in #280 first, that was the right place to start.

The frontend approach is solid and the action/modal shape matches how we do order and waypoint labels. Two things needed changing before this goes in. Since maintainer edits are enabled I've pushed a commit to your branch rather than send you round again — please look it over.

1. The backend already exposes this

The internal namespace already has orders/label/{id} in server/src/routes.php, dispatching to Internal\v1\OrderController@label — and that method already resolves type=entity via findEntityLabelSubject(). So the new labels group wasn't needed.

It also pointed at Api\v1\LabelController, which is our public API controller. The internal group is namespaced Internal\v1, which is exactly why it needed the hardcoded leading-backslash FQCN to reach across. We keep those two surfaces separate, so that reach-across is the part to avoid — the controller itself does exist, it's just on the other side of the fence.

The frontend now calls the same endpoint the waypoint action does:

const { data: pdfStream } = await this.fetch.get(`orders/label/${entity.public_id}?format=base64`);

$type defaults to strtok($publicId, '_'), so an entity_* public id resolves on its own and ?type=entity isn't needed. That's why viewWaypointLabel in addon/components/order/details/payload.js omits it too.

2. Cross-tenant leak in the label lookups

Not something you introduced — reviewing this surfaced it. The lookups matched on identifier alone:

Entity::where('public_id', $publicId)->orWhere('uuid', $publicId)->withoutGlobalScopes()->first();

We have a CompanyScope, but it isn't registered on Order, Waypoint or Entity, so there was no tenant filter here at all — any authenticated user could render a label for any order, waypoint or entity in another organization by supplying its public id. Since we were touching label code anyway, I fixed it here rather than leave it open.

Worth knowing if you hit this pattern elsewhere: appending the company constraint to the existing chain reads as public_id = ? OR (uuid = ? AND company_uuid = ?) and still leaks. The identifier match has to be grouped in a closure. Resolution also fails closed when there's no company session. Both the internal and public API paths are fixed, with regression tests for the foreign-company, precedence and no-session cases.

Smaller things folded in

  • Reused modals/order-label instead of the new modals/entity-label clone — viewWaypointLabel shares it the same way. Added an @options.subject fallback so the object alt still resolves.
  • failed-to-load-entity-label had a trailing .s.
  • Added the two new keys to bg-bg, es-pa, fr-fr, mn-mn, pt-br and ru-ru, which all already carry the waypoint equivalents.

On the two open questions

Placement — sorry for the slow reply on the issue. The entity card next to edit/delete is the right spot; keep it as you have it.

"Works consistently across multiple entity types" — you can drop that one. Entity::label() renders a single fleetops::labels/entity-label view for every entity type, with no per-type branching, so there's one render path to verify and #280 already covered it.

Backend suite and lint pass locally. Once you've looked over the pushed commit I'm happy to merge.

@roncodes
roncodes merged commit e6b18dd into fleetbase:dev-v0.6.59 Aug 3, 2026
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