# Codebase Analysis: Menu Fetching, Rendering, and Modeling This document provides a detailed investigation and mapping of the menu systems across the HomAroma applications. --- ## 1. TypeScript Type Definitions for Menus TypeScript definitions for menus are spread across the shared types package and individual application stores/components. ### Shared / Package Definitions **File**: `packages/types/index.ts` These define the database-facing structures and basic relational entities. ```typescript export interface MenuItem { id: number label: string url: string order: number translations?: any[] } export interface Menu { id: number name: string items: MenuItem[] payload?: any } ``` ### SPA Application definitions **File**: `apps/spa/src/store/menuStore.ts` These define the recursive client-side tree structure used by the Mega Menu payload. ```typescript export interface MenuLink { label: Record; url?: string; links?: MenuLink[]; } export interface MenuColumn { title: Record; links: MenuLink[]; } export interface MegaMenuPayload { id?: string; nodeId?: string; label: Record; url?: string; columns?: MenuColumn[]; } ``` ### Website Storefront definitions **File**: `apps/website/src/components/navigation/menuTypes.ts` Similar to the SPA definitions but more flexible, supporting both localized translation maps and plain string fallbacks. ```typescript export interface MenuLink { label: Record | string url?: string links?: MenuLink[] } export interface MenuColumn { title: Record | string links: MenuLink[] } export interface MegaMenuPayload { id?: string nodeId?: string label: Record | string url?: string columns?: MenuColumn[] } ``` --- ## 2. Elysia Backend Routes **File**: `apps/backend/src/routes/menus.ts` The backend exposes routes under the prefix `/menus` to manage menu structure, fetch menus, and update metadata. ### Route List and Functionality - **`GET /`**: Retrieves all menus, including their menu items sorted by `order` in ascending order and all associated translations. - **`GET /:name`**: Retrieves a single menu by name. If it is not found in the database, it returns a default fallback structure: ```json { "name": "name", "items": [], "payload": null, "isActive": false } ``` - **`POST /`** *(Admin only)*: Creates a menu with an optional array of flat items. - **`PUT /:name`** *(Admin only)*: The main entry point to upsert/save a menu's tree payload. If the menu does not exist, it is created. It then uses a database transaction (`prisma.$transaction`) to: 1. Retrieve all existing `MenuItem` entries. 2. Compute item differences and delete any old `MenuItem`s not present in the payload. 3. Iterate over the input items. If an item exists, it updates `label`, `url`, and `order`, and upserts its language translations in `MenuItemTranslation`. 4. If the item is new, it creates a new `MenuItem` and its nested translations. - **`PATCH /items/:id/translations`**: Directly manages/patches the translations for a single `MenuItem` for a set list of supported languages (`['th', 'en', 'ja', 'zh', 'vi', 'ko']`). ### Request Validation Elysia validates request payloads using TypeBox schemas. The `PUT /:name` schema is: ```typescript { body: t.Object({ payload: t.Optional(t.Any()), isActive: t.Optional(t.Boolean()), items: t.Array(t.Object({ id: t.Optional(t.Number()), label: t.String(), url: t.String(), order: t.Number(), translations: t.Optional(t.Array(t.Object({ langCode: t.String(), label: t.String() }))) })) }) } ``` *Note: The `payload` field is validated as `t.Optional(t.Any())`. This means backend validation is schema-less for the JSON tree structure stored in `Menu.payload`.* --- ## 3. Frontend Menu Rendering The main menu and dropdown panels are fetched and rendered differently in `apps/spa` and `apps/website`. ### A. SPA Frontend (`apps/spa`) - **Fetching**: Fetched via the Zustand store (`useMenuStore`) inside `apps/spa/src/components/Navbar.tsx` on component mount (`fetchMegaMenu()`). - **Desktop Mega Menu (`apps/spa/src/components/DesktopMegaMenu.tsx`)**: - Maps `items` (i.e. `MegaMenuPayload[]`) horizontally to trigger buttons. - Dropdown panels are positioned absolutely flush below the navbar using a CSS custom property `--navbar-bottom` computed dynamically by the `Navbar`. - **Image Placements (Currently Mocked)**: - *Root Image (Far Left)*: Mocked using ``. - *Column Image (Inside Center Grid)*: Renders a 1:1 ratio square thumbnail above/next to each column title, mocked using ``. - *Link Hover Image (Far Right)*: Tracks the hovered link inside `onMouseEnter` of each link list item. The hovered image URL is stored in local state `hoveredImage` and rendered inside the right column. It is mocked using `https://picsum.photos/seed/link-${uniqueId}-${idx}-${lIdx}/400/400`. - **Mobile Drawer (`apps/spa/src/components/MobileDrawer.tsx`)**: - Uses stack-based navigation to transition between parent menus and child links. - Does **not** render any images. ### B. Website Storefront (`apps/website`) - **Fetching**: Fetched during Server-Side Rendering (SSR) in `apps/website/src/app/[lang]/layout.tsx` (`getMegaMenu(lang)`) and passed down to client-side components to eliminate layout shifts or content flashes. - **Desktop Mega Menu (`apps/website/src/components/navigation/DesktopMegaMenu.tsx`)**: - Renders a horizontal trigger list and dropdown panels similarly to the SPA. - **Current Gap**: The 3-image layout (Root, Column, Hover) is **not yet implemented**. It only renders the columns and links list structure. - **Mobile Drawer (`apps/website/src/components/navigation/MobileDrawer.tsx`)**: - Stack-based hierarchical navigation drawer. - Hides the complex image elements. --- ## 4. Admin Menu Manager Page **File**: `apps/admin/src/pages/MenuManagerPage.tsx` The `MenuManagerPage` is where administrators configure the flat footer links and the hierarchical mega menu payload. ### Construction and Editing - **Hierarchy Representation**: The page uses a recursive sub-component `TreeNodeEditor` to build and edit the menu structure: - **Root Nodes (MegaMenuPayload)**: - Editable fields: translations for `label`, `url`. - Contains child array: `columns` (representing `MenuColumn`s). - **Column Nodes (MenuColumn)**: - Editable fields: translations for `title`. - Contains child array: `links` (representing `MenuLink`s). - **Link Nodes (MenuLink)**: - Editable fields: translations for `label`, `url`. - Allows further nested links. - **Save Operations**: - Triggered by submitting the form. - Makes a `PUT` request to `${API_BASE}/menus/${selectedMenuName}`. - For the `main` menu, the payload consists of: ```json { "items": [], "payload": [...] // Complete JSON tree structures built via TreeNodeEditor } ``` - **Current Gap**: There are no input controls in `TreeNodeEditor` to set `rootImage`, `columnImage`, or `hoverImage` values.