Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multi-Branch Package Creation — Frontend Implementation Plan

Companion to Multi_Branch_Package_Creation_20260909000000.md. This is the implementation plan for book-bite Partner Portal web.

OwnerFrontend — book-bite Partner Portal
Backendhh-server Partner API
DesignFigma — Partner Portal Desktop, Group Login
Existing componentSelectMultipleBranch.vue
StatusContract aligned with the current hh-server API.

1. Product behavior

Create

When the current login has access to multiple restaurants (loginAsGroup === true), show a Branches multi-select in the Create Package form. The user can select one, several, or all accessible restaurants. Submit one request and create one shared package.

When the current login has only one restaurant, keep the current create flow. Do not add restaurant_ids to that request.

View and edit

Package ownership is shared at the package level, while activation and expiry remain restaurant-specific.

UserSingle-restaurant packageShared package
Single-restaurant staffView and edit as todayView only; disable Edit and Deactivate
Group staffView and edit as todayEdit shared package; the change applies to all attached restaurants

The edit form does not get a Branches selector. Editing a shared package is a package-level action, not a new branch-selection action.

The UI must not rely on the disabled state as the security boundary. If the API returns 403, keep the package unchanged and show the localized permission error.

2. Create form implementation

2.1 Branches field

Reuse SelectMultipleBranch.vue and the same restaurant source already used by Group Login.

ConditionUI behavior
Group LoginRender Branches multi-select in the form header.
Single-restaurant loginDo not render the field and preserve the existing form.
No accessible restaurantKeep submit unavailable and show the existing access/validation state.

The All Branches shortcut may select all restaurants currently available in userStore.restaurants. Resolve the final IDs at submit time so the request reflects the current access list.

2.2 Selection state

Recommended state shape:

selectedRestaurantIds: []

Rules:

  • Remove duplicate IDs before submit.
  • At least one restaurant is required for Group Login create.
  • Selecting All Branches expands to all currently accessible restaurant IDs.
  • Selecting or removing an individual branch updates the All Branches state consistently.
  • Do not mutate package fields per branch in the form; package fields are shared.

2.3 Submit state

Use a single request state:

idle -> validating -> submitting -> success
                         \-> failure

During submitting:

  • Disable the submit button and prevent double-submit.
  • Show Creating package….
  • Do not show branch-level progress because the backend operation is one transaction.

On success:

  • Show one success toast.
  • Close/reset the form using the existing create flow.
  • Refresh or navigate to the package list once.

On failure:

  • Keep the form values so the user can correct or retry.
  • Show the backend error message.
  • Do not display partial success; the backend either creates all selected branches or none.

3. API contract

3.1 Multi-restaurant create

POST /api/partner/v1/restaurant_packages

Send the existing package payload and add restaurant_ids at the request top level:

{
  "package_type": "ayce",
  "restaurant_ids": [101, 102, 103],
  "package": {
    "name_en": "Shared package",
    "restaurant_packages_attributes": {
      "0": {
        "start_date": "2026-09-18",
        "end_date": "2026-12-31"
      }
    }
  }
}

The FE sends the restaurant selection once. The backend creates one shared package and one RestaurantPackage per selected ID.

Expected success response includes the legacy singular restaurant-package ID, every created restaurant-package ID, and the one creation submission ID:

{
  "success": true,
  "restaurant_package_id": 9001,
  "restaurant_package_ids": [9001, 9002, 9003],
  "submission_id": 7001
}

3.2 Single-restaurant create regression

If the user is not Group Login, keep the current request shape and omit restaurant_ids. Do not send [defaultRestaurantId] as a new field on the legacy path.

3.3 Edit and mutation permissions

Keep the existing edit endpoint and package form. Package-list responses expose attributes.restaurants as an array of { id, name, url } for every attached non-deleted restaurant. Use that complete list for the Branches column and any shared-package presentation; do not construct it from the currently selected/default restaurant only.

For a shared package:

  • Group staff may submit the normal edit request.
  • Single-restaurant staff see the package but Edit is disabled/hidden according to the existing design system.
  • A direct update or deactivate request returning 403 must be handled as a permission failure.

Deactivation controls remain tied to an individual RestaurantPackage; the FE must not turn them into a package-wide toggle.

4. Error and copy handling

Use the existing API error/toast infrastructure. Required states:

StateFE behavior
Empty selectionPrevent submit and show “Please select at least one restaurant.”
Unauthorized restaurant IDShow the backend permission error; do not retry automatically.
Single staff mutates shared packageShow “Multi-restaurant packages cannot be edited by single-restaurant staff.”
Validation failureKeep form state and show field/API errors using existing mapping.
Network/unknown failureShow generic failure toast and allow retry.
SuccessOne success toast; refresh once.

No new FE currency decision is required. The FE should display the server response if the backend rejects a request for any existing validation reason.

5. Implementation sequence

  1. Locate the current Create Package form, submit action, restaurant store, and SelectMultipleBranch.vue usage.
  2. Add a local selectedRestaurantIds state, Group Login visibility guard, and All Branches behavior.
  3. Add restaurant_ids only to the Group Login create payload.
  4. Add one-request loading/success/failure handling; remove any per-branch retry or partial-success assumptions.
  5. Identify the package-list/detail field used to distinguish shared packages. Add the smallest serializer/API contract addition if needed.
  6. Add view-only behavior for single-restaurant staff on shared packages; retain the API 403 as the security boundary.
  7. Preserve the existing edit form for Group staff; do not add a branch selector.
  8. Keep deactivation and expiry controls per restaurant package.
  9. Add component tests, request-mock tests, and an end-to-end happy path.
  10. Verify the non-group create payload and form snapshot remain unchanged.

6. Analytics

Use one event for a successful multi-restaurant creation if the existing analytics convention requires it:

multi_branch_package_created
  restaurant_ids
  branch_count
  package_type

Do not emit partial-success events because partial success is not a valid outcome of the one-transaction contract.

7. i18n keys

Reuse existing branch-selection and API error translation keys where available. New copy should be localized through the existing book-bite i18n system:

KeyEnglish
multiBranch.branchField.labelBranches
multiBranch.branchField.placeholderSelect branches
multiBranch.branchField.allBranchesAll Branches
multiBranch.validation.requiredPlease select at least one restaurant.
multiBranch.submit.progressCreating package…
multiBranch.toast.successPackage created successfully.
multiBranch.permission.sharedPackageMulti-restaurant packages cannot be edited by single-restaurant staff.

8. Test plan

Component tests

  • Group Login renders the Branches field.
  • Single-restaurant login does not render the Branches field.
  • All Branches selects and clears correctly.
  • Empty selection prevents submit.
  • Group create sends one request with the expected restaurant_ids.
  • Submit is disabled while the request is in flight.
  • Success resets/refreshes once.
  • Failure preserves form state.

Edit permission tests

  • Single staff can open/view a shared package.
  • Single staff does not get an enabled Edit or Deactivate action.
  • Group staff can open and submit the shared package edit flow.
  • A direct 403 response is rendered as a permission error without clearing the form.
  • Deactivation remains scoped to the selected restaurant package.

E2E/regression tests

  • Group Login selects three restaurants and creates one shared package.
  • A failing response leaves no partial-success UI state.
  • Non-group create remains unchanged and omits restaurant_ids.
  • Shared package created by Group Login is visible to single staff but read-only.