|
|
|
|
@@ -47,6 +47,68 @@ public class PosDataService
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// EN: Robust list deserialization — handles plain arrays, PagedResult wrappers, and ApiResponse envelopes.
|
|
|
|
|
/// VI: Deserialize list linh hoạt — xử lý array thuần, PagedResult wrapper, và ApiResponse envelope.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private async Task<List<T>> GetListFromApiAsync<T>(string url)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var resp = await _http.GetAsync(url);
|
|
|
|
|
if (!resp.IsSuccessStatusCode) return new();
|
|
|
|
|
var json = await resp.Content.ReadAsStringAsync();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(json)) return new();
|
|
|
|
|
|
|
|
|
|
using var doc = JsonDocument.Parse(json);
|
|
|
|
|
var root = doc.RootElement;
|
|
|
|
|
|
|
|
|
|
// Case 1: plain array [...]
|
|
|
|
|
if (root.ValueKind == JsonValueKind.Array)
|
|
|
|
|
return JsonSerializer.Deserialize<List<T>>(json, _jsonOptions) ?? new();
|
|
|
|
|
|
|
|
|
|
// Case 2: { "items": [...] } (PagedResult)
|
|
|
|
|
if (root.TryGetProperty("items", out var items) && items.ValueKind == JsonValueKind.Array)
|
|
|
|
|
return JsonSerializer.Deserialize<List<T>>(items.GetRawText(), _jsonOptions) ?? new();
|
|
|
|
|
|
|
|
|
|
// Case 3: { "data": { "items": [...] } } (ApiResponse<PagedResult>)
|
|
|
|
|
// Case 4: { "data": [...] } (ApiResponse<List>)
|
|
|
|
|
if (root.TryGetProperty("data", out var data))
|
|
|
|
|
{
|
|
|
|
|
if (data.ValueKind == JsonValueKind.Object && data.TryGetProperty("items", out var dataItems) && dataItems.ValueKind == JsonValueKind.Array)
|
|
|
|
|
return JsonSerializer.Deserialize<List<T>>(dataItems.GetRawText(), _jsonOptions) ?? new();
|
|
|
|
|
if (data.ValueKind == JsonValueKind.Array)
|
|
|
|
|
return JsonSerializer.Deserialize<List<T>>(data.GetRawText(), _jsonOptions) ?? new();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// EN: Robust single-object deserialization — handles plain objects and ApiResponse envelopes.
|
|
|
|
|
/// VI: Deserialize đối tượng đơn linh hoạt — xử lý object thuần và ApiResponse envelope.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private async Task<T?> GetObjectFromApiAsync<T>(string url) where T : class
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var resp = await _http.GetAsync(url);
|
|
|
|
|
if (!resp.IsSuccessStatusCode) return null;
|
|
|
|
|
var json = await resp.Content.ReadAsStringAsync();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(json)) return null;
|
|
|
|
|
|
|
|
|
|
using var doc = JsonDocument.Parse(json);
|
|
|
|
|
var root = doc.RootElement;
|
|
|
|
|
|
|
|
|
|
// Case 1: { "data": {...} } (ApiResponse envelope)
|
|
|
|
|
if (root.TryGetProperty("data", out var data) && data.ValueKind == JsonValueKind.Object)
|
|
|
|
|
return JsonSerializer.Deserialize<T>(data.GetRawText(), _jsonOptions);
|
|
|
|
|
|
|
|
|
|
// Case 2: plain object
|
|
|
|
|
if (root.ValueKind == JsonValueKind.Object)
|
|
|
|
|
return JsonSerializer.Deserialize<T>(json, _jsonOptions);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public record ShopInfo(Guid Id, string Name, string Slug, string? Description, string? Phone, string? Email, string? Category, string? Status, Guid? MerchantId = null);
|
|
|
|
|
public record ProductInfo(Guid Id, string Name, decimal Price, string? Sku, string? Description, string? Category, int? DurationMinutes);
|
|
|
|
|
public record CategoryInfo(Guid Id, string Name, string? Description, int DisplayOrder);
|
|
|
|
|
@@ -55,25 +117,25 @@ public class PosDataService
|
|
|
|
|
public record StaffInfo(Guid Id, Guid? UserId, string? EmployeeCode, string? Phone, string? Email, DateTime? JoinedAt, DateTime? TerminatedAt, string? Role, string? Status, string? ShopName);
|
|
|
|
|
|
|
|
|
|
public async Task<List<ShopInfo>> GetShopsAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<ShopInfo>>("api/bff/shops", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<ShopInfo>("api/bff/shops");
|
|
|
|
|
|
|
|
|
|
public async Task<ShopInfo?> GetShopByIdAsync(Guid shopId)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<ShopInfo>($"api/bff/shops/{shopId}", _jsonOptions); }
|
|
|
|
|
=> await GetObjectFromApiAsync<ShopInfo>($"api/bff/shops/{shopId}");
|
|
|
|
|
|
|
|
|
|
public async Task<List<ProductInfo>> GetProductsAsync(Guid shopId)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<ProductInfo>>($"api/bff/shops/{shopId}/products", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<ProductInfo>($"api/bff/shops/{shopId}/products");
|
|
|
|
|
|
|
|
|
|
public async Task<List<CategoryInfo>> GetCategoriesAsync(Guid shopId)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<CategoryInfo>>($"api/bff/shops/{shopId}/categories", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<CategoryInfo>($"api/bff/shops/{shopId}/categories");
|
|
|
|
|
|
|
|
|
|
public async Task<List<TableInfo>> GetTablesAsync(Guid shopId)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<TableInfo>>($"api/bff/shops/{shopId}/tables", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<TableInfo>($"api/bff/shops/{shopId}/tables");
|
|
|
|
|
|
|
|
|
|
public async Task<List<AppointmentInfo>> GetAppointmentsAsync(Guid shopId)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<AppointmentInfo>>($"api/bff/shops/{shopId}/appointments", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<AppointmentInfo>($"api/bff/shops/{shopId}/appointments");
|
|
|
|
|
|
|
|
|
|
public async Task<List<StaffInfo>> GetStaffAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<StaffInfo>>("api/bff/staff", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<StaffInfo>("api/bff/staff");
|
|
|
|
|
|
|
|
|
|
// ═══ ADMIN-LEVEL PRODUCT/CATEGORY METHODS ═══
|
|
|
|
|
|
|
|
|
|
@@ -88,16 +150,14 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<AdminProductInfo>> GetAllProductsAsync(Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue ? $"api/bff/products?shopId={shopId}" : "api/bff/products";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<AdminProductInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<AdminProductInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<List<AdminCategoryInfo>> GetAllCategoriesAsync(Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue ? $"api/bff/categories?shopId={shopId}" : "api/bff/categories";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<AdminCategoryInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<AdminCategoryInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> CreateProductAsync(CreateProductRequest req)
|
|
|
|
|
@@ -128,9 +188,8 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<InventoryItemInfo>> GetInventoryAsync(Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue ? $"api/bff/inventory?shopId={shopId}" : "api/bff/inventory";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<InventoryItemInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<InventoryItemInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ═══ MEMBERSHIP/CUSTOMER METHODS ═══
|
|
|
|
|
@@ -139,7 +198,7 @@ public class PosDataService
|
|
|
|
|
int CurrentLevel, int TotalExpEarned, DateTime CreatedAt, string? LevelName);
|
|
|
|
|
|
|
|
|
|
public async Task<List<MemberInfo>> GetMembersAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<MemberInfo>>("api/bff/members", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<MemberInfo>("api/bff/members");
|
|
|
|
|
|
|
|
|
|
// ═══ STAFF CREATE ═══
|
|
|
|
|
|
|
|
|
|
@@ -182,13 +241,12 @@ public class PosDataService
|
|
|
|
|
string? EmployeeCode, string? Role, string? Phone);
|
|
|
|
|
|
|
|
|
|
public async Task<List<StaffRoleInfo>> GetStaffRolesAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<StaffRoleInfo>>("api/bff/staff/roles", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<StaffRoleInfo>("api/bff/staff/roles");
|
|
|
|
|
|
|
|
|
|
public async Task<List<ScheduleInfo>> GetStaffSchedulesAsync(Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue ? $"api/bff/staff/schedules?shopId={shopId}" : "api/bff/staff/schedules";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<ScheduleInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<ScheduleInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ═══ ORDERS ═══
|
|
|
|
|
@@ -198,11 +256,10 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<OrderInfo>> GetOrdersAsync(Guid? shopId = null, string filter = "today")
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue
|
|
|
|
|
? $"api/bff/orders?shopId={shopId}&filter={filter}"
|
|
|
|
|
: $"api/bff/orders?filter={filter}";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<OrderInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<OrderInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ═══ WALLETS / FINANCE ═══
|
|
|
|
|
@@ -211,17 +268,17 @@ public class PosDataService
|
|
|
|
|
public record WalletTxnInfo(Guid Id, Guid WalletId, decimal Amount, string? Description, DateTime CreatedAt, string? ItemName);
|
|
|
|
|
|
|
|
|
|
public async Task<List<WalletInfo>> GetWalletsAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<WalletInfo>>("api/bff/wallets", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<WalletInfo>("api/bff/wallets");
|
|
|
|
|
|
|
|
|
|
public async Task<List<WalletTxnInfo>> GetWalletTransactionsAsync(int limit = 50)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<WalletTxnInfo>>($"api/bff/wallet/transactions?limit={limit}", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<WalletTxnInfo>($"api/bff/wallet/transactions?limit={limit}");
|
|
|
|
|
|
|
|
|
|
// ═══ DEVICES ═══
|
|
|
|
|
|
|
|
|
|
public record DeviceInfo(Guid Id, string? DeviceToken, string? Platform, bool IsActive, DateTime CreatedAt, string? StaffCode);
|
|
|
|
|
|
|
|
|
|
public async Task<List<DeviceInfo>> GetDevicesAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<DeviceInfo>>("api/bff/devices", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<DeviceInfo>("api/bff/devices");
|
|
|
|
|
|
|
|
|
|
// ═══ PROMOTIONS ═══
|
|
|
|
|
|
|
|
|
|
@@ -229,7 +286,7 @@ public class PosDataService
|
|
|
|
|
bool IsActive, string? DiscountType, decimal? DiscountValue, int VoucherCount, int RedemptionCount);
|
|
|
|
|
|
|
|
|
|
public async Task<List<PromotionInfo>> GetPromotionsAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<PromotionInfo>>("api/bff/promotions", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<PromotionInfo>("api/bff/promotions");
|
|
|
|
|
|
|
|
|
|
// ═══ CAMPAIGNS CRUD ═══
|
|
|
|
|
|
|
|
|
|
@@ -240,7 +297,7 @@ public class PosDataService
|
|
|
|
|
public record CreateCampaignRequest(string Name, string? Description, decimal FaceValue, int TotalVouchers, DateTime StartDate, DateTime EndDate);
|
|
|
|
|
|
|
|
|
|
public async Task<List<CampaignInfo>> GetCampaignsAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<CampaignInfo>>("api/bff/campaigns", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<CampaignInfo>("api/bff/campaigns");
|
|
|
|
|
|
|
|
|
|
public async Task<bool> CreateCampaignAsync(CreateCampaignRequest req)
|
|
|
|
|
{
|
|
|
|
|
@@ -297,9 +354,8 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<InventoryTxnInfo>> GetInventoryTransactionsAsync(Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue ? $"api/bff/inventory/transactions?shopId={shopId}" : "api/bff/inventory/transactions";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<InventoryTxnInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<InventoryTxnInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ═══ MEMBERSHIP LEVELS ═══
|
|
|
|
|
@@ -307,21 +363,21 @@ public class PosDataService
|
|
|
|
|
public record LevelDefinitionInfo(Guid Id, int Level, string Name, int MinExp, int MaxExp, int MemberCount);
|
|
|
|
|
|
|
|
|
|
public async Task<List<LevelDefinitionInfo>> GetMembershipLevelsAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<LevelDefinitionInfo>>("api/bff/membership/levels", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<LevelDefinitionInfo>("api/bff/membership/levels");
|
|
|
|
|
|
|
|
|
|
// ═══ SHOP STATS (aggregated per-shop) ═══
|
|
|
|
|
|
|
|
|
|
public record ShopStatsInfo(Guid ShopId, int ProductCount, int OrderCount, int StaffCount, decimal Revenue);
|
|
|
|
|
|
|
|
|
|
public async Task<List<ShopStatsInfo>> GetShopStatsAsync()
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<ShopStatsInfo>>("api/bff/shops/stats", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<ShopStatsInfo>("api/bff/shops/stats");
|
|
|
|
|
|
|
|
|
|
// ═══ BOOKING RESOURCES ═══
|
|
|
|
|
|
|
|
|
|
public record ResourceInfo(Guid Id, string Name, string? ResourceType, int Capacity, bool IsActive);
|
|
|
|
|
|
|
|
|
|
public async Task<List<ResourceInfo>> GetResourcesAsync(Guid shopId)
|
|
|
|
|
{ AttachToken(); return await _http.GetFromJsonAsync<List<ResourceInfo>>($"api/bff/shops/{shopId}/resources", _jsonOptions) ?? new(); }
|
|
|
|
|
=> await GetListFromApiAsync<ResourceInfo>($"api/bff/shops/{shopId}/resources");
|
|
|
|
|
|
|
|
|
|
// ═══ POS DASHBOARD (real-time daily stats) ═══
|
|
|
|
|
|
|
|
|
|
@@ -351,7 +407,7 @@ public class PosDataService
|
|
|
|
|
// EN: POS order creation DTOs
|
|
|
|
|
// VI: DTOs cho tạo đơn POS
|
|
|
|
|
public record CreatePosOrderRequest(Guid ShopId, string? PaymentMethod, List<PosOrderItemRequest> Items);
|
|
|
|
|
public record PosOrderItemRequest(Guid ProductId, string ProductName, int Quantity, decimal UnitPrice);
|
|
|
|
|
public record PosOrderItemRequest(Guid ProductId, string ProductName, int Quantity, decimal UnitPrice, string? ProductType = "Physical");
|
|
|
|
|
public record CreatePosOrderResponse(Guid OrderId, string TransactionId, decimal TotalAmount, string Status);
|
|
|
|
|
|
|
|
|
|
public async Task<CreatePosOrderResponse?> CreatePosOrderAsync(CreatePosOrderRequest req)
|
|
|
|
|
@@ -434,11 +490,10 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<RevenueReportItem>> GetRevenueReportAsync(string period = "daily", Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue
|
|
|
|
|
? $"api/bff/reports/revenue?period={period}&shopId={shopId}"
|
|
|
|
|
: $"api/bff/reports/revenue?period={period}";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<RevenueReportItem>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<RevenueReportItem>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ═══ SHOP SETTINGS ═══
|
|
|
|
|
@@ -447,10 +502,7 @@ public class PosDataService
|
|
|
|
|
public record UpdateShopSettingsRequest(string? FeaturesConfig, string? OpenTime, string? CloseTime, string? OpenDays);
|
|
|
|
|
|
|
|
|
|
public async Task<ShopSettingsInfo?> GetShopSettingsAsync(Guid shopId)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
return await _http.GetFromJsonAsync<ShopSettingsInfo>($"api/bff/shops/{shopId}/settings", _jsonOptions);
|
|
|
|
|
}
|
|
|
|
|
=> await GetObjectFromApiAsync<ShopSettingsInfo>($"api/bff/shops/{shopId}/settings");
|
|
|
|
|
|
|
|
|
|
public async Task<bool> UpdateShopSettingsAsync(Guid shopId, UpdateShopSettingsRequest req)
|
|
|
|
|
{
|
|
|
|
|
@@ -465,11 +517,10 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<TopProductInfo>> GetTopProductsAsync(Guid? shopId = null, int limit = 10)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
var url = shopId.HasValue
|
|
|
|
|
? $"api/bff/reports/top-products?shopId={shopId}&limit={limit}"
|
|
|
|
|
: $"api/bff/reports/top-products?limit={limit}";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<TopProductInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<TopProductInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ═══ TABLES CRUD ═══
|
|
|
|
|
@@ -531,10 +582,9 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<KitchenTicketInfo>> GetKitchenTicketsAsync(Guid? shopId = null, string status = "pending")
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
if (!shopId.HasValue) return new();
|
|
|
|
|
var url = $"api/bff/shops/{shopId}/kitchen-tickets?status={status}";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<KitchenTicketInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<KitchenTicketInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> UpdateTicketStatusAsync(Guid ticketId, UpdateTicketStatusRequest req)
|
|
|
|
|
@@ -549,10 +599,9 @@ public class PosDataService
|
|
|
|
|
|
|
|
|
|
public async Task<List<RecipeInfo>> GetRecipesAsync(Guid? shopId = null)
|
|
|
|
|
{
|
|
|
|
|
AttachToken();
|
|
|
|
|
if (!shopId.HasValue) return new();
|
|
|
|
|
var url = $"api/bff/shops/{shopId}/recipes";
|
|
|
|
|
return await _http.GetFromJsonAsync<List<RecipeInfo>>(url, _jsonOptions) ?? new();
|
|
|
|
|
return await GetListFromApiAsync<RecipeInfo>(url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> CreateRecipeAsync(CreateRecipeRequest req)
|
|
|
|
|
|