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.
| Owner | Frontend — book-bite Partner Portal |
| Backend | hh-server Partner API |
| Design | Figma — Partner Portal Desktop, Group Login |
| Existing component | SelectMultipleBranch.vue |
| Status | Contract 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.
| User | Single-restaurant package | Shared package |
|---|---|---|
| Single-restaurant staff | View and edit as today | View only; disable Edit and Deactivate |
| Group staff | View and edit as today | Edit 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.
| Condition | UI behavior |
|---|---|
| Group Login | Render Branches multi-select in the form header. |
| Single-restaurant login | Do not render the field and preserve the existing form. |
| No accessible restaurant | Keep 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 Branchesexpands to all currently accessible restaurant IDs. - Selecting or removing an individual branch updates the
All Branchesstate 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
403must 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:
| State | FE behavior |
|---|---|
| Empty selection | Prevent submit and show “Please select at least one restaurant.” |
| Unauthorized restaurant ID | Show the backend permission error; do not retry automatically. |
| Single staff mutates shared package | Show “Multi-restaurant packages cannot be edited by single-restaurant staff.” |
| Validation failure | Keep form state and show field/API errors using existing mapping. |
| Network/unknown failure | Show generic failure toast and allow retry. |
| Success | One 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
- Locate the current Create Package form, submit action, restaurant store, and
SelectMultipleBranch.vueusage. - Add a local
selectedRestaurantIdsstate, Group Login visibility guard, and All Branches behavior. - Add
restaurant_idsonly to the Group Login create payload. - Add one-request loading/success/failure handling; remove any per-branch retry or partial-success assumptions.
- Identify the package-list/detail field used to distinguish shared packages. Add the smallest serializer/API contract addition if needed.
- Add view-only behavior for single-restaurant staff on shared packages; retain the API
403as the security boundary. - Preserve the existing edit form for Group staff; do not add a branch selector.
- Keep deactivation and expiry controls per restaurant package.
- Add component tests, request-mock tests, and an end-to-end happy path.
- 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:
| Key | English |
|---|---|
multiBranch.branchField.label | Branches |
multiBranch.branchField.placeholder | Select branches |
multiBranch.branchField.allBranches | All Branches |
multiBranch.validation.required | Please select at least one restaurant. |
multiBranch.submit.progress | Creating package… |
multiBranch.toast.success | Package created successfully. |
multiBranch.permission.sharedPackage | Multi-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
403response 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.