diff --git a/data/cafe-theme-presets.json b/data/cafe-theme-presets.json
new file mode 100644
index 0000000..ec5b51f
--- /dev/null
+++ b/data/cafe-theme-presets.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "palettes": [
+ "meezi-green", "ocean-blue", "royal-purple", "sunset-orange", "rose-blush",
+ "charcoal-gold", "espresso", "forest", "midnight", "coral", "gold-luxury",
+ "mint-fresh", "wine-bar", "slate-modern", "cherry", "teal-wave", "sand-cafe"
+ ],
+ "panelStyles": ["flat", "modern", "glass", "minimal", "bold", "soft", "elevated", "outline"],
+ "menuStyles": ["cards", "compact", "grid", "list", "magazine", "classic"],
+ "menuTextures": ["none", "paper", "linen", "dots", "grid", "marble", "wood", "warm"],
+ "densities": ["compact", "comfortable", "spacious"],
+ "radius": ["none", "sm", "md", "lg", "full"],
+ "customColorKeys": ["primary", "secondary", "accent", "background", "surface", "text", "textMuted", "destructive", "success"]
+}
diff --git a/data/category-icon-presets.json b/data/category-icon-presets.json
new file mode 100644
index 0000000..9b46e68
--- /dev/null
+++ b/data/category-icon-presets.json
@@ -0,0 +1,46 @@
+{
+ "version": 2,
+ "styles": ["flat", "modern", "real", "minimal", "outline", "soft", "bold", "gradient", "pastel", "duotone"],
+ "presets": [
+ { "id": "drinks-hot", "kind": "drink" },
+ { "id": "drinks-cold", "kind": "drink" },
+ { "id": "drinks-tea", "kind": "drink" },
+ { "id": "drinks-juice", "kind": "drink" },
+ { "id": "drinks-milkshake", "kind": "drink" },
+ { "id": "drinks-alcohol", "kind": "drink" },
+ { "id": "drinks-beer", "kind": "drink" },
+ { "id": "breakfast", "kind": "food" },
+ { "id": "food-mains", "kind": "food" },
+ { "id": "food-fastfood", "kind": "food" },
+ { "id": "food-rice", "kind": "food" },
+ { "id": "pasta-pizza", "kind": "food" },
+ { "id": "dessert", "kind": "food" },
+ { "id": "ice-cream", "kind": "food" },
+ { "id": "bakery", "kind": "food" },
+ { "id": "salad", "kind": "food" },
+ { "id": "grill", "kind": "food" },
+ { "id": "seafood", "kind": "food" },
+ { "id": "snacks", "kind": "food" },
+ { "id": "snacks-sweet", "kind": "food" },
+ { "id": "appetizers", "kind": "food" },
+ { "id": "vegan", "kind": "food" },
+ { "id": "fruits", "kind": "food" },
+ { "id": "specials", "kind": "food" },
+ { "id": "chef-special", "kind": "food" },
+ { "id": "generic", "kind": "food" }
+ ],
+ "emojiGroups": [
+ "hotDrinks",
+ "coldDrinks",
+ "breakfast",
+ "mains",
+ "pastaPizza",
+ "desserts",
+ "salads",
+ "seafoodGrill",
+ "snacks",
+ "vegan",
+ "specials",
+ "general"
+ ]
+}
diff --git a/data/demo-credentials.json b/data/demo-credentials.json
new file mode 100644
index 0000000..c227ba8
--- /dev/null
+++ b/data/demo-credentials.json
@@ -0,0 +1,34 @@
+{
+ "version": 1,
+ "environment": "development",
+ "cafe": {
+ "id": "cafe_demo_001",
+ "slug": "demo-cafe",
+ "name": "کافه دمو"
+ },
+ "branch": {
+ "id": "branch_demo_main",
+ "name": "شعبه اصلی"
+ },
+ "auth": {
+ "note": "در محیط Development با OTP توسعه وارد شوید (کد در لاگ API).",
+ "otpDevBypass": true
+ },
+ "systemAdmin": {
+ "phone": "09120000001",
+ "note": "ورود مدیر سامانه: /fa/admin/login — OTP در لاگ API"
+ },
+ "employees": [
+ { "id": "emp_demo_owner", "name": "مدیر دمو", "phone": "09121234567", "role": "Owner" },
+ { "id": "emp_demo_manager", "name": "مدیر شعبه", "phone": "09121111111", "role": "Manager" },
+ { "id": "emp_demo_cashier", "name": "صندوقدار", "phone": "09122222222", "role": "Cashier" },
+ { "id": "emp_demo_waiter", "name": "گارسون", "phone": "09123333333", "role": "Waiter" },
+ { "id": "emp_demo_waiter2", "name": "گارسون ۲", "phone": "09124444444", "role": "Waiter" },
+ { "id": "emp_demo_chef", "name": "آشپز", "phone": "09125555555", "role": "Chef" },
+ { "id": "emp_demo_delivery", "name": "پیک", "phone": "09126666666", "role": "Delivery" }
+ ],
+ "publicQr": {
+ "tableQr": "demo_table_01",
+ "discoverSlug": "demo-cafe"
+ }
+}
diff --git a/data/demo-menu-food101.json b/data/demo-menu-food101.json
new file mode 100644
index 0000000..3afb954
--- /dev/null
+++ b/data/demo-menu-food101.json
@@ -0,0 +1,644 @@
+{
+ "version": 1,
+ "cafeId": "cafe_demo_001",
+ "categories": [
+ {
+ "Id": "cat_demo_drinks",
+ "Name": "\u0646\u0648\u0634\u06CC\u062F\u0646\u06CC \u06AF\u0631\u0645",
+ "NameEn": "Hot drinks",
+ "NameAr": "\u0645\u0634\u0631\u0648\u0628\u0627\u062A \u0633\u0627\u062E\u0646\u0629",
+ "SortOrder": 1,
+ "Icon": null,
+ "IconPresetId": "drinks-hot",
+ "IconStyle": "flat"
+ },
+ {
+ "Id": "cat_demo_cold",
+ "Name": "\u0646\u0648\u0634\u06CC\u062F\u0646\u06CC \u0633\u0631\u062F",
+ "NameEn": "Cold drinks",
+ "NameAr": "\u0645\u0634\u0631\u0648\u0628\u0627\u062A \u0628\u0627\u0631\u062F\u0629",
+ "SortOrder": 2,
+ "Icon": null,
+ "IconPresetId": "drinks-cold",
+ "IconStyle": "flat"
+ },
+ {
+ "Id": "cat_demo_breakfast",
+ "Name": "\u0635\u0628\u062D\u0627\u0646\u0647",
+ "NameEn": "Breakfast",
+ "NameAr": "\u0641\u0637\u0648\u0631",
+ "SortOrder": 3,
+ "Icon": null,
+ "IconPresetId": "breakfast",
+ "IconStyle": "flat"
+ },
+ {
+ "Id": "cat_demo_food",
+ "Name": "\u063A\u0630\u0627 \u0648 \u067E\u06CC\u0634\u200C\u063A\u0630\u0627",
+ "NameEn": "Food \u0026 snacks",
+ "NameAr": "\u0637\u0639\u0627\u0645",
+ "SortOrder": 4,
+ "Icon": null,
+ "IconPresetId": "food-mains",
+ "IconStyle": "flat"
+ },
+ {
+ "Id": "cat_demo_pasta",
+ "Name": "\u067E\u0627\u0633\u062A\u0627 \u0648 \u067E\u06CC\u062A\u0632\u0627",
+ "NameEn": "Pasta \u0026 pizza",
+ "NameAr": "\u0645\u0639\u0643\u0631\u0648\u0646\u0629 \u0648\u0628\u064A\u062A\u0632\u0627",
+ "SortOrder": 5,
+ "Icon": null,
+ "IconPresetId": "pasta-pizza",
+ "IconStyle": "flat"
+ },
+ {
+ "Id": "cat_demo_dessert",
+ "Name": "\u062F\u0633\u0631",
+ "NameEn": "Desserts",
+ "NameAr": "\u062D\u0644\u0648\u064A\u0627\u062A",
+ "SortOrder": 6,
+ "Icon": null,
+ "IconPresetId": "dessert",
+ "IconStyle": "flat"
+ }
+ ],
+ "items": [
+ {
+ "Id": "item_demo_espresso",
+ "CategoryId": "cat_demo_drinks",
+ "Name": "\u0627\u0633\u067E\u0631\u0633\u0648",
+ "NameEn": "Espresso",
+ "NameAr": "\u0625\u0633\u0628\u0631\u064A\u0633\u0648",
+ "Description": "\u062F\u0648\u0628\u0644 \u06CC\u0627 \u0633\u06CC\u0646\u06AF\u0644",
+ "priceToman": 65000,
+ "DiscountPercent": 0,
+ "Food101Class": "espresso",
+ "imageUrl": "https://images.unsplash.com/photo-1509042239860-f550ce710b93?w=600\u0026auto=format\u0026fit=crop"
+ },
+ {
+ "Id": "item_demo_americano",
+ "CategoryId": "cat_demo_drinks",
+ "Name": "\u0622\u0645\u0631\u06CC\u06A9\u0627\u0646\u0648",
+ "NameEn": "Americano",
+ "NameAr": "\u0623\u0645\u0631\u064A\u0643\u0627\u0646\u0648",
+ "Description": null,
+ "priceToman": 75000,
+ "DiscountPercent": 0,
+ "Food101Class": "cappuccino",
+ "imageUrl": "https://images.unsplash.com/photo-1572442388796-11668a67e3d0?w=600"
+ },
+ {
+ "Id": "item_demo_latte",
+ "CategoryId": "cat_demo_drinks",
+ "Name": "\u0644\u0627\u062A\u0647",
+ "NameEn": "Latte",
+ "NameAr": "\u0644\u0627\u062A\u064A\u0647",
+ "Description": "\u0634\u06CC\u0631 \u0628\u062E\u0627\u0631 \u06AF\u0631\u0641\u062A\u0647",
+ "priceToman": 120000,
+ "DiscountPercent": 0,
+ "Food101Class": "latte",
+ "imageUrl": "https://images.unsplash.com/photo-1461023058943-07fcbe16d735?w=600"
+ },
+ {
+ "Id": "item_demo_cappuccino",
+ "CategoryId": "cat_demo_drinks",
+ "Name": "\u06A9\u0627\u067E\u0648\u0686\u06CC\u0646\u0648",
+ "NameEn": "Cappuccino",
+ "NameAr": "\u0643\u0627\u0628\u062A\u0634\u064A\u0646\u0648",
+ "Description": null,
+ "priceToman": 110000,
+ "DiscountPercent": 10,
+ "Food101Class": "cappuccino",
+ "imageUrl": "https://images.unsplash.com/photo-1572442388796-11668a67e3d0?w=600"
+ },
+ {
+ "Id": "item_demo_mocha",
+ "CategoryId": "cat_demo_drinks",
+ "Name": "\u0645\u0648\u06A9\u0627",
+ "NameEn": "Mocha",
+ "NameAr": "\u0645\u0648\u0643\u0627",
+ "Description": "\u0634\u06A9\u0644\u0627\u062A \u0648 \u0642\u0647\u0648\u0647",
+ "priceToman": 135000,
+ "DiscountPercent": 0,
+ "Food101Class": "mocha",
+ "imageUrl": "https://images.unsplash.com/photo-1577887233537-a81b387b125e?w=600"
+ },
+ {
+ "Id": "item_demo_tea",
+ "CategoryId": "cat_demo_drinks",
+ "Name": "\u0686\u0627\u06CC \u0645\u0627\u0633\u0627\u0644\u0627",
+ "NameEn": "Masala tea",
+ "NameAr": "\u0634\u0627\u064A \u0645\u0627\u0633\u0627\u0644\u0627",
+ "Description": null,
+ "priceToman": 85000,
+ "DiscountPercent": 0,
+ "Food101Class": "miso_soup",
+ "imageUrl": "https://images.unsplash.com/photo-1556679343-c7306c1976bc?w=600"
+ },
+ {
+ "Id": "item_demo_iced_latte",
+ "CategoryId": "cat_demo_cold",
+ "Name": "\u0622\u06CC\u0633 \u0644\u0627\u062A\u0647",
+ "NameEn": "Iced latte",
+ "NameAr": "\u0622\u064A\u0633 \u0644\u0627\u062A\u064A\u0647",
+ "Description": null,
+ "priceToman": 130000,
+ "DiscountPercent": 0,
+ "Food101Class": "iced_coffee",
+ "imageUrl": "https://images.unsplash.com/photo-1517487881594-2787aeee8f58?w=600\u0026auto=format\u0026fit=crop"
+ },
+ {
+ "Id": "item_demo_cold_brew",
+ "CategoryId": "cat_demo_cold",
+ "Name": "\u06A9\u0648\u0644\u062F \u0628\u0631\u0648",
+ "NameEn": "Cold brew",
+ "NameAr": "\u0643\u0648\u0644\u062F \u0628\u0631\u0648",
+ "Description": null,
+ "priceToman": 140000,
+ "DiscountPercent": 0,
+ "Food101Class": "iced_coffee",
+ "imageUrl": "https://images.unsplash.com/photo-1517487881594-2787aeee8f58?w=600\u0026auto=format\u0026fit=crop"
+ },
+ {
+ "Id": "item_demo_lemonade",
+ "CategoryId": "cat_demo_cold",
+ "Name": "\u0644\u06CC\u0645\u0648\u0646\u0627\u062F",
+ "NameEn": "Lemonade",
+ "NameAr": "\u0644\u064A\u0645\u0648\u0646\u0627\u062F\u0629",
+ "Description": "\u062A\u0627\u0632\u0647",
+ "priceToman": 95000,
+ "DiscountPercent": 0,
+ "Food101Class": "lemonade",
+ "imageUrl": "https://images.unsplash.com/photo-1523672990561-64c16245f769?w=600"
+ },
+ {
+ "Id": "item_demo_smoothie",
+ "CategoryId": "cat_demo_cold",
+ "Name": "\u0627\u0633\u0645\u0648\u062A\u06CC \u062A\u0648\u062A",
+ "NameEn": "Berry smoothie",
+ "NameAr": "\u0633\u0645\u0648\u0630\u064A",
+ "Description": null,
+ "priceToman": 150000,
+ "DiscountPercent": 15,
+ "Food101Class": "smoothie",
+ "imageUrl": "https://images.unsplash.com/photo-1505252585463-0433371f7f6b?w=600"
+ },
+ {
+ "Id": "item_demo_croissant",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u06A9\u0631\u0648\u0633\u0627\u0646",
+ "NameEn": "Croissant",
+ "NameAr": "\u0643\u0631\u0648\u0627\u0633\u0627\u0646",
+ "Description": "\u06A9\u0631\u0647\u200C\u0627\u06CC",
+ "priceToman": 75000,
+ "DiscountPercent": 0,
+ "Food101Class": "croque_madame",
+ "imageUrl": "https://images.unsplash.com/photo-1555507036342-9231d37c10f3?w=600"
+ },
+ {
+ "Id": "item_demo_omelette",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u0627\u0645\u0644\u062A",
+ "NameEn": "Omelette",
+ "NameAr": "\u0623\u0648\u0645\u0644\u064A\u062A",
+ "Description": "\u0646\u0627\u0646 \u0633\u0646\u06AF\u06A9",
+ "priceToman": 145000,
+ "DiscountPercent": 0,
+ "Food101Class": "omelette",
+ "imageUrl": "https://images.unsplash.com/photo-1525351484343-752d43d363f1?w=600"
+ },
+ {
+ "Id": "item_demo_avocado",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u062A\u0648\u0633\u062A \u0622\u0648\u0648\u06A9\u0627\u062F\u0648",
+ "NameEn": "Avocado toast",
+ "NameAr": "\u062A\u0648\u0633\u062A \u0623\u0641\u0648\u0643\u0627\u062F\u0648",
+ "Description": null,
+ "priceToman": 185000,
+ "DiscountPercent": 0,
+ "Food101Class": "avocado_toast",
+ "imageUrl": "https://images.unsplash.com/photo-1541519221064-49632fb3380e?w=600"
+ },
+ {
+ "Id": "item_demo_pancakes",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u067E\u0646\u06A9\u06CC\u06A9",
+ "NameEn": "Pancakes",
+ "NameAr": "\u0641\u0637\u0627\u0626\u0631",
+ "Description": "\u0639\u0633\u0644 \u0648 \u06A9\u0631\u0647",
+ "priceToman": 165000,
+ "DiscountPercent": 0,
+ "Food101Class": "pancakes",
+ "imageUrl": "https://images.unsplash.com/photo-1567620905732-2d1ec7ab7440?w=600"
+ },
+ {
+ "Id": "item_demo_waffles",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u0648\u0627\u0641\u0644",
+ "NameEn": "Waffles",
+ "NameAr": "\u0648\u0627\u0641\u0644",
+ "Description": null,
+ "priceToman": 175000,
+ "DiscountPercent": 0,
+ "Food101Class": "waffles",
+ "imageUrl": "https://images.unsplash.com/photo-1567818735240-7acbb4b7e34c?w=600"
+ },
+ {
+ "Id": "item_demo_french_toast",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u0641\u0631\u0646\u0686 \u062A\u0633\u062A",
+ "NameEn": "French toast",
+ "NameAr": "\u062A\u0648\u0633\u062A \u0641\u0631\u0646\u0633\u064A",
+ "Description": null,
+ "priceToman": 155000,
+ "DiscountPercent": 0,
+ "Food101Class": "french_toast",
+ "imageUrl": "https://images.unsplash.com/photo-1484723091739-30a329e1f0c4?w=600"
+ },
+ {
+ "Id": "item_demo_eggs_benedict",
+ "CategoryId": "cat_demo_breakfast",
+ "Name": "\u0627\u06AF \u0628\u0646\u062F\u06CC\u06A9\u062A",
+ "NameEn": "Eggs Benedict",
+ "NameAr": "\u0628\u064A\u0636 \u0628\u0646\u062F\u064A\u0643\u062A",
+ "Description": null,
+ "priceToman": 195000,
+ "DiscountPercent": 0,
+ "Food101Class": "eggs_benedict",
+ "imageUrl": "https://images.unsplash.com/photo-1608039819502-3d5a2a2e0b0e?w=600"
+ },
+ {
+ "Id": "item_demo_sandwich",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0633\u0627\u0646\u062F\u0648\u06CC\u0686 \u0645\u0631\u063A",
+ "NameEn": "Chicken sandwich",
+ "NameAr": "\u0633\u0627\u0646\u062F\u0648\u064A\u062A\u0634 \u062F\u062C\u0627\u062C",
+ "Description": null,
+ "priceToman": 195000,
+ "DiscountPercent": 0,
+ "Food101Class": "club_sandwich",
+ "imageUrl": "https://images.unsplash.com/photo-1528735602780-2552fd46c7af?w=600"
+ },
+ {
+ "Id": "item_demo_salad",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0633\u0627\u0644\u0627\u062F \u0633\u0632\u0627\u0631",
+ "NameEn": "Caesar salad",
+ "NameAr": "\u0633\u0644\u0637\u0629 \u0633\u064A\u0632\u0631",
+ "Description": null,
+ "priceToman": 175000,
+ "DiscountPercent": 0,
+ "Food101Class": "caesar_salad",
+ "imageUrl": "https://images.unsplash.com/photo-1546793665-c74683f339c1?w=600"
+ },
+ {
+ "Id": "item_demo_greek_salad",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0633\u0627\u0644\u0627\u062F \u06CC\u0648\u0646\u0627\u0646\u06CC",
+ "NameEn": "Greek salad",
+ "NameAr": "\u0633\u0644\u0637\u0629 \u064A\u0648\u0646\u0627\u0646\u064A\u0629",
+ "Description": null,
+ "priceToman": 168000,
+ "DiscountPercent": 0,
+ "Food101Class": "greek_salad",
+ "imageUrl": "https://images.unsplash.com/photo-1540189549336-e6e99c3679fe?w=600"
+ },
+ {
+ "Id": "item_demo_burger",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0647\u0645\u0628\u0631\u06AF\u0631",
+ "NameEn": "Burger",
+ "NameAr": "\u0628\u0631\u062C\u0631",
+ "Description": "\u06F1\u06F5\u06F0 \u06AF\u0631\u0645 \u06AF\u0648\u0634\u062A",
+ "priceToman": 245000,
+ "DiscountPercent": 0,
+ "Food101Class": "hamburger",
+ "imageUrl": "https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=600"
+ },
+ {
+ "Id": "item_demo_steak",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0627\u0633\u062A\u06CC\u06A9",
+ "NameEn": "Steak",
+ "NameAr": "\u0633\u062A\u064A\u0643",
+ "Description": "medium",
+ "priceToman": 385000,
+ "DiscountPercent": 0,
+ "Food101Class": "steak",
+ "imageUrl": "https://images.unsplash.com/photo-1600891963295-d66a269b9202?w=600"
+ },
+ {
+ "Id": "item_demo_salmon",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0633\u0627\u0644\u0645\u0648\u0646 \u06AF\u0631\u06CC\u0644",
+ "NameEn": "Grilled salmon",
+ "NameAr": "\u0633\u0644\u0645\u0648\u0646 \u0645\u0634\u0648\u064A",
+ "Description": null,
+ "priceToman": 320000,
+ "DiscountPercent": 0,
+ "Food101Class": "grilled_salmon",
+ "imageUrl": "https://images.unsplash.com/photo-1467003909585-2f8a72700288?w=600"
+ },
+ {
+ "Id": "item_demo_tacos",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u062A\u0627\u06A9\u0648",
+ "NameEn": "Tacos",
+ "NameAr": "\u062A\u0627\u0643\u0648",
+ "Description": "\u0633\u0647 \u0639\u062F\u062F",
+ "priceToman": 210000,
+ "DiscountPercent": 0,
+ "Food101Class": "tacos",
+ "imageUrl": "https://images.unsplash.com/photo-1565299585323-38174c4aab1e?w=600"
+ },
+ {
+ "Id": "item_demo_shawarma",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0634\u0627\u0648\u0631\u0645\u0627",
+ "NameEn": "Shawarma",
+ "NameAr": "\u0634\u0627\u0648\u0631\u0645\u0627",
+ "Description": null,
+ "priceToman": 185000,
+ "DiscountPercent": 0,
+ "Food101Class": "shawarma",
+ "imageUrl": "https://images.unsplash.com/photo-1529006557810-274adbcb39d8?w=600"
+ },
+ {
+ "Id": "item_demo_falafel",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0641\u0644\u0627\u0641\u0644",
+ "NameEn": "Falafel",
+ "NameAr": "\u0641\u0644\u0627\u0641\u0644",
+ "Description": "\u06F6 \u0639\u062F\u062F",
+ "priceToman": 125000,
+ "DiscountPercent": 0,
+ "Food101Class": "falafel",
+ "imageUrl": "https://images.unsplash.com/photo-1601050690597-df5748fb5cee?w=600"
+ },
+ {
+ "Id": "item_demo_hummus",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u062D\u0645\u0635",
+ "NameEn": "Hummus",
+ "NameAr": "\u062D\u0645\u0635",
+ "Description": "\u0646\u0627\u0646 \u067E\u06CC\u062A\u0627",
+ "priceToman": 95000,
+ "DiscountPercent": 0,
+ "Food101Class": "hummus",
+ "imageUrl": "https://images.unsplash.com/photo-1626208082043-e6319abfeec2?w=600"
+ },
+ {
+ "Id": "item_demo_fries",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0633\u06CC\u0628\u200C\u0632\u0645\u06CC\u0646\u06CC \u0633\u0631\u062E\u200C\u06A9\u0631\u062F\u0647",
+ "NameEn": "French fries",
+ "NameAr": "\u0628\u0637\u0627\u0637\u0633 \u0645\u0642\u0644\u064A\u0629",
+ "Description": null,
+ "priceToman": 85000,
+ "DiscountPercent": 0,
+ "Food101Class": "french_fries",
+ "imageUrl": "https://images.unsplash.com/photo-1573080496219-76b9e6909700?w=600"
+ },
+ {
+ "Id": "item_demo_spring_rolls",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0627\u0633\u067E\u0631\u06CC\u0646\u06AF \u0631\u0648\u0644",
+ "NameEn": "Spring rolls",
+ "NameAr": "\u0633\u0628\u0631\u064A\u0646\u063A \u0631\u0648\u0644",
+ "Description": null,
+ "priceToman": 115000,
+ "DiscountPercent": 0,
+ "Food101Class": "spring_rolls",
+ "imageUrl": "https://images.unsplash.com/photo-1526318896985-4d29c0903299?w=600"
+ },
+ {
+ "Id": "item_demo_ramen",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0631\u0627\u0645\u0646",
+ "NameEn": "Ramen",
+ "NameAr": "\u0631\u0627\u0645\u0646",
+ "Description": null,
+ "priceToman": 235000,
+ "DiscountPercent": 0,
+ "Food101Class": "ramen",
+ "imageUrl": "https://images.unsplash.com/photo-1569718212165-3a8278d5f624?w=600"
+ },
+ {
+ "Id": "item_demo_pho",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0641\u0648",
+ "NameEn": "Pho",
+ "NameAr": "\u0641\u0648",
+ "Description": null,
+ "priceToman": 225000,
+ "DiscountPercent": 0,
+ "Food101Class": "pho",
+ "imageUrl": "https://images.unsplash.com/photo-1591814468924-caf36d123dd6?w=600"
+ },
+ {
+ "Id": "item_demo_sushi",
+ "CategoryId": "cat_demo_food",
+ "Name": "\u0633\u0648\u0634\u06CC",
+ "NameEn": "Sushi",
+ "NameAr": "\u0633\u0648\u0634\u064A",
+ "Description": "\u06F8 \u062A\u06A9\u0647",
+ "priceToman": 290000,
+ "DiscountPercent": 0,
+ "Food101Class": "sushi",
+ "imageUrl": "https://images.unsplash.com/photo-1579584425555-c3ce17fd4351?w=600"
+ },
+ {
+ "Id": "item_demo_pasta",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u067E\u0627\u0633\u062A\u0627 \u0622\u0644\u0641\u0631\u062F\u0648",
+ "NameEn": "Alfredo pasta",
+ "NameAr": "\u0628\u0627\u0633\u062A\u0627",
+ "Description": null,
+ "priceToman": 220000,
+ "DiscountPercent": 0,
+ "Food101Class": "pasta_carbonara",
+ "imageUrl": "https://images.unsplash.com/photo-1621996346565-e3dbc646d9a9?w=600"
+ },
+ {
+ "Id": "item_demo_carbonara",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u06A9\u0627\u0631\u0628\u0648\u0646\u0627\u0631\u0627",
+ "NameEn": "Carbonara",
+ "NameAr": "\u0643\u0627\u0631\u0628\u0648\u0646\u0627\u0631\u0627",
+ "Description": null,
+ "priceToman": 228000,
+ "DiscountPercent": 0,
+ "Food101Class": "spaghetti_carbonara",
+ "imageUrl": "https://images.unsplash.com/photo-1612874741227-866d1aeeecd1?w=600"
+ },
+ {
+ "Id": "item_demo_bolognese",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u0628\u0648\u0644\u0648\u0646\u0632",
+ "NameEn": "Bolognese",
+ "NameAr": "\u0628\u0648\u0644\u0648\u0646\u064A\u0632",
+ "Description": null,
+ "priceToman": 215000,
+ "DiscountPercent": 0,
+ "Food101Class": "spaghetti_bolognese",
+ "imageUrl": "https://images.unsplash.com/photo-1622973536968-77544a8a4e0e?w=600"
+ },
+ {
+ "Id": "item_demo_lasagna",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u0644\u0627\u0632\u0627\u0646\u06CC\u0627",
+ "NameEn": "Lasagna",
+ "NameAr": "\u0644\u0627\u0632\u0627\u0646\u064A\u0627",
+ "Description": null,
+ "priceToman": 240000,
+ "DiscountPercent": 0,
+ "Food101Class": "lasagna",
+ "imageUrl": "https://images.unsplash.com/photo-1574894709920-11b28e7367e3?w=600"
+ },
+ {
+ "Id": "item_demo_gnocchi",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u0646\u06CC\u0648\u06A9\u06CC",
+ "NameEn": "Gnocchi",
+ "NameAr": "\u062C\u0646\u0648\u0643\u064A",
+ "Description": null,
+ "priceToman": 232000,
+ "DiscountPercent": 0,
+ "Food101Class": "gnocchi",
+ "imageUrl": "https://images.unsplash.com/photo-1551183053-bf33a48c970a?w=600"
+ },
+ {
+ "Id": "item_demo_pizza",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u067E\u06CC\u062A\u0632\u0627 \u0645\u0627\u0631\u06AF\u0627\u0631\u06CC\u062A\u0627",
+ "NameEn": "Margherita pizza",
+ "NameAr": "\u0628\u064A\u062A\u0632\u0627",
+ "Description": null,
+ "priceToman": 265000,
+ "DiscountPercent": 10,
+ "Food101Class": "pizza",
+ "imageUrl": "https://images.unsplash.com/photo-1513104890138-7c749659a591?w=600"
+ },
+ {
+ "Id": "item_demo_risotto",
+ "CategoryId": "cat_demo_pasta",
+ "Name": "\u0631\u06CC\u0632\u0648\u062A\u0648 \u0642\u0627\u0631\u0686",
+ "NameEn": "Mushroom risotto",
+ "NameAr": "\u0631\u064A\u0632\u0648\u062A\u0648",
+ "Description": null,
+ "priceToman": 238000,
+ "DiscountPercent": 0,
+ "Food101Class": "mushroom_risotto",
+ "imageUrl": "https://images.unsplash.com/photo-1476124362071-b9f2c96ef2db?w=600"
+ },
+ {
+ "Id": "item_demo_cake",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u06A9\u06CC\u06A9 \u0634\u06A9\u0644\u0627\u062A\u06CC",
+ "NameEn": "Chocolate cake",
+ "NameAr": "\u0643\u064A\u0643 \u0634\u0648\u0643\u0648\u0644\u0627\u062A\u0629",
+ "Description": "\u0628\u0631\u0634\u06CC",
+ "priceToman": 95000,
+ "DiscountPercent": 15,
+ "Food101Class": "chocolate_cake",
+ "imageUrl": "https://images.unsplash.com/photo-1578985545062-69928b1d9587?w=600"
+ },
+ {
+ "Id": "item_demo_cheesecake",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u0686\u06CC\u0632\u06A9\u06CC\u06A9",
+ "NameEn": "Cheesecake",
+ "NameAr": "\u062A\u0634\u064A\u0632 \u0643\u064A\u0643",
+ "Description": null,
+ "priceToman": 115000,
+ "DiscountPercent": 0,
+ "Food101Class": "cheesecake",
+ "imageUrl": "https://images.unsplash.com/photo-1524351199678-941a58cfcc36?w=600"
+ },
+ {
+ "Id": "item_demo_brownie",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u0628\u0631\u0627\u0648\u0646\u06CC",
+ "NameEn": "Brownie",
+ "NameAr": "\u0628\u0631\u0627\u0648\u0646\u064A",
+ "Description": "\u0628\u0633\u062A\u0646\u06CC \u0648\u0627\u0646\u06CC\u0644\u06CC",
+ "priceToman": 105000,
+ "DiscountPercent": 0,
+ "Food101Class": "brownie",
+ "imageUrl": "https://images.unsplash.com/photo-1606313564200-e75d5e30476e?w=600"
+ },
+ {
+ "Id": "item_demo_icecream",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u0628\u0633\u062A\u0646\u06CC",
+ "NameEn": "Ice cream",
+ "NameAr": "\u0622\u064A\u0633 \u0643\u0631\u064A\u0645",
+ "Description": "\u062F\u0648 \u0627\u0633\u06A9\u0648\u067E",
+ "priceToman": 88000,
+ "DiscountPercent": 0,
+ "Food101Class": "ice_cream",
+ "imageUrl": "https://images.unsplash.com/photo-1563805042-7684c019e1cb?w=600"
+ },
+ {
+ "Id": "item_demo_tiramisu",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u062A\u06CC\u0631\u0627\u0645\u06CC\u0633\u0648",
+ "NameEn": "Tiramisu",
+ "NameAr": "\u062A\u064A\u0631\u0627\u0645\u064A\u0633\u0648",
+ "Description": null,
+ "priceToman": 125000,
+ "DiscountPercent": 0,
+ "Food101Class": "tiramisu",
+ "imageUrl": "https://images.unsplash.com/photo-1571877227200-a0d98ea607e9?w=600"
+ },
+ {
+ "Id": "item_demo_donuts",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u062F\u0648\u0646\u0627\u062A",
+ "NameEn": "Donuts",
+ "NameAr": "\u062F\u0648\u0646\u0627\u062A",
+ "Description": null,
+ "priceToman": 78000,
+ "DiscountPercent": 0,
+ "Food101Class": "donuts",
+ "imageUrl": "https://images.unsplash.com/photo-1551024506-0bccd28d3071?w=600"
+ },
+ {
+ "Id": "item_demo_churros",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u0686\u0648\u0631\u0648\u0633",
+ "NameEn": "Churros",
+ "NameAr": "\u062A\u0634\u0648\u0631\u0648",
+ "Description": "\u0634\u06A9\u0644\u0627\u062A",
+ "priceToman": 92000,
+ "DiscountPercent": 0,
+ "Food101Class": "churros",
+ "imageUrl": "https://images.unsplash.com/photo-1627482299165-0883a056a48f?w=600"
+ },
+ {
+ "Id": "item_demo_baklava",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u0628\u0627\u0642\u0644\u0648\u0627",
+ "NameEn": "Baklava",
+ "NameAr": "\u0628\u0642\u0644\u0627\u0648\u0629",
+ "Description": null,
+ "priceToman": 98000,
+ "DiscountPercent": 0,
+ "Food101Class": "baklava",
+ "imageUrl": "https://images.unsplash.com/photo-1598110756419-84b30681f41f?w=600"
+ },
+ {
+ "Id": "item_demo_creme_brulee",
+ "CategoryId": "cat_demo_dessert",
+ "Name": "\u06A9\u0631\u0645 \u0628\u0631\u0648\u0644\u0647",
+ "NameEn": "Cr\u00E8me br\u00FBl\u00E9e",
+ "NameAr": "\u0643\u0631\u064A\u0645 \u0628\u0631\u0648\u0644\u064A\u0647",
+ "Description": null,
+ "priceToman": 118000,
+ "DiscountPercent": 0,
+ "Food101Class": "creme_brulee",
+ "imageUrl": "https://images.unsplash.com/photo-1470309864661-683be0ef7eaf?w=600"
+ }
+ ]
+}
diff --git a/data/menu-image-manifest.json b/data/menu-image-manifest.json
new file mode 100644
index 0000000..6919f42
--- /dev/null
+++ b/data/menu-image-manifest.json
@@ -0,0 +1,298 @@
+{
+ "version": 2,
+ "source": "Food-101 class mapping (Unsplash fallbacks until Kaggle JPEG import)",
+ "defaults": {
+ "drink": "https://images.unsplash.com/photo-1509042239860-f550ce710b93?w=600\u0026auto=format\u0026fit=crop",
+ "food": "https://images.unsplash.com/photo-1546793665-c74683f339c1?w=600"
+ },
+ "items": {
+ "item_demo_espresso": {
+ "food101Class": "espresso",
+ "imageUrl": "https://images.unsplash.com/photo-1509042239860-f550ce710b93?w=600\u0026auto=format\u0026fit=crop",
+ "nameEn": "Espresso",
+ "categoryId": "cat_demo_drinks"
+ },
+ "item_demo_americano": {
+ "food101Class": "cappuccino",
+ "imageUrl": "https://images.unsplash.com/photo-1572442388796-11668a67e3d0?w=600",
+ "nameEn": "Americano",
+ "categoryId": "cat_demo_drinks"
+ },
+ "item_demo_latte": {
+ "food101Class": "latte",
+ "imageUrl": "https://images.unsplash.com/photo-1461023058943-07fcbe16d735?w=600",
+ "nameEn": "Latte",
+ "categoryId": "cat_demo_drinks"
+ },
+ "item_demo_cappuccino": {
+ "food101Class": "cappuccino",
+ "imageUrl": "https://images.unsplash.com/photo-1572442388796-11668a67e3d0?w=600",
+ "nameEn": "Cappuccino",
+ "categoryId": "cat_demo_drinks"
+ },
+ "item_demo_mocha": {
+ "food101Class": "mocha",
+ "imageUrl": "https://images.unsplash.com/photo-1577887233537-a81b387b125e?w=600",
+ "nameEn": "Mocha",
+ "categoryId": "cat_demo_drinks"
+ },
+ "item_demo_tea": {
+ "food101Class": "miso_soup",
+ "imageUrl": "https://images.unsplash.com/photo-1556679343-c7306c1976bc?w=600",
+ "nameEn": "Masala tea",
+ "categoryId": "cat_demo_drinks"
+ },
+ "item_demo_iced_latte": {
+ "food101Class": "iced_coffee",
+ "imageUrl": "https://images.unsplash.com/photo-1517487881594-2787aeee8f58?w=600\u0026auto=format\u0026fit=crop",
+ "nameEn": "Iced latte",
+ "categoryId": "cat_demo_cold"
+ },
+ "item_demo_cold_brew": {
+ "food101Class": "iced_coffee",
+ "imageUrl": "https://images.unsplash.com/photo-1517487881594-2787aeee8f58?w=600\u0026auto=format\u0026fit=crop",
+ "nameEn": "Cold brew",
+ "categoryId": "cat_demo_cold"
+ },
+ "item_demo_lemonade": {
+ "food101Class": "lemonade",
+ "imageUrl": "https://images.unsplash.com/photo-1523672990561-64c16245f769?w=600",
+ "nameEn": "Lemonade",
+ "categoryId": "cat_demo_cold"
+ },
+ "item_demo_smoothie": {
+ "food101Class": "smoothie",
+ "imageUrl": "https://images.unsplash.com/photo-1505252585463-0433371f7f6b?w=600",
+ "nameEn": "Berry smoothie",
+ "categoryId": "cat_demo_cold"
+ },
+ "item_demo_croissant": {
+ "food101Class": "croque_madame",
+ "imageUrl": "https://images.unsplash.com/photo-1555507036342-9231d37c10f3?w=600",
+ "nameEn": "Croissant",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_omelette": {
+ "food101Class": "omelette",
+ "imageUrl": "https://images.unsplash.com/photo-1525351484343-752d43d363f1?w=600",
+ "nameEn": "Omelette",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_avocado": {
+ "food101Class": "avocado_toast",
+ "imageUrl": "https://images.unsplash.com/photo-1541519221064-49632fb3380e?w=600",
+ "nameEn": "Avocado toast",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_pancakes": {
+ "food101Class": "pancakes",
+ "imageUrl": "https://images.unsplash.com/photo-1567620905732-2d1ec7ab7440?w=600",
+ "nameEn": "Pancakes",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_waffles": {
+ "food101Class": "waffles",
+ "imageUrl": "https://images.unsplash.com/photo-1567818735240-7acbb4b7e34c?w=600",
+ "nameEn": "Waffles",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_french_toast": {
+ "food101Class": "french_toast",
+ "imageUrl": "https://images.unsplash.com/photo-1484723091739-30a329e1f0c4?w=600",
+ "nameEn": "French toast",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_eggs_benedict": {
+ "food101Class": "eggs_benedict",
+ "imageUrl": "https://images.unsplash.com/photo-1608039819502-3d5a2a2e0b0e?w=600",
+ "nameEn": "Eggs Benedict",
+ "categoryId": "cat_demo_breakfast"
+ },
+ "item_demo_sandwich": {
+ "food101Class": "club_sandwich",
+ "imageUrl": "https://images.unsplash.com/photo-1528735602780-2552fd46c7af?w=600",
+ "nameEn": "Chicken sandwich",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_salad": {
+ "food101Class": "caesar_salad",
+ "imageUrl": "https://images.unsplash.com/photo-1546793665-c74683f339c1?w=600",
+ "nameEn": "Caesar salad",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_greek_salad": {
+ "food101Class": "greek_salad",
+ "imageUrl": "https://images.unsplash.com/photo-1540189549336-e6e99c3679fe?w=600",
+ "nameEn": "Greek salad",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_burger": {
+ "food101Class": "hamburger",
+ "imageUrl": "https://images.unsplash.com/photo-1568901346375-23c9450c58cd?w=600",
+ "nameEn": "Burger",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_steak": {
+ "food101Class": "steak",
+ "imageUrl": "https://images.unsplash.com/photo-1600891963295-d66a269b9202?w=600",
+ "nameEn": "Steak",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_salmon": {
+ "food101Class": "grilled_salmon",
+ "imageUrl": "https://images.unsplash.com/photo-1467003909585-2f8a72700288?w=600",
+ "nameEn": "Grilled salmon",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_tacos": {
+ "food101Class": "tacos",
+ "imageUrl": "https://images.unsplash.com/photo-1565299585323-38174c4aab1e?w=600",
+ "nameEn": "Tacos",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_shawarma": {
+ "food101Class": "shawarma",
+ "imageUrl": "https://images.unsplash.com/photo-1529006557810-274adbcb39d8?w=600",
+ "nameEn": "Shawarma",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_falafel": {
+ "food101Class": "falafel",
+ "imageUrl": "https://images.unsplash.com/photo-1601050690597-df5748fb5cee?w=600",
+ "nameEn": "Falafel",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_hummus": {
+ "food101Class": "hummus",
+ "imageUrl": "https://images.unsplash.com/photo-1626208082043-e6319abfeec2?w=600",
+ "nameEn": "Hummus",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_fries": {
+ "food101Class": "french_fries",
+ "imageUrl": "https://images.unsplash.com/photo-1573080496219-76b9e6909700?w=600",
+ "nameEn": "French fries",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_spring_rolls": {
+ "food101Class": "spring_rolls",
+ "imageUrl": "https://images.unsplash.com/photo-1526318896985-4d29c0903299?w=600",
+ "nameEn": "Spring rolls",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_ramen": {
+ "food101Class": "ramen",
+ "imageUrl": "https://images.unsplash.com/photo-1569718212165-3a8278d5f624?w=600",
+ "nameEn": "Ramen",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_pho": {
+ "food101Class": "pho",
+ "imageUrl": "https://images.unsplash.com/photo-1591814468924-caf36d123dd6?w=600",
+ "nameEn": "Pho",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_sushi": {
+ "food101Class": "sushi",
+ "imageUrl": "https://images.unsplash.com/photo-1579584425555-c3ce17fd4351?w=600",
+ "nameEn": "Sushi",
+ "categoryId": "cat_demo_food"
+ },
+ "item_demo_pasta": {
+ "food101Class": "pasta_carbonara",
+ "imageUrl": "https://images.unsplash.com/photo-1621996346565-e3dbc646d9a9?w=600",
+ "nameEn": "Alfredo pasta",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_carbonara": {
+ "food101Class": "spaghetti_carbonara",
+ "imageUrl": "https://images.unsplash.com/photo-1612874741227-866d1aeeecd1?w=600",
+ "nameEn": "Carbonara",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_bolognese": {
+ "food101Class": "spaghetti_bolognese",
+ "imageUrl": "https://images.unsplash.com/photo-1622973536968-77544a8a4e0e?w=600",
+ "nameEn": "Bolognese",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_lasagna": {
+ "food101Class": "lasagna",
+ "imageUrl": "https://images.unsplash.com/photo-1574894709920-11b28e7367e3?w=600",
+ "nameEn": "Lasagna",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_gnocchi": {
+ "food101Class": "gnocchi",
+ "imageUrl": "https://images.unsplash.com/photo-1551183053-bf33a48c970a?w=600",
+ "nameEn": "Gnocchi",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_pizza": {
+ "food101Class": "pizza",
+ "imageUrl": "https://images.unsplash.com/photo-1513104890138-7c749659a591?w=600",
+ "nameEn": "Margherita pizza",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_risotto": {
+ "food101Class": "mushroom_risotto",
+ "imageUrl": "https://images.unsplash.com/photo-1476124362071-b9f2c96ef2db?w=600",
+ "nameEn": "Mushroom risotto",
+ "categoryId": "cat_demo_pasta"
+ },
+ "item_demo_cake": {
+ "food101Class": "chocolate_cake",
+ "imageUrl": "https://images.unsplash.com/photo-1578985545062-69928b1d9587?w=600",
+ "nameEn": "Chocolate cake",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_cheesecake": {
+ "food101Class": "cheesecake",
+ "imageUrl": "https://images.unsplash.com/photo-1524351199678-941a58cfcc36?w=600",
+ "nameEn": "Cheesecake",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_brownie": {
+ "food101Class": "brownie",
+ "imageUrl": "https://images.unsplash.com/photo-1606313564200-e75d5e30476e?w=600",
+ "nameEn": "Brownie",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_icecream": {
+ "food101Class": "ice_cream",
+ "imageUrl": "https://images.unsplash.com/photo-1563805042-7684c019e1cb?w=600",
+ "nameEn": "Ice cream",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_tiramisu": {
+ "food101Class": "tiramisu",
+ "imageUrl": "https://images.unsplash.com/photo-1571877227200-a0d98ea607e9?w=600",
+ "nameEn": "Tiramisu",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_donuts": {
+ "food101Class": "donuts",
+ "imageUrl": "https://images.unsplash.com/photo-1551024506-0bccd28d3071?w=600",
+ "nameEn": "Donuts",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_churros": {
+ "food101Class": "churros",
+ "imageUrl": "https://images.unsplash.com/photo-1627482299165-0883a056a48f?w=600",
+ "nameEn": "Churros",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_baklava": {
+ "food101Class": "baklava",
+ "imageUrl": "https://images.unsplash.com/photo-1598110756419-84b30681f41f?w=600",
+ "nameEn": "Baklava",
+ "categoryId": "cat_demo_dessert"
+ },
+ "item_demo_creme_brulee": {
+ "food101Class": "creme_brulee",
+ "imageUrl": "https://images.unsplash.com/photo-1470309864661-683be0ef7eaf?w=600",
+ "nameEn": "Cr\u00E8me br\u00FBl\u00E9e",
+ "categoryId": "cat_demo_dessert"
+ }
+ }
+}
diff --git a/src/Meezi.API/16 b/src/Meezi.API/16
new file mode 100644
index 0000000..2c477b6
Binary files /dev/null and b/src/Meezi.API/16 differ
diff --git a/src/Meezi.API/Configuration/DeliveryPlatformsOptions.cs b/src/Meezi.API/Configuration/DeliveryPlatformsOptions.cs
new file mode 100644
index 0000000..e2ba572
--- /dev/null
+++ b/src/Meezi.API/Configuration/DeliveryPlatformsOptions.cs
@@ -0,0 +1,34 @@
+namespace Meezi.API.Configuration;
+
+public class DeliveryPlatformsOptions
+{
+ public const string SectionName = "DeliveryPlatforms";
+
+ public SnappfoodPlatformOptions Snappfood { get; set; } = new();
+ public Tap30PlatformOptions Tap30 { get; set; } = new();
+ public DigikalaPlatformOptions Digikala { get; set; } = new();
+
+ public decimal DefaultSnappfoodCommissionPercent { get; set; } = 18m;
+ public decimal DefaultTap30CommissionPercent { get; set; } = 15m;
+ public decimal DefaultDigikalaCommissionPercent { get; set; } = 12m;
+}
+
+public class SnappfoodPlatformOptions
+{
+ public string WebhookSecret { get; set; } = "";
+ public string ApiKey { get; set; } = "";
+ public string ApiBaseUrl { get; set; } = "";
+}
+
+public class Tap30PlatformOptions
+{
+ public string WebhookSecret { get; set; } = "";
+ public string ApiKey { get; set; } = "";
+ public string ApiBaseUrl { get; set; } = "";
+}
+
+public class DigikalaPlatformOptions
+{
+ public string WebhookSecret { get; set; } = "";
+ public bool Enabled { get; set; }
+}
diff --git a/src/Meezi.API/Configuration/MenuAi3dOptions.cs b/src/Meezi.API/Configuration/MenuAi3dOptions.cs
new file mode 100644
index 0000000..853c0b4
--- /dev/null
+++ b/src/Meezi.API/Configuration/MenuAi3dOptions.cs
@@ -0,0 +1,14 @@
+namespace Meezi.API.Configuration;
+
+public class MenuAi3dOptions
+{
+ public const string SectionName = "Ai3d";
+
+ public string ApiKey { get; set; } = "";
+ public string BaseUrl { get; set; } = "https://api.meshy.ai";
+ public string ImageTo3dPath { get; set; } = "/openapi/v1/image-to-3d";
+ public int PollIntervalSeconds { get; set; } = 4;
+ public int PollTimeoutSeconds { get; set; } = 300;
+ /// When true and ApiKey is empty, returns a minimal GLB (development only).
+ public bool AllowDevStub { get; set; }
+}
diff --git a/src/Meezi.API/Controllers/.gitkeep b/src/Meezi.API/Controllers/.gitkeep
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/Meezi.API/Controllers/.gitkeep
@@ -0,0 +1 @@
+
diff --git a/src/Meezi.API/Controllers/AuthController.cs b/src/Meezi.API/Controllers/AuthController.cs
new file mode 100644
index 0000000..72a16a8
--- /dev/null
+++ b/src/Meezi.API/Controllers/AuthController.cs
@@ -0,0 +1,125 @@
+using FluentValidation;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.RateLimiting;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Meezi.API.Models.Auth;
+using Meezi.API.Services;
+using Meezi.Core.Constants;
+using Meezi.Shared;
+
+namespace Meezi.API.Controllers;
+
+[ApiController]
+[Route("api/auth")]
+public class AuthController : ControllerBase
+{
+ private readonly IAuthService _authService;
+ private readonly IValidator _sendOtpValidator;
+ private readonly IValidator _verifyOtpValidator;
+ private readonly IValidator _refreshValidator;
+
+ public AuthController(
+ IAuthService authService,
+ IValidator sendOtpValidator,
+ IValidator verifyOtpValidator,
+ IValidator refreshValidator)
+ {
+ _authService = authService;
+ _sendOtpValidator = sendOtpValidator;
+ _verifyOtpValidator = verifyOtpValidator;
+ _refreshValidator = refreshValidator;
+ }
+
+ [HttpPost("send-otp")]
+ [EnableRateLimiting("auth-otp")]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task SendOtp([FromBody] SendOtpRequest request, CancellationToken cancellationToken)
+ {
+ var validation = await _sendOtpValidator.ValidateAsync(request, cancellationToken);
+ if (!validation.IsValid)
+ return BadRequest(ValidationError(validation));
+
+ var (success, data, code, message) = await _authService.SendOtpAsync(request, cancellationToken);
+ if (!success)
+ return ErrorResult(code!, message!);
+
+ return Ok(new ApiResponse(true, data));
+ }
+
+ [HttpPost("verify-otp")]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task VerifyOtp([FromBody] VerifyOtpRequest request, CancellationToken cancellationToken)
+ {
+ var validation = await _verifyOtpValidator.ValidateAsync(request, cancellationToken);
+ if (!validation.IsValid)
+ return BadRequest(ValidationError(validation));
+
+ var (success, data, code, message) = await _authService.VerifyOtpAsync(request, cancellationToken);
+ if (!success)
+ return ErrorResult(code!, message!);
+
+ return Ok(new ApiResponse(true, data));
+ }
+
+ [HttpPost("refresh")]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task Refresh([FromBody] RefreshTokenRequest request, CancellationToken cancellationToken)
+ {
+ var validation = await _refreshValidator.ValidateAsync(request, cancellationToken);
+ if (!validation.IsValid)
+ return BadRequest(ValidationError(validation));
+
+ var (success, data, code, message) = await _authService.RefreshAsync(request, cancellationToken);
+ if (!success)
+ return ErrorResult(code!, message!);
+
+ return Ok(new ApiResponse(true, data));
+ }
+
+ [HttpGet("me")]
+ [Authorize]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public IActionResult GetMe()
+ {
+ var userId = User.FindFirstValue(JwtRegisteredClaimNames.Sub)
+ ?? User.FindFirstValue(ClaimTypes.NameIdentifier)
+ ?? string.Empty;
+
+ var expClaim = User.FindFirstValue(JwtRegisteredClaimNames.Exp);
+ var expiresAt = expClaim != null && long.TryParse(expClaim, out var exp)
+ ? DateTimeOffset.FromUnixTimeSeconds(exp).UtcDateTime
+ : DateTime.UtcNow;
+
+ var data = new AuthTokenResponse(
+ AccessToken: string.Empty,
+ RefreshToken: string.Empty,
+ ExpiresAt: expiresAt,
+ UserId: userId,
+ CafeId: User.FindFirstValue(MeeziClaimTypes.CafeId) ?? string.Empty,
+ Role: User.FindFirstValue(MeeziClaimTypes.Role) ?? string.Empty,
+ PlanTier: User.FindFirstValue(MeeziClaimTypes.PlanTier) ?? string.Empty,
+ Language: User.FindFirstValue(MeeziClaimTypes.Language) ?? string.Empty,
+ Actor: User.FindFirstValue(MeeziClaimTypes.Actor) ?? MeeziActorKinds.Merchant,
+ BranchId: User.FindFirstValue(MeeziClaimTypes.BranchId));
+
+ return Ok(new ApiResponse(true, data));
+ }
+
+ private static ApiResponse