fix(web-client-tpos): fix all build errors — add CRUD methods, fix type bindings
- Added missing CRUD methods: DeleteRecipeItem, SaveRecipe, AddTable, SaveTable, EditTable, DeleteTableItem, AddAppointment, AddResource, SaveResource, DeleteResourceItem, DeleteScheduleItem, MarkTicketDone, LoadKitchenTickets - Fixed CS1654: recipe ingredient foreach mutation → index-based for loop - Fixed CS1503: schedule time input type=time → type=text (Blazor binding) - Fixed CS1061: Guid.HasValue/Value → direct Guid in BFF kitchen/recipes
This commit is contained in:
@@ -1257,8 +1257,8 @@
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:12px;">
|
||||
<div><label style="font-size:12px;font-weight:600;display:block;margin-bottom:4px;">Nhân viên (ID)</label><input type="text" @bind="_newSchedStaffIdStr" placeholder="Staff ID..." style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);" /></div>
|
||||
<div><label style="font-size:12px;font-weight:600;display:block;margin-bottom:4px;">Thứ (1=T2..7=CN)</label><input type="number" @bind="_newSchedDay" min="1" max="7" style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);" /></div>
|
||||
<div><label style="font-size:12px;font-weight:600;display:block;margin-bottom:4px;">Bắt đầu</label><input type="time" @bind="_newSchedStart" style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);" /></div>
|
||||
<div><label style="font-size:12px;font-weight:600;display:block;margin-bottom:4px;">Kết thúc</label><input type="time" @bind="_newSchedEnd" style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);" /></div>
|
||||
<div><label style="font-size:12px;font-weight:600;display:block;margin-bottom:4px;">Bắt đầu</label><input type="text" @bind="_newSchedStart" placeholder="08:00" style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);" /></div>
|
||||
<div><label style="font-size:12px;font-weight:600;display:block;margin-bottom:4px;">Kết thúc</label><input type="text" @bind="_newSchedEnd" placeholder="17:00" style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);" /></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;margin-top:12px;">
|
||||
<button class="admin-btn-primary" @onclick="AddSchedule" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="check" style="width:14px;height:14px;"></i>Lưu</button>
|
||||
@@ -1322,14 +1322,15 @@
|
||||
</div>
|
||||
<div style="margin-bottom:12px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;"><span style="font-size:13px;font-weight:600;">Nguyên liệu</span><button @onclick='() => _recipeIngredients.Add(new("","","",0,0))' style="padding:4px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:transparent;color:var(--admin-text-primary);font-size:12px;cursor:pointer;">+ Thêm</button></div>
|
||||
@foreach (var (ing, idx) in _recipeIngredients.Select((x,i) => (x,i)))
|
||||
@for (var idx = 0; idx < _recipeIngredients.Count; idx++)
|
||||
{
|
||||
var i = idx;
|
||||
<div style="display:grid;grid-template-columns:2fr 1fr 1fr 1fr auto;gap:8px;margin-bottom:6px;">
|
||||
<input @bind="ing.Name" placeholder="Tên nguyên liệu" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<input @bind="ing.Quantity" type="number" placeholder="Qty" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<input @bind="ing.Unit" placeholder="Đơn vị" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<input @bind="ing.Cost" type="number" placeholder="Chi phí" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<button @onclick='() => _recipeIngredients.RemoveAt(idx)' style="padding:6px;border-radius:6px;border:none;background:rgba(239,68,68,0.1);color:#EF4444;cursor:pointer;">✕</button>
|
||||
<input value="@_recipeIngredients[i].Name" @onchange="@(e => { var t = _recipeIngredients[i]; _recipeIngredients[i] = (e.Value?.ToString() ?? "", t.Unit, t.Qty, t.Quantity, t.Cost); })" placeholder="Tên nguyên liệu" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<input value="@_recipeIngredients[i].Quantity" @onchange="@(e => { var t = _recipeIngredients[i]; _recipeIngredients[i] = (t.Name, t.Unit, t.Qty, decimal.TryParse(e.Value?.ToString(), out var v) ? v : 0, t.Cost); })" type="number" placeholder="Qty" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<input value="@_recipeIngredients[i].Unit" @onchange="@(e => { var t = _recipeIngredients[i]; _recipeIngredients[i] = (t.Name, e.Value?.ToString() ?? "", t.Qty, t.Quantity, t.Cost); })" placeholder="Đơn vị" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<input value="@_recipeIngredients[i].Cost" @onchange="@(e => { var t = _recipeIngredients[i]; _recipeIngredients[i] = (t.Name, t.Unit, t.Qty, t.Quantity, decimal.TryParse(e.Value?.ToString(), out var v) ? v : 0); })" type="number" placeholder="Chi phí" style="padding:6px 10px;border-radius:6px;border:1px solid var(--admin-border-subtle);background:var(--admin-bg-elevated);color:var(--admin-text-primary);font-size:12px;" />
|
||||
<button @onclick='() => _recipeIngredients.RemoveAt(i)' style="padding:6px;border-radius:6px;border:none;background:rgba(239,68,68,0.1);color:#EF4444;cursor:pointer;">✕</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -2503,4 +2504,234 @@
|
||||
await DataService.DeleteMemberAsync(memberId);
|
||||
_members = await DataService.GetMembersAsync();
|
||||
}
|
||||
|
||||
// ═══ TABLE CRUD ═══
|
||||
private void EditTable(PosDataService.TableInfo table)
|
||||
{
|
||||
_editingTableId = table.Id;
|
||||
_newTableNumber = table.TableNumber;
|
||||
_newTableCapacity = table.Capacity;
|
||||
_newTableZone = table.Zone ?? "";
|
||||
_tableFormMessage = null;
|
||||
_showTableForm = true;
|
||||
}
|
||||
|
||||
private async Task AddTable()
|
||||
{
|
||||
_tableFormMessage = null;
|
||||
if (string.IsNullOrWhiteSpace(_newTableNumber) || !_shopGuid.HasValue)
|
||||
{
|
||||
_tableFormMessage = "Vui lòng nhập số bàn."; _tableFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await DataService.CreateTableAsync(new PosDataService.CreateTableRequest(_shopGuid.Value, _newTableNumber, _newTableCapacity, _newTableZone));
|
||||
_tableFormMessage = $"Đã thêm bàn '{_newTableNumber}' thành công!"; _tableFormSuccess = true;
|
||||
_newTableNumber = ""; _newTableCapacity = 4; _newTableZone = "";
|
||||
if (_shopGuid.HasValue) _tables = await DataService.GetTablesAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _tableFormMessage = $"Lỗi: {ex.Message}"; _tableFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task SaveTable()
|
||||
{
|
||||
_tableFormMessage = null;
|
||||
if (string.IsNullOrWhiteSpace(_newTableNumber) || !_shopGuid.HasValue || !_editingTableId.HasValue)
|
||||
{
|
||||
_tableFormMessage = "Vui lòng nhập số bàn."; _tableFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await DataService.UpdateTableAsync(_editingTableId.Value, new PosDataService.CreateTableRequest(_shopGuid.Value, _newTableNumber, _newTableCapacity, _newTableZone));
|
||||
_tableFormMessage = $"Đã cập nhật bàn '{_newTableNumber}' thành công!"; _tableFormSuccess = true;
|
||||
_editingTableId = null;
|
||||
if (_shopGuid.HasValue) _tables = await DataService.GetTablesAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _tableFormMessage = $"Lỗi: {ex.Message}"; _tableFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task DeleteTableItem(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DataService.DeleteTableAsync(id);
|
||||
if (_shopGuid.HasValue) _tables = await DataService.GetTablesAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể xóa bàn: {ex.Message}"; }
|
||||
}
|
||||
|
||||
// ═══ APPOINTMENT CRUD ═══
|
||||
private async Task AddAppointment()
|
||||
{
|
||||
_apptFormMessage = null;
|
||||
if (!_shopGuid.HasValue)
|
||||
{
|
||||
_apptFormMessage = "Thiếu thông tin cửa hàng."; _apptFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await DataService.CreateAppointmentAsync(new PosDataService.CreateAppointmentRequest(
|
||||
_shopGuid.Value, null, null, null, null, _newApptStart, _newApptEnd));
|
||||
_apptFormMessage = "Đã thêm lịch hẹn thành công!"; _apptFormSuccess = true;
|
||||
_showApptForm = false;
|
||||
_appointments = await DataService.GetAppointmentsAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _apptFormMessage = $"Lỗi: {ex.Message}"; _apptFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task CancelAppt(Guid apptId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DataService.CancelAppointmentAsync(apptId);
|
||||
if (_shopGuid.HasValue) _appointments = await DataService.GetAppointmentsAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể hủy lịch hẹn: {ex.Message}"; }
|
||||
}
|
||||
|
||||
// ═══ RESOURCE CRUD ═══
|
||||
private void EditResource(PosDataService.ResourceInfo r)
|
||||
{
|
||||
_editingResourceId = r.Id;
|
||||
_newResourceName = r.Name;
|
||||
_newResourceType = r.ResourceType ?? "Room";
|
||||
_newResourceCapacity = r.Capacity;
|
||||
_resourceFormMessage = null;
|
||||
_showResourceForm = true;
|
||||
}
|
||||
|
||||
private async Task AddResource()
|
||||
{
|
||||
_resourceFormMessage = null;
|
||||
if (string.IsNullOrWhiteSpace(_newResourceName) || !_shopGuid.HasValue)
|
||||
{
|
||||
_resourceFormMessage = "Vui lòng nhập tên tài nguyên."; _resourceFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await DataService.CreateResourceAsync(new PosDataService.CreateResourceRequest(_shopGuid.Value, _newResourceName, _newResourceType, _newResourceCapacity));
|
||||
_resourceFormMessage = $"Đã thêm '{_newResourceName}' thành công!"; _resourceFormSuccess = true;
|
||||
_newResourceName = ""; _newResourceType = "Room"; _newResourceCapacity = 1;
|
||||
_resources = await DataService.GetResourcesAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _resourceFormMessage = $"Lỗi: {ex.Message}"; _resourceFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task SaveResource()
|
||||
{
|
||||
_resourceFormMessage = null;
|
||||
if (string.IsNullOrWhiteSpace(_newResourceName) || !_shopGuid.HasValue || !_editingResourceId.HasValue)
|
||||
{
|
||||
_resourceFormMessage = "Vui lòng nhập tên tài nguyên."; _resourceFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await DataService.UpdateResourceAsync(_editingResourceId.Value, new PosDataService.CreateResourceRequest(_shopGuid.Value, _newResourceName, _newResourceType, _newResourceCapacity));
|
||||
_resourceFormMessage = $"Đã cập nhật '{_newResourceName}' thành công!"; _resourceFormSuccess = true;
|
||||
_editingResourceId = null;
|
||||
_resources = await DataService.GetResourcesAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _resourceFormMessage = $"Lỗi: {ex.Message}"; _resourceFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task DeleteResourceItem(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DataService.DeleteResourceAsync(id);
|
||||
if (_shopGuid.HasValue) _resources = await DataService.GetResourcesAsync(_shopGuid.Value);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể xóa tài nguyên: {ex.Message}"; }
|
||||
}
|
||||
|
||||
// ═══ SCHEDULE CRUD ═══
|
||||
private async Task AddSchedule()
|
||||
{
|
||||
_schedFormMessage = null;
|
||||
if (!Guid.TryParse(_newSchedStaffIdStr, out var staffId) || !_shopGuid.HasValue)
|
||||
{
|
||||
_schedFormMessage = "Vui lòng nhập đúng Staff ID."; _schedFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_newSchedStaffId = staffId;
|
||||
await DataService.CreateScheduleAsync(new PosDataService.CreateScheduleRequest(_shopGuid.Value, _newSchedStaffId, _newSchedDay, _newSchedStart, _newSchedEnd));
|
||||
_schedFormMessage = "Đã thêm lịch làm việc thành công!"; _schedFormSuccess = true;
|
||||
_showScheduleForm = false;
|
||||
_staffSchedules = await DataService.GetStaffSchedulesAsync(_shopGuid);
|
||||
}
|
||||
catch (Exception ex) { _schedFormMessage = $"Lỗi: {ex.Message}"; _schedFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task DeleteScheduleItem(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DataService.DeleteScheduleAsync(id);
|
||||
_staffSchedules = await DataService.GetStaffSchedulesAsync(_shopGuid);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể xóa lịch làm việc: {ex.Message}"; }
|
||||
}
|
||||
|
||||
// ═══ KITCHEN ═══
|
||||
private async Task LoadKitchenTickets(string status)
|
||||
{
|
||||
_kitchenStatusFilter = status;
|
||||
try
|
||||
{
|
||||
if (_shopGuid.HasValue)
|
||||
_kitchenTickets = await DataService.GetKitchenTicketsAsync(_shopGuid, status);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể tải kitchen tickets: {ex.Message}"; }
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task MarkTicketDone(Guid ticketId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DataService.UpdateTicketStatusAsync(ticketId, new PosDataService.UpdateTicketStatusRequest("completed"));
|
||||
if (_shopGuid.HasValue)
|
||||
_kitchenTickets = await DataService.GetKitchenTicketsAsync(_shopGuid, _kitchenStatusFilter);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể cập nhật trạng thái: {ex.Message}"; }
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
// ═══ RECIPE CRUD ═══
|
||||
private async Task SaveRecipe()
|
||||
{
|
||||
_recipeFormMessage = null;
|
||||
if (string.IsNullOrWhiteSpace(_newRecipeName) || !_shopGuid.HasValue)
|
||||
{
|
||||
_recipeFormMessage = "Vui lòng nhập tên công thức."; _recipeFormSuccess = false; return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var ingredients = _recipeIngredients
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i.Name))
|
||||
.Select(i => new PosDataService.RecipeIngredientRequest(i.Name, i.Quantity, i.Unit, i.Cost))
|
||||
.ToList();
|
||||
var req = new PosDataService.CreateRecipeRequest(_shopGuid.Value, Guid.Empty, _newRecipeName, _newRecipeInstructions, _newRecipePrepTime, ingredients);
|
||||
bool ok;
|
||||
if (_editingRecipeId.HasValue)
|
||||
ok = await DataService.UpdateRecipeAsync(_editingRecipeId.Value, req);
|
||||
else
|
||||
ok = await DataService.CreateRecipeAsync(req);
|
||||
_recipeFormMessage = ok ? (_editingRecipeId.HasValue ? "Đã cập nhật công thức!" : "Đã thêm công thức!") : "Lỗi khi lưu công thức.";
|
||||
_recipeFormSuccess = ok;
|
||||
if (ok) { _showRecipeForm = false; _editingRecipeId = null; _recipes = await DataService.GetRecipesAsync(_shopGuid); }
|
||||
}
|
||||
catch (Exception ex) { _recipeFormMessage = $"Lỗi: {ex.Message}"; _recipeFormSuccess = false; }
|
||||
}
|
||||
|
||||
private async Task DeleteRecipeItem(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DataService.DeleteRecipeAsync(id);
|
||||
_recipes = await DataService.GetRecipesAsync(_shopGuid);
|
||||
}
|
||||
catch (Exception ex) { _errorMessage = $"Không thể xóa công thức: {ex.Message}"; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1689,8 +1689,8 @@ public class BffDataController : ControllerBase
|
||||
if (merchantId == null) return Ok(Array.Empty<object>());
|
||||
var myShopIds = await GetMyShopIdsAsync(merchantId.Value);
|
||||
if (!myShopIds.Any()) return Ok(Array.Empty<object>());
|
||||
if (shopId.HasValue && !myShopIds.Contains(shopId.Value)) return Ok(Array.Empty<object>());
|
||||
var targetShopIds = shopId.HasValue ? new List<Guid> { shopId.Value } : myShopIds;
|
||||
if (!myShopIds.Contains(shopId)) return Ok(Array.Empty<object>());
|
||||
var targetShopIds = new List<Guid> { shopId };
|
||||
try
|
||||
{
|
||||
await using var conn = new NpgsqlConnection(ConnStr("fnb_engine"));
|
||||
@@ -1733,8 +1733,8 @@ public class BffDataController : ControllerBase
|
||||
if (merchantId == null) return Ok(Array.Empty<object>());
|
||||
var myShopIds = await GetMyShopIdsAsync(merchantId.Value);
|
||||
if (!myShopIds.Any()) return Ok(Array.Empty<object>());
|
||||
if (shopId.HasValue && !myShopIds.Contains(shopId.Value)) return Ok(Array.Empty<object>());
|
||||
var targetShopIds = shopId.HasValue ? new List<Guid> { shopId.Value } : myShopIds;
|
||||
if (!myShopIds.Contains(shopId)) return Ok(Array.Empty<object>());
|
||||
var targetShopIds = new List<Guid> { shopId };
|
||||
try
|
||||
{
|
||||
await using var conn = new NpgsqlConnection(ConnStr("catalog_service"));
|
||||
|
||||
Reference in New Issue
Block a user