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

HH Menu V3 and Comparing package

Description / Background

It’s difficult to compare menus between packages because each package needs the menu written separately, even when they’re mostly the same, just with different quantities. This takes time and can lead to mistakes. A better solution would allow using the same menu in different packages, only changing the quantities. This would save time and make it much easier to compare and manage packages.

Glossary

https://app.clickup.com/9003122396/docs/8ca1fpw-35796/8ca1fpw-41516

Objectives

  • Admin can see the menu master list and menu group on admin dashboard
  • Admin can set the menu master list on their own page
  • Admin can create, edit, and manage menu groups
  • Admin can filter the menu master list and menu group by various parameters (menu status, upload time, category, image availability, tags)
  • Admin can print the menu group and it will be downloaded as PDF
  • Admin can duplicate the menu group
  • Admin can pick HH Menu V3 on edit package → menu type
  • Admin can pick the menu group and it will show the menu sections
  • Admin can set the quantity of each menu section for how many items can be selected by the user from each menu section
  • Admin can set the unlimited or free flow drink for each menu section
  • Admin can set the time limit minutes on the edit package on the package comparison section
  • Admin can generate AI images for specific menus using pre-configured prompts based on restaurant data (name, description, dining style, cuisine, city, menu details)
  • Admin can generate AI images for all menus in a restaurant/branch
  • Admin can generate AI images only for menus missing images
  • Admin can customize the AI generation prompt before generating (one-time use)
  • Admin can regenerate AI images for menus that already have AI-generated images
  • Admin can see confirmation prompts when replacing real images with AI-generated ones
  • Admin can mark menus with “AI-generated image” flag via checkbox
  • Admin can control the visibility of “AI-generated” label on front-end via “Show AI Generated label” checkbox in menu settings
  • Admin can see the used and unused menu items on Menu Master List
  • Admin can view upload time for each menu (timestamp when menu was first created/submitted)
  • Admin can sort menus with priority: Used menus first, then Unused menus
  • Admin can delete menus by upload time (shows all unique upload times in dropdown)
  • Admin can delete all unused menus at once
  • Admin can filter menus by status (Used/Unused), upload time, category, image availability, and tags
  • The system will show “unlimited” when the menu section type is food
  • The system will show “free flow drink” when the menu section type is beverage
  • The system will hide the menu from user (on detail package and view menu) when the quantity is 0
  • The system will automatically determine “Used” status when menu is: not expired AND linked to packages
  • The system will automatically determine “Unused” status when menu is: expired (regardless of link status) OR not linked to any packages
  • The system will display AI-generated images with visible watermark saying “AI-generated”
  • The system will show disclaimer text on menu pages: “📌 AI-generated image: for illustrative purposes only…” when at least one menu has the “Show AI Generated label” enabled
  • The system will reuse previously generated AI images if the same menu name exists (no regeneration needed)
  • User can see the package comparison for each package type (AYCE, PP, XP)
  • User can only see the package comparison when the package is active or upcoming package
  • User can see AI-generated watermark and disclaimer on menus where enabled
  • User can view menus with clear indication when images are AI-generated (both in QR menu and web/app views)
  • Admin can activate/deactivate the menu on menu master list of each restaurant setting
  • When creating the menu at the first time, the status will be active
    • All the menus created before, will become active
  • Active (by Default)
    • Show to QR menu page / view menu page on HH menu v2, v3, imenupro
    • Show menu master list on menu group
    • If the previous status is Used, no need to change the status. keep it Used
    • If the previous status is Unused no need to change to be Used, unless it meets the requirement to be Used. check here: Used & Unused Status (https://app.clickup.com/9003122396/docs/8ca1fpw-11562/8ca1fpw-49076)
    • Only menu with active status will be displayed on menu sections on menu group setting (HH menu V2/V3)
  • Inactive
    • Don’t Show to QR menu page / view menu page on HH menu v2, v3, imenupro
    • Don’t Show menu master list on menu group
    • Used status will be changed to Unused status

Scope

HH menu

Sequence Diagram / Flow

Sequence: Package page loads + “Comparing packages” (AYCE/PP) Compare Sequence

Sequence: Admin manages Master Menu & Menu Group

Component/Context diagram

ERD

Backend Implementation

  • Main Formapp/javascript/admin/pages/packages/MainForm.vue
    • Integrates a new MenuType component.
    • Handles selectedMenuTypeFromResponse and propagates it to child components.
    • Standardizes and simplifies form structure.
    • Adds logic to normalize package_type for PartyPack and Ayce.
  • MenuType.vue: (New) Handles menu type selection.
  • menuItem/MenuV1.vue and menuItem/MenuV3.vue: (New) Support for menu management, including dynamic menu group and section selection, editing, and quantity/unlimited toggles.
  • app/services/menu_data_service.rb: Fetches and processes menu data from external APIs, supports both Ayce and PartyPack.
  • app/services/comparing_package_service.rb: Compares packages for a menu group and restaurant, returns formatted comparison data.
  • app/services/branch_list_service.rb: Handles branch listing, filtering, and pagination.
  • app/services/restaurant_list_service.rb: Handles restaurant listing, filtering, and pagination.
  • app/controllers/hh_menu/comparing_package_lists_controller.rb: (New)
    • New endpoint for comparing packages (PartyPack & Ayce) by menu group and restaurant.
    • Validates and normalizes package_type.
    • Uses the new ComparingPackageService.
  • app/controllers/hh_menu/packages_controller.rb
    • Normalizes package_type parameter for PartyPack and Ayce.
    • No longer relies on ambiguous values like pp/ayce.
  • app/controllers/hh_menu/restaurants_controller.rb
    • Adds new actions:
      • branch: Get branch details.
      • branches: Paginated, filtered branch list using BranchListService.
      • restaurant_lists: Paginated, filtered restaurant list using RestaurantListService.
    • Now includes ResponseCacheConcern for caching.
  • Adds a new “Menu” dropdown for quick links to Menu Master List and Menu Groups in the admin panel.
  • Utility for serializing restaurant packages, with optional inclusion of the restaurant.
  • Wherever branches were filtered by their associated restaurants, the code used both .active and .not_expired scopes together. Example:
    • branch.restaurants.active.not_expired.count > 0
    • For master menu filtering: branch.restaurants.active.not_expired
  • The set of restaurants returned by this service is now broader, as restaurants do not need to be “active”—only “not expired” to be included.
  • Adds support for menu_v3.
  • If the package uses menu_v3, it calls a new method: calculateOriginalPriceV3().
  • Updates the warning to include 'HH Menu V3'.
  • New Helper Methods:
  • getMenuSectionsV3()
    • Fetches menu sections for the selected menu group (includes menus).
    • Handles errors (menu group ID not found, API error).
  • getMenuGroupDetailsV3()
    • Fetches details for the menu group, including sections, menu group sections, and menus.
    • Handles errors similarly.
  • This change likely ensures that the rewards page for a specific user in the admin section uses the correct route, probably one that is scoped or protected for admin users.
  • Adds a new attribute package_status that:
    • Returns active if today’s date is within (or after) the package’s start_date (and before or on the end_date if it exists).
    • Returns inactive otherwise (start date is in the future, end date is in the past, or dates are missing).
    • The logic prioritizes:
      • If both start_date and end_date are present:
      • Active if today is between those dates (inclusive)

HH MENU REPO:

  • Adds new admin dashboard pages for managing MasterMenu and MenuGroup resources, using the Administrate gem.
  • New filters and search options for master menus and menu groups (by ID, outlet, date, etc.).
  • Modal UI for duplicating menu masters with outlet/branch selection.
  • Custom table views and improved navigation.
  • admin/master_menus_controller.rb and admin/menu_groups_controller.rb provide CRUD, search, filtering, and duplication actions for admins.
  • Outlet/branch info is fetched from external APIs for display.
  • Duplicating a master menu or group involves copying all associated menus/sections.
  • New admin base controller with an access token check, and an AuthController for OAuth2 admin login/logout flows.
  • Adds Api::V1::MenuGroupsControllerApi::V1::MasterMenusControllerApi::V1::BranchesController, etc., supporting CRUD, serialization, and bulk operations.
  • New API endpoints for creating, updating, and importing menu groups and master menus.
  • Enhanced SectionsController to support menu group associations and new attributes (like unlimited).
  • Bulk import endpoints for menu groups/master menus via Excel files.
  • New Models:
    • MenuGroupMenuGroupSection, and supporting serializers and dashboards.
  • Adds new columns to existing tables:
    • branch_id to master_menuscategory_menus, etc.
    • custom_menu_id to menus.
    • menu_group_idsection_type, and unlimited to sections.
    • main_languageoptional_languagepackage_type to menu_groups.
  • Enforces at least one of restaurant_id or branch_id for menu groups/master menus.
  • Updates model relationships for new entities.
  • Adds new and customized ERB partials for the admin panel (navigation, sidebar, collection tables, modals, etc.).
  • Improved flash messaging and layout structure.
  • Gemfile: Adds the administrate gem for the new admin UI.
  • Dockerfile: Updates asset precompilation for Tailwind CSS.
  • Routes: Extensive new admin and API routes for all new features.
  • The controller responsible for admin master menus (master_menus_controller.rb) was updated so its search now strictly uses only the outlet name.
  • This is enforced by initializing the GraphQL search input with { searchNameOnly: true }.
  • This prevents other search criteria from interfering, making results more precise and predictable.
  • The code now contains explicit comments and inline documentation explaining the use of the searchNameOnly parameter.
  • The GraphQL query is documented to show how $input: SearchInput! is expected and how searchNameOnly should be passed.
  • This helps anyone maintaining or extending the code to avoid misuse or misunderstanding of the search input.
  • Adds a deleted_at column (datetime) to the sections table.
  • Adds an index on this column for better query performance.
  • Adds logic to set the deleted_at timestamp when soft-deleting.
  • Adds scopes:
    • not_deleted: Returns sections where deleted_at is null.
    • deleted: Returns sections where deleted_at is not null.
  • Overrides the default destroy method to perform a soft-delete.
  • Adds a method to restore a deleted section by setting deleted_at to null.
  • Updates the destroy action to perform a soft-delete instead of a hard delete.
  • Adds a new restore action to allow recovery of soft-deleted sections.
  • Ensures that deleted sections are excluded from standard queries and responses.
  • Filters out soft-deleted sections when fetching menu groups.
  • Adds support for branch filtering and section/package filters.
  • Updates associations to use the new not_deleted scope, ensuring soft-deleted sections are excluded unless specified.
  • Adds all_sections association to get all sections including deleted ones if needed.
  • When returning restaurant IDs, only includes those associated with non-deleted sections.
  • Adds caching hints for improved performance.
  • Adds new routes to support the restore functionality (PUT /restore) for sections under both packages and groups.
  • Updates schema comments to document the new deleted_at field.
  • Ensures API responses properly exclude deleted sections and reflect restoration status.
  • The PR addresses an issue related to searching for outlets and branches in the codebase.
  • It modifies 2 files, adding 14 lines of code and no deletions.
  • The changes are likely focused on improving or correcting the search functionality for outlets and branches.
  • Themenu_groups_controller.rb adds a destroy action that checks for linked sections before allowing deletion. If any sections are linked, deletion is blocked with an alert.
  • The imports_controller.rb ensures that when importing, if a menu_group_id or master_menu_id is missing, the system automatically creates the required MenuGroup or MasterMenu and includes their IDs in the response.
  • improves restaurant and branch filtering for creating master menus.
  • New boolean column active to menus table (default: true, not null).
  • Scopes (activeinactive) and methods (activate!deactivate!) in the Menu model.
    • New boolean column active to menus table (default: true, not null).
    • Scopes (activeinactive) and methods (activate!deactivate!) in the Menu model.
  • Added import_at:
    • New datetime column import_at to menus table for tracking import time.
    • Sets import_at to current time when importing menus in various import controller actions.
  • Updated permitted and serialized menu attributes to include active and import_at.
  • Updated factory and spec for menus to support new attributes.
  • New service class to check if a menu is “used” (included in selected_menus and has active restaurant/package).
    • Uses external API calls to determine restaurant & package status, result is cached (2 min TTL).
    • Provides public class methods for clearing the relevant cache.
  • MenuSerializer:
    • Adds used_menu attribute, using the above service to expose usage status per menu.
    • Frontend is advised (in controller code comments) to filter menus by used_menu client-side for performance, rather than server-side.
  • In all places where selected_menus are created, updated, or destroyed, now call cache invalidation for affected menus (using MenuUsageCheckerService).
  • Sections are “touched” (timestamp updated) after selected_menu changes to help with caching.
  • selected_menu.rb, my_image.rb, and section.rb now call Rails.cache.delete for affected menu keys whenever related records are changed (after_save, after_destroy).
  • Section model now clears usage cache for all its selected_menus when saved or destroyed.
  • When a menu is set to inactive (menu.active = false) via create or update, it is automatically removed from all selected_menus.
  • Comments added to explain that server-side filtering by used_menu is not supported (performance).
  • Comments and logic improved for handling and clearing caches, updating section modification time (touch), and managing selected menu assignments.
  • MenuSerializer and MyImageSerializer updated to expose new fields (active, import_at, created_at).
  • Factory file for Menu updated: support for active (default true), import_at, and nil category_menu_id.
  • add active to menus table (boolean, default true, not null)
  • add import_at to menus table (datetime)

https://github.com/hungryhub-team/hh-server/pull/7078/files

https://github.com/hungryhub-team/hh-menu/pull/236

https://github.com/hungryhub-team/hh-menu/pull/263/files

https://github.com/hungryhub-team/hh-menu/pull/264/files

https://github.com/hungryhub-team/hh-server/pull/6142/files

https://github.com/hungryhub-team/hh-menu/pull/268/files

https://github.com/hungryhub-team/hh-server/pull/7191/files

https://github.com/hungryhub-team/hh-server/pull/7096/files

https://github.com/hungryhub-team/hh-menu/pull/265/files

https://github.com/hungryhub-team/hh-server/pull/7106/files

https://github.com/hungryhub-team/hh-menu/pull/267/files

https://github.com/hungryhub-team/hh-server/pull/7078/files

https://github.com/hungryhub-team/hh-menu/pull/281

Frontend Implementation

  • New /v3 routes are added in src/router/index.js:
    • /v3/master/create and /v3/master/:id for master menu creation and editing under the new flow.
    • /v3/package/:id/v3/menu-group/create/v3/menu-group/:id for package and menu group handling.
    • /v3/layout and /v3/print for new layout and print views.
  • SectionMenuListCategory.vue (new): Displays menu sections grouped by category, including support for multi-language and price options.
  • MenuCategory.vue (new): Simple component to display a category header.
  • SectionComparison.vue (new): Provides a comparison table for packages (e.g., “All You Can Eat” vs. “Party Pack”), supporting dynamic columns based on package type.
  • views/layout/v3/IndexV3.vue (new): The main container for the new v3 layout/print page. It loads and coordinates header, footer, package comparison, categories, and menu list, fetching required data from APIs.
    • Handles loading states, API calls, and error notifications.
  • stores/menuGroup.js (new): Manages the state for menu groups, including form data for branch, restaurant, name, and sections.
  • stores/packageList.js: Adds a section_type field (default “food”) to section state for v3 handling.
  • stores/master.js: Adds a custom_menu_id field for menu item identification.
  • views/master/MasterList.vue:Conditional rendering based on the route (checks if in v3 mode).
  • New logic for handling outlet/branch selection (FormOutletSetting.vue).
  • Ability to create a master menu and its items in a single API call (createNewMasterAndMenu), and redirect after creation.
  • The form now includes and persists custom_menu_id for each menu item.
  • FormOutletSetting.vue: Allows users to select either an outlet or branch and search for them via API.
  • views/package/FormSection.vue:
    • Adds a section_type radio selector (“food” or “beverage”) for v3.
    • Adjusts logic for add/update section to support both save-and-edit and non-edit (for v3).
    • Handles menu selection and availability based on new menu group data structure.
  • views/print/PrintMenu.vue:
    • Adds v3 support for template selection (package/group based) and group naming for print output.
    • Changes parameter passing and URL construction logic for print/preview in v3 mode.
    • Integrates new restaurant selection logic for v3 menu groups and branches.
  • SelectLang.vue: Button styling now rotates when open.
  • FormLanguageSetting.vue: Adds usePrice and useTranslate props to control optional UI.
  • Many components: Conditional logic and props to control v3 vs. legacy flows.
  • General code tidying: Use of vue-types for prop validation, improved comments, and clearer method names.
  • Adds logic for handling ‘Other’ menu categories if items do not have a category_menu_id.
  • Adds random key generation for list rendering stability in v3 components.
  • Handles error and loading states gracefully in new and updated components.
  • Generalizes several API endpoints to work with either branch or restaurant context.
  • Menu images and gallery images now load reliably from the correct domains.
  • The code is cleaner, with fewer hardcoded values and more flexible helpers.
  • Added: date-fns@^4.1.0 for date operations.
  • Removed: moment@^2.29.3 (previously used for date/time formatting).
  • Applied max height and scroll to dropdown (SelectLang.vue) for better usability.
  • Added visual toggle and status tags in FormMenu.vue for menu active state and used/unused labelling.
  • Added filters for menu status, import time, category, image (with/without), and tags (popular, vegetarian, chef-recommended, long-cooking).
  • Filtered lists update the .show property on each menu to control visibility.
  • Shows a “No result” message if nothing matches the filters applied.
  • Add Delete by imported time: Prompts for a timestamp to delete all menus imported at that time.
  • Add Delete all unused menus: Deletes all menus not marked as used.
  • Add Checkbox toggle per menu for setting whether a menu is active.
  • Menu displays, menu grouping, and print/export views now only show items with active: true.
  • In multiple places (MenuGroup, Print, layout Index and v3 layout), menu lists filter and group only active items.
  • All date manipulations in FormSection.vue migrated from moment.js to date-fns, including checks, formatting, and submission of seasonal menu ranges.

https://github.com/hungryhub-team/hh-menu-frontend-new/pull/143

https://github.com/hungryhub-team/hh-pegasus/pull/2039/files

https://github.com/hungryhub-team/hh-pegasus/pull/2039/files

https://github.com/hungryhub-team/hh-pegasus/pull/2030/files

https://github.com/hungryhub-team/hh-pegasus/pull/2039/files

https://github.com/hungryhub-team/hh-menu-frontend-new/pull/190

PRD & Task

PRD: https://app.clickup.com/9003122396/docs/8ca1fpw-7922/8ca1fpw-42456 Active & Inactive Menu : https://app.clickup.com/9003122396/v/dc/8ca1fpw-11562/8ca1fpw-53936

TASK: https://app.clickup.com/t/86cuu81rm https://app.clickup.com/t/86d157268 (https://app.clickup.com/t/86czkyeq3)[https://app.clickup.com/t/86czkyeq3]

Design

https://www.figma.com/file/BIXEZKqq8npMPpE7lu4lCK/Comparing-packages-table-(Party-Pack)?type=design&node-id=0-1&mode=design

API Blueprint

MethodPathURLDescriptionPayload
GET/api/v1/comparing_package_listsAPI endpoint to compare package lists by menu group and package type.menu_group_idpackage_typerestaurant_id
GET/api/v1/menu_groups/check_datato fetch detailed package-section data.-
GET/api/v1/restaurants/branch/:idReturns info (idname) for a specific branch by ID.id (in path)
GET/api/v1/branches Returns a paginated list of branches with active, not-expired restaurants. Supports filtering by name and pagination parameters.name_likepageper_page
GET/api/v1/restaurant_listsReturns a paginated list of active, not-expired restaurants. Supports name filtering, pagination, and caching.name_likepageper_page
-----
GET/hh_menu/restaurant_lists.jsonname_like (string, query)
GET/hh_menu/restaurants/{id}id  (path)
GET/hh_menu/branches.jsonname_like (string, query)
GET/hh_menu/restaurants/branch/{id}
id  (path)
POST/api/v1/master_menus/create_master_menu{ branch_id, restaurant_id, main_language, menu: [...] } (body, JSON)
GET/api/v1/menu_groups/{id}/detailid (path)
GET/api/v1/branches/{id}/master_menuid (path)
GET/api/v1/restaurants/{id}/master_menuid (path)

New Query

N/A

DB Schema / Database Migration

  • Creates the users table for Devise authentication.
  • Adds admin functionality and Devise “confirmable” to users
  • Creates the menu_groups table.
  • Adds a branch_id column to the master_menus table and an index on it.
  • Adds a custom_menu_id column to the menus table and an index on it.
  • Adds menu_group_id to the sections table.
  • Adds an unlimited boolean column (default: false) to sections, indicating if the section has unlimited quantity.
  • A data migration that seeds two admin users.
  • Adds main_language and optional_language ` to menu_groups
  • Adds a branch_id column (bigint) and index to category_menus, enabling menus to be associated with a specific branch.
  • Creates a new table called menu_group_sections to represent a many-to-many relationship between MenuGroup and Section .
  • Adds a menu_group_id column to all relevant package tables (e.g., party_packsaycehungry_sets, etc.) to relate packages to menu groups.
  • add active to menus table (boolean, default true, not null)
  • add import_at to menus table (datetime)

Improvement:

N/A