diff --git a/crud.py b/crud.py index ffe7e97..ceba368 100644 --- a/crud.py +++ b/crud.py @@ -21,15 +21,12 @@ from lnbits.helpers import urlsafe_short_hash from .models import ( MAX_MENU_DEPTH, AvailabilityWindow, - Category, CreateAvailabilityWindow, - CreateCategory, CreateMenuItem, CreateMenuNode, CreateModifier, CreateModifierGroup, CreateRestaurant, - CreateSubcategory, MenuItem, MenuNode, MenuNodeRow, @@ -41,7 +38,6 @@ from .models import ( Restaurant, RestaurantSettings, SelectedModifier, - Subcategory, ) db = Database("ext_restaurant") @@ -397,129 +393,6 @@ async def delete_menu_node(node_id: str, cascade: bool = False) -> None: ) -# --------------------------------------------------------------------- # -# Categories / subcategories — transitional shims (drop in commit 3) # -# --------------------------------------------------------------------- # -# These keep the old /categories and /subcategories REST endpoints -# working over the new menu_nodes table for one commit's lifetime. -# Drop entirely in the next commit once the new endpoints are live. - - -def _node_row_to_category(row: MenuNodeRow) -> Category: - return Category( - id=row.id, - restaurant_id=row.restaurant_id, - name=row.name, - description=row.description, - sort_order=row.sort_order, - image_url=row.image_url, - time=row.time, - ) - - -def _node_row_to_subcategory(row: MenuNodeRow) -> Subcategory: - # Subcategory carries the parent category id, not its own restaurant. - return Subcategory( - id=row.id, - category_id=row.parent_id or "", - name=row.name, - sort_order=row.sort_order, - time=row.time, - ) - - -async def create_category(data: CreateCategory) -> Category: - node = await create_menu_node( - CreateMenuNode( - restaurant_id=data.restaurant_id, - parent_id=None, - name=data.name, - description=data.description, - sort_order=data.sort_order, - image_url=data.image_url, - ) - ) - return _node_row_to_category(node) - - -async def update_category(category: Category) -> Category: - row = await get_menu_node(category.id) - if not row: - raise ValueError("Category not found") - row.name = category.name - row.description = category.description - row.sort_order = category.sort_order - row.image_url = category.image_url - await update_menu_node(row) - return category - - -async def get_category(category_id: str) -> Optional[Category]: - row = await get_menu_node(category_id) - if not row or row.depth != 0: - return None - return _node_row_to_category(row) - - -async def get_categories(restaurant_id: str) -> list[Category]: - rows = await db.fetchall( - """ - SELECT * FROM restaurant.menu_nodes - WHERE restaurant_id = :rid AND depth = 0 - ORDER BY sort_order, time - """, - {"rid": restaurant_id}, - model=MenuNodeRow, - ) - return [_node_row_to_category(r) for r in rows] - - -async def delete_category(category_id: str) -> None: - await delete_menu_node(category_id, cascade=True) - - -async def create_subcategory(data: CreateSubcategory) -> Subcategory: - parent = await get_menu_node(data.category_id) - if not parent: - raise ValueError("Category not found") - node = await create_menu_node( - CreateMenuNode( - restaurant_id=parent.restaurant_id, - parent_id=parent.id, - name=data.name, - sort_order=data.sort_order, - ) - ) - return _node_row_to_subcategory(node) - - -async def update_subcategory(subcategory: Subcategory) -> Subcategory: - row = await get_menu_node(subcategory.id) - if not row: - raise ValueError("Subcategory not found") - row.name = subcategory.name - row.sort_order = subcategory.sort_order - await update_menu_node(row) - return subcategory - - -async def get_subcategories(category_id: str) -> list[Subcategory]: - rows = await db.fetchall( - """ - SELECT * FROM restaurant.menu_nodes - WHERE parent_id = :pid - ORDER BY sort_order, time - """, - {"pid": category_id}, - model=MenuNodeRow, - ) - return [_node_row_to_subcategory(r) for r in rows] - - -async def delete_subcategory(subcategory_id: str) -> None: - await delete_menu_node(subcategory_id, cascade=True) - - # --------------------------------------------------------------------- # # Menu items # # --------------------------------------------------------------------- # diff --git a/models.py b/models.py index 49dccf9..a71d5bd 100644 --- a/models.py +++ b/models.py @@ -213,46 +213,6 @@ class MenuNode(MenuNodeRow): items: list["MenuItem"] = Field(default_factory=list) -# --------------------------------------------------------------------- # -# Transitional shims (kept until commit 3) # -# --------------------------------------------------------------------- # -# These let the old /categories and /subcategories endpoints keep -# working over the new menu_nodes table for one commit's lifetime. -# Drop in commit 3. - - -class CreateCategory(BaseModel): - restaurant_id: str - name: str - description: Optional[str] = None - sort_order: int = 0 - image_url: Optional[str] = None - - -class Category(BaseModel): - id: str - restaurant_id: str - name: str - description: Optional[str] = None - sort_order: int = 0 - image_url: Optional[str] = None - time: datetime - - -class CreateSubcategory(BaseModel): - category_id: str - name: str - sort_order: int = 0 - - -class Subcategory(BaseModel): - id: str - category_id: str - name: str - sort_order: int = 0 - time: datetime - - # --------------------------------------------------------------------- # # Menu items # # --------------------------------------------------------------------- # diff --git a/static/js/api.js b/static/js/api.js index 52cf7f6..1a7179e 100644 --- a/static/js/api.js +++ b/static/js/api.js @@ -23,17 +23,6 @@ deleteRestaurant: (key, id) => call(key, 'DELETE', `/restaurants/${id}`), - // Categories (transitional shim, drop in commit 3) - listCategories: (id) => call(null, 'GET', `/restaurants/${id}/categories`), - createCategory: (key, data) => call(key, 'POST', '/categories', data), - deleteCategory: (key, id) => call(key, 'DELETE', `/categories/${id}`), - - // Subcategories (transitional shim, drop in commit 3) - listSubcategories: (catId) => - call(null, 'GET', `/categories/${catId}/subcategories`), - createSubcategory: (key, data) => call(key, 'POST', '/subcategories', data), - deleteSubcategory: (key, id) => call(key, 'DELETE', `/subcategories/${id}`), - // Menu nodes (the tree) listMenuNodes: (restaurantId) => call(null, 'GET', `/restaurants/${restaurantId}/menu_nodes`), diff --git a/views_api.py b/views_api.py index de6af8f..3f09a5d 100644 --- a/views_api.py +++ b/views_api.py @@ -34,24 +34,18 @@ from lnbits.decorators import ( from .crud import ( create_availability_window, - create_category, create_menu_item, create_menu_node, create_modifier, create_modifier_group, create_restaurant, - create_subcategory, delete_availability_window, - delete_category, delete_menu_item, delete_menu_node, delete_modifier, delete_modifier_group, delete_restaurant, - delete_subcategory, get_availability_windows, - get_categories, - get_category, get_menu_item, get_menu_items, get_menu_node, @@ -67,7 +61,6 @@ from .crud import ( get_restaurant, get_restaurants, get_settings, - get_subcategories, move_menu_node, update_menu_item, update_menu_node, @@ -77,18 +70,14 @@ from .crud import ( ) from .models import ( AvailabilityWindow, - Category, CreateAvailabilityWindow, - CreateCategory, CreateMenuItem, CreateMenuNode, CreateModifier, CreateModifierGroup, CreateOrder, CreateRestaurant, - CreateSubcategory, MenuItem, - MenuNode, MenuNodeRow, Modifier, ModifierGroup, @@ -97,7 +86,6 @@ from .models import ( OrderWithItems, Restaurant, RestaurantSettings, - Subcategory, ) from .nostr_publisher import ( build_delete_event, @@ -421,72 +409,6 @@ async def api_delete_menu_node( # --------------------------------------------------------------------- # -@restaurant_api_router.get("/api/v1/restaurants/{restaurant_id}/categories") -async def api_list_categories(restaurant_id: str) -> list[Category]: - return await get_categories(restaurant_id) - - -@restaurant_api_router.post("/api/v1/categories") -async def api_create_category( - data: CreateCategory, - wallet: WalletTypeInfo = Depends(require_admin_key), -) -> Category: - restaurant = await get_restaurant(data.restaurant_id) - if not restaurant: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Restaurant not found." - ) - _require_owner(restaurant, wallet) - return await create_category(data) - - -@restaurant_api_router.delete("/api/v1/categories/{category_id}") -async def api_delete_category( - category_id: str, - wallet: WalletTypeInfo = Depends(require_admin_key), -): - cat = await get_category(category_id) - if not cat: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Category not found." - ) - restaurant = await get_restaurant(cat.restaurant_id) - if restaurant: - _require_owner(restaurant, wallet) - await delete_category(category_id) - return "", HTTPStatus.NO_CONTENT - - -@restaurant_api_router.get("/api/v1/categories/{category_id}/subcategories") -async def api_list_subcategories(category_id: str) -> list[Subcategory]: - return await get_subcategories(category_id) - - -@restaurant_api_router.post("/api/v1/subcategories") -async def api_create_subcategory( - data: CreateSubcategory, - wallet: WalletTypeInfo = Depends(require_admin_key), -) -> Subcategory: - cat = await get_category(data.category_id) - if not cat: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Category not found." - ) - restaurant = await get_restaurant(cat.restaurant_id) - if restaurant: - _require_owner(restaurant, wallet) - return await create_subcategory(data) - - -@restaurant_api_router.delete("/api/v1/subcategories/{subcategory_id}") -async def api_delete_subcategory( - subcategory_id: str, - wallet: WalletTypeInfo = Depends(require_admin_key), -): - await delete_subcategory(subcategory_id) - return "", HTTPStatus.NO_CONTENT - - # --------------------------------------------------------------------- # # Menu items # # --------------------------------------------------------------------- # @@ -495,18 +417,16 @@ async def api_delete_subcategory( @restaurant_api_router.get("/api/v1/restaurants/{restaurant_id}/menu") async def api_get_menu(restaurant_id: str) -> dict: """ - Public composite endpoint: returns the menu in three shapes in one + Public composite endpoint: returns the menu in two shapes in one round trip. - * ``tree`` — the full hydrated tree (root nodes with - nested children + items, depth, path). - * ``items`` — flat enriched list (modifier groups, modifier - options, availability windows attached); - useful for search / filter. - * ``categories`` — depth-0 nodes only, with their direct items. - A transitional projection so the existing - CMS keeps rendering until commit 4 swaps it - for q-tree. Drops in commit 3. + * ``tree`` — the full hydrated tree (root nodes with nested + children + items, depth, path). Each item is the + bare MenuItem (no modifier hydration). + * ``items`` — flat enriched list (modifier groups, modifier + options, availability windows attached); useful + for search / filter and for hydrating the items + referenced from ``tree``. The webapp loads this once and then trusts Nostr events for incremental updates. @@ -535,44 +455,10 @@ async def api_get_menu(restaurant_id: str) -> dict: ] enriched_items.append(item_dict) - # Synthetic transitional "categories" projection: depth-0 nodes - # plus their immediate items, mapped to the legacy shape the CMS - # still consumes. Removed in commit 3. - items_by_node: dict[str, list[dict]] = {} - for it in enriched_items: - nid = it.get("node_id") - if nid: - items_by_node.setdefault(nid, []).append(it) - - legacy_categories: list[dict] = [] - for root in tree: - cat_dict = { - "id": root.id, - "restaurant_id": root.restaurant_id, - "name": root.name, - "description": root.description, - "sort_order": root.sort_order, - "image_url": root.image_url, - "time": root.time, - "subcategories": [ - { - "id": child.id, - "category_id": root.id, - "name": child.name, - "sort_order": child.sort_order, - "time": child.time, - } - for child in root.children - ], - "items": items_by_node.get(root.id, []), - } - legacy_categories.append(cat_dict) - return { "restaurant": restaurant.dict(), "tree": [t.dict() for t in tree], "items": enriched_items, - "categories": legacy_categories, # transitional, drop in commit 3 }