ef15fd6247
Full backend implementation: - Multi-tenant cafe/restaurant management (menus, orders, tables, staff) - POS order flow with ZarinPal and Snappfood payment integration - OTP authentication via Kavenegar SMS - QR digital menu with public discover/finder endpoints - Customer loyalty, coupons, CRM - PostgreSQL via EF Core, Redis for caching/sessions - Background jobs, webhook handlers - Full migration history Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
68 lines
2.4 KiB
C#
68 lines
2.4 KiB
C#
using Meezi.Core.Entities;
|
|
|
|
namespace Meezi.Infrastructure.Data;
|
|
|
|
public enum MenuItemVisualKind
|
|
{
|
|
Food,
|
|
Drink
|
|
}
|
|
|
|
public static class MenuItemImageDefaults
|
|
{
|
|
private static readonly HashSet<string> DrinkCategoryIds = new(StringComparer.Ordinal)
|
|
{
|
|
"cat_demo_drinks",
|
|
"cat_demo_cold"
|
|
};
|
|
|
|
private static readonly string[] DrinkCategoryHints =
|
|
[
|
|
"drink", "cold", "coffee", "tea", "juice", "smoothie", "beverage", "bar",
|
|
"نوشیدنی", "سرد", "گرم", "قهوه", "چای", "آبمیوه", "اسموتی", "مشروب", "بار"
|
|
];
|
|
|
|
public static MenuItemVisualKind InferKind(string categoryId, string? categoryName = null)
|
|
{
|
|
if (DrinkCategoryIds.Contains(categoryId))
|
|
return MenuItemVisualKind.Drink;
|
|
|
|
var haystack = $"{categoryId} {categoryName}".ToLowerInvariant();
|
|
if (DrinkCategoryHints.Any(h => haystack.Contains(h, StringComparison.Ordinal)))
|
|
return MenuItemVisualKind.Drink;
|
|
|
|
return MenuItemVisualKind.Food;
|
|
}
|
|
|
|
public static string GetDefaultImageUrl(MenuItemVisualKind kind)
|
|
=> kind == MenuItemVisualKind.Drink
|
|
? MenuImageManifest.GetDefaultDrinkImageUrl()
|
|
: MenuImageManifest.GetDefaultFoodImageUrl();
|
|
|
|
/// <summary>Remote https URL suitable for <img src> (excludes missing local uploads).</summary>
|
|
public static bool IsUsableImageUrl(string? imageUrl) =>
|
|
!string.IsNullOrWhiteSpace(imageUrl)
|
|
&& (imageUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
|
|
|| imageUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase));
|
|
|
|
public static bool NeedsImageRepair(string? imageUrl) => !IsUsableImageUrl(imageUrl);
|
|
|
|
public static string ResolveImageUrl(string itemId, string categoryId, string? categoryName)
|
|
{
|
|
var localOverride = MenuImageManifest.GetLocalImageOverride(itemId);
|
|
if (!string.IsNullOrWhiteSpace(localOverride))
|
|
return localOverride;
|
|
|
|
var catalog = DemoMenuCatalog.Items.FirstOrDefault(i => i.Id == itemId);
|
|
if (catalog is not null)
|
|
return DemoMenuCatalog.ResolveItemImageUrl(catalog);
|
|
|
|
return GetDefaultImageUrl(InferKind(categoryId, categoryName));
|
|
}
|
|
|
|
public static string ResolveDisplayImageUrl(MenuItem item) =>
|
|
IsUsableImageUrl(item.ImageUrl)
|
|
? item.ImageUrl!
|
|
: ResolveImageUrl(item.Id, item.CategoryId, item.Category?.Name);
|
|
}
|