chnage in layout of view all 'order' modal and chnage the local table name from ScanRecords to lunch_order_transactions
parent
cb35b6581a
commit
306e03e71a
|
|
@ -54,7 +54,7 @@ public partial class App : Application
|
||||||
};
|
};
|
||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
|
|
||||||
// Background sync: every 15 minutes, POST unsynced ScanRecords to API
|
// Background sync: every 15 minutes, POST unsynced lunch_order_transactions to API
|
||||||
_syncTimer = new System.Timers.Timer(TimeSpan.FromMinutes(15).TotalMilliseconds)
|
_syncTimer = new System.Timers.Timer(TimeSpan.FromMinutes(15).TotalMilliseconds)
|
||||||
{
|
{
|
||||||
AutoReset = true
|
AutoReset = true
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using UtopiaCanteenSystem.Models;
|
||||||
namespace UtopiaCanteenSystem.Data;
|
namespace UtopiaCanteenSystem.Data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SQLite DbContext for Labour and ScanRecord tables.
|
/// SQLite DbContext for Labour and lunch_order_transactions (scan records) tables.
|
||||||
/// Database file is created in application directory on first run.
|
/// Database file is created in application directory on first run.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AppDbContext : DbContext
|
public class AppDbContext : DbContext
|
||||||
|
|
@ -16,7 +16,7 @@ public class AppDbContext : DbContext
|
||||||
"utopia_canteen.db");
|
"utopia_canteen.db");
|
||||||
|
|
||||||
public DbSet<Labour> Labour { get; set; }
|
public DbSet<Labour> Labour { get; set; }
|
||||||
public DbSet<ScanRecord> ScanRecords { get; set; }
|
public DbSet<ScanRecord> LunchOrderTransactions { get; set; }
|
||||||
public DbSet<AdminLoginRecord> AdminLoginRecords { get; set; }
|
public DbSet<AdminLoginRecord> AdminLoginRecords { get; set; }
|
||||||
|
|
||||||
public AppDbContext() { }
|
public AppDbContext() { }
|
||||||
|
|
@ -38,9 +38,10 @@ public class AppDbContext : DbContext
|
||||||
e.HasIndex(x => x.CardId);
|
e.HasIndex(x => x.CardId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ScanRecord: Index for unsynced queries and by ScanTime.
|
// lunch_order_transactions (scan records): Index for unsynced queries and by ScanTime.
|
||||||
modelBuilder.Entity<ScanRecord>(e =>
|
modelBuilder.Entity<ScanRecord>(e =>
|
||||||
{
|
{
|
||||||
|
e.ToTable("lunch_order_transactions");
|
||||||
e.HasKey(x => x.Id);
|
e.HasKey(x => x.Id);
|
||||||
e.HasIndex(x => x.IsSynced);
|
e.HasIndex(x => x.IsSynced);
|
||||||
e.HasIndex(x => x.ScanTime);
|
e.HasIndex(x => x.ScanTime);
|
||||||
|
|
@ -59,15 +60,72 @@ public class AppDbContext : DbContext
|
||||||
public void EnsureDatabaseCreated()
|
public void EnsureDatabaseCreated()
|
||||||
{
|
{
|
||||||
Database.EnsureCreated();
|
Database.EnsureCreated();
|
||||||
UpgradeScanRecordsSchemaIfNeeded();
|
MigrateScanRecordsToLunchOrderTransactionsIfNeeded();
|
||||||
|
UpgradeLunchOrderTransactionsSchemaIfNeeded();
|
||||||
EnsureAdminLoginTableExists();
|
EnsureAdminLoginTableExists();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lightweight schema upgrade: add SiteId and DeviceId to ScanRecords if missing (no EF migrations).
|
/// One-time migration: rename old tables (ScanRecords or CanteenScanEvents) to lunch_order_transactions for existing databases.
|
||||||
|
/// </summary>
|
||||||
|
private void MigrateScanRecordsToLunchOrderTransactionsIfNeeded()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var conn = Database.GetDbConnection();
|
||||||
|
if (conn.State != ConnectionState.Open)
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
var hasOldScanRecords = false;
|
||||||
|
var hasOldCanteenScanEvents = false;
|
||||||
|
var hasNew = false;
|
||||||
|
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table' AND name='ScanRecords'";
|
||||||
|
using var r = cmd.ExecuteReader();
|
||||||
|
hasOldScanRecords = r.Read();
|
||||||
|
}
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table' AND name='CanteenScanEvents'";
|
||||||
|
using var r = cmd.ExecuteReader();
|
||||||
|
hasOldCanteenScanEvents = r.Read();
|
||||||
|
}
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type='table' AND name='lunch_order_transactions'";
|
||||||
|
using var r = cmd.ExecuteReader();
|
||||||
|
hasNew = r.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasNew)
|
||||||
|
{
|
||||||
|
if (hasOldScanRecords)
|
||||||
|
{
|
||||||
|
using var cmd = conn.CreateCommand();
|
||||||
|
cmd.CommandText = "ALTER TABLE ScanRecords RENAME TO lunch_order_transactions";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
else if (hasOldCanteenScanEvents)
|
||||||
|
{
|
||||||
|
using var cmd = conn.CreateCommand();
|
||||||
|
cmd.CommandText = "ALTER TABLE CanteenScanEvents RENAME TO lunch_order_transactions";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore; DB may be new or already migrated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lightweight schema upgrade: add SiteId and DeviceId to lunch_order_transactions if missing (no EF migrations).
|
||||||
/// Does not delete any data.
|
/// Does not delete any data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpgradeScanRecordsSchemaIfNeeded()
|
private void UpgradeLunchOrderTransactionsSchemaIfNeeded()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -78,23 +136,23 @@ public class AppDbContext : DbContext
|
||||||
var columns = new List<string>();
|
var columns = new List<string>();
|
||||||
using (var cmd = conn.CreateCommand())
|
using (var cmd = conn.CreateCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = "SELECT name FROM pragma_table_info('ScanRecords')";
|
cmd.CommandText = "SELECT name FROM pragma_table_info('lunch_order_transactions')";
|
||||||
using var r = cmd.ExecuteReader();
|
using var r = cmd.ExecuteReader();
|
||||||
while (r.Read())
|
while (r.Read())
|
||||||
columns.Add(r.GetString(0));
|
columns.Add(r.GetString(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!columns.Contains("SiteId", StringComparer.OrdinalIgnoreCase))
|
if (columns.Count > 0 && !columns.Contains("SiteId", StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
using var cmd = conn.CreateCommand();
|
using var cmd = conn.CreateCommand();
|
||||||
cmd.CommandText = "ALTER TABLE ScanRecords ADD COLUMN SiteId TEXT DEFAULT ''";
|
cmd.CommandText = "ALTER TABLE lunch_order_transactions ADD COLUMN SiteId TEXT DEFAULT ''";
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!columns.Contains("DeviceId", StringComparer.OrdinalIgnoreCase))
|
if (columns.Count > 0 && !columns.Contains("DeviceId", StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
using var cmd = conn.CreateCommand();
|
using var cmd = conn.CreateCommand();
|
||||||
cmd.CommandText = "ALTER TABLE ScanRecords ADD COLUMN DeviceId TEXT DEFAULT ''";
|
cmd.CommandText = "ALTER TABLE lunch_order_transactions ADD COLUMN DeviceId TEXT DEFAULT ''";
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ public interface IRfidService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes a scan for the given card ID. Applies configured timeout,
|
/// Processes a scan for the given card ID. Applies configured timeout,
|
||||||
/// saves to ScanRecords if allowed, and returns result message.
|
/// saves to lunch_order_transactions if allowed, and returns result message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
(bool Success, string Message) ProcessScan(string cardId);
|
(bool Success, string Message) ProcessScan(string cardId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace UtopiaCanteenSystem.Services;
|
namespace UtopiaCanteenSystem.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Syncs unsynced ScanRecords to the configured UIND API endpoint.
|
/// Syncs unsynced lunch_order_transactions to the configured UIND API endpoint.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISyncService
|
public interface ISyncService
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace UtopiaCanteenSystem.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles RFID scan logic: validates input, enforces configurable timeout,
|
/// Handles RFID scan logic: validates input, enforces configurable timeout,
|
||||||
/// and saves ScanRecord to SQLite.
|
/// and saves scan events to SQLite (lunch_order_transactions table).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RfidService : IRfidService
|
public class RfidService : IRfidService
|
||||||
{
|
{
|
||||||
|
|
@ -45,7 +45,7 @@ public class RfidService : IRfidService
|
||||||
var windowStart = nowUtc.AddSeconds(-timeoutSeconds);
|
var windowStart = nowUtc.AddSeconds(-timeoutSeconds);
|
||||||
|
|
||||||
// Enforce timeout: no duplicate scan within the timeout window
|
// Enforce timeout: no duplicate scan within the timeout window
|
||||||
var lastInWindow = db.ScanRecords
|
var lastInWindow = db.LunchOrderTransactions
|
||||||
.Where(r => r.CardId == cardId)
|
.Where(r => r.CardId == cardId)
|
||||||
.Where(r => r.ScanTime >= windowStart)
|
.Where(r => r.ScanTime >= windowStart)
|
||||||
.OrderByDescending(r => r.ScanTime)
|
.OrderByDescending(r => r.ScanTime)
|
||||||
|
|
@ -61,7 +61,7 @@ public class RfidService : IRfidService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: prevent any scan within the timeout window (any card).
|
// Optional: prevent any scan within the timeout window (any card).
|
||||||
var lastAnyScanInWindow = db.ScanRecords
|
var lastAnyScanInWindow = db.LunchOrderTransactions
|
||||||
.Where(r => r.ScanTime >= windowStart)
|
.Where(r => r.ScanTime >= windowStart)
|
||||||
.OrderByDescending(r => r.ScanTime)
|
.OrderByDescending(r => r.ScanTime)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
@ -83,7 +83,7 @@ public class RfidService : IRfidService
|
||||||
SiteId = _configService.GetSiteId(),
|
SiteId = _configService.GetSiteId(),
|
||||||
DeviceId = _configService.GetDeviceId()
|
DeviceId = _configService.GetDeviceId()
|
||||||
};
|
};
|
||||||
db.ScanRecords.Add(record);
|
db.LunchOrderTransactions.Add(record);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
|
||||||
return new ScanResult(true, "Order recorded successfully.", 0);
|
return new ScanResult(true, "Order recorded successfully.", 0);
|
||||||
|
|
@ -92,7 +92,7 @@ public class RfidService : IRfidService
|
||||||
public ScanRecord? GetLastScan()
|
public ScanRecord? GetLastScan()
|
||||||
{
|
{
|
||||||
using var db = _dbFactory.CreateDbContext();
|
using var db = _dbFactory.CreateDbContext();
|
||||||
return db.ScanRecords
|
return db.LunchOrderTransactions
|
||||||
.OrderByDescending(r => r.ScanTime)
|
.OrderByDescending(r => r.ScanTime)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ public class RfidService : IRfidService
|
||||||
{
|
{
|
||||||
if (count <= 0) return Array.Empty<ScanRecord>();
|
if (count <= 0) return Array.Empty<ScanRecord>();
|
||||||
using var db = _dbFactory.CreateDbContext();
|
using var db = _dbFactory.CreateDbContext();
|
||||||
return db.ScanRecords
|
return db.LunchOrderTransactions
|
||||||
.OrderByDescending(r => r.ScanTime)
|
.OrderByDescending(r => r.ScanTime)
|
||||||
.Take(count)
|
.Take(count)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
@ -114,7 +114,7 @@ public class RfidService : IRfidService
|
||||||
var startUtc = startOfTodayLocal.ToUniversalTime();
|
var startUtc = startOfTodayLocal.ToUniversalTime();
|
||||||
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
||||||
using var db = _dbFactory.CreateDbContext();
|
using var db = _dbFactory.CreateDbContext();
|
||||||
return db.ScanRecords
|
return db.LunchOrderTransactions
|
||||||
.Where(r => r.ScanTime >= startUtc && r.ScanTime < endUtc)
|
.Where(r => r.ScanTime >= startUtc && r.ScanTime < endUtc)
|
||||||
.OrderByDescending(r => r.ScanTime)
|
.OrderByDescending(r => r.ScanTime)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
@ -130,7 +130,7 @@ public class RfidService : IRfidService
|
||||||
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
||||||
|
|
||||||
using var db = _dbFactory.CreateDbContext();
|
using var db = _dbFactory.CreateDbContext();
|
||||||
return db.ScanRecords
|
return db.LunchOrderTransactions
|
||||||
.Count(r => r.ScanTime >= startUtc && r.ScanTime < endUtc);
|
.Count(r => r.ScanTime >= startUtc && r.ScanTime < endUtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +144,7 @@ public class RfidService : IRfidService
|
||||||
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
||||||
|
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return await db.ScanRecords
|
return await db.LunchOrderTransactions
|
||||||
.CountAsync(r => r.ScanTime >= startUtc && r.ScanTime < endUtc, cancellationToken)
|
.CountAsync(r => r.ScanTime >= startUtc && r.ScanTime < endUtc, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +152,7 @@ public class RfidService : IRfidService
|
||||||
public async Task<int> GetTotalScanCountAsync(CancellationToken cancellationToken = default)
|
public async Task<int> GetTotalScanCountAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return await db.ScanRecords.CountAsync(cancellationToken).ConfigureAwait(false);
|
return await db.LunchOrderTransactions.CountAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> GetTotalScanCountForCardAsync(string cardId, CancellationToken cancellationToken = default)
|
public async Task<int> GetTotalScanCountForCardAsync(string cardId, CancellationToken cancellationToken = default)
|
||||||
|
|
@ -163,7 +163,7 @@ public class RfidService : IRfidService
|
||||||
cardId = cardId.Trim();
|
cardId = cardId.Trim();
|
||||||
|
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return await db.ScanRecords
|
return await db.LunchOrderTransactions
|
||||||
.CountAsync(r => r.CardId == cardId, cancellationToken)
|
.CountAsync(r => r.CardId == cardId, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +183,7 @@ public class RfidService : IRfidService
|
||||||
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
var endUtc = startOfTomorrowLocal.ToUniversalTime();
|
||||||
|
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
await using var db = await _dbFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return await db.ScanRecords
|
return await db.LunchOrderTransactions
|
||||||
.CountAsync(r => r.CardId == cardId && r.ScanTime >= startUtc && r.ScanTime < endUtc, cancellationToken)
|
.CountAsync(r => r.CardId == cardId && r.ScanTime >= startUtc && r.ScanTime < endUtc, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Net.Http;
|
||||||
namespace UtopiaCanteenSystem.Services;
|
namespace UtopiaCanteenSystem.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scheduled sync: fetches unsynced ScanRecords, POSTs them to the configured API,
|
/// Scheduled sync: fetches unsynced lunch_order_transactions, POSTs them to the configured API,
|
||||||
/// and deletes uploaded records from local SQLite on success.
|
/// and deletes uploaded records from local SQLite on success.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SyncService : ISyncService
|
public class SyncService : ISyncService
|
||||||
|
|
@ -32,7 +32,7 @@ public class SyncService : ISyncService
|
||||||
List<ScanRecord> toSync;
|
List<ScanRecord> toSync;
|
||||||
using (var db = _dbFactory.CreateDbContext())
|
using (var db = _dbFactory.CreateDbContext())
|
||||||
{
|
{
|
||||||
toSync = await db.ScanRecords
|
toSync = await db.LunchOrderTransactions
|
||||||
.Where(r => !r.IsSynced)
|
.Where(r => !r.IsSynced)
|
||||||
.OrderBy(r => r.ScanTime)
|
.OrderBy(r => r.ScanTime)
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
|
|
@ -63,12 +63,12 @@ public class SyncService : ISyncService
|
||||||
var ids = toSync.Select(r => r.Id).ToList();
|
var ids = toSync.Select(r => r.Id).ToList();
|
||||||
using (var db = _dbFactory.CreateDbContext())
|
using (var db = _dbFactory.CreateDbContext())
|
||||||
{
|
{
|
||||||
var records = await db.ScanRecords
|
var records = await db.LunchOrderTransactions
|
||||||
.Where(r => ids.Contains(r.Id))
|
.Where(r => ids.Contains(r.Id))
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
// On successful upload, delete uploaded scan records from local SQLite.
|
// On successful upload, delete uploaded scan events from local SQLite.
|
||||||
db.ScanRecords.RemoveRange(records);
|
db.LunchOrderTransactions.RemoveRange(records);
|
||||||
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
|
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -454,11 +454,13 @@
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" MinWidth="0"/>
|
<ColumnDefinition Width="*" MinWidth="0"/>
|
||||||
<ColumnDefinition Width="110"/>
|
<ColumnDefinition Width="110"/>
|
||||||
|
<ColumnDefinition Width="140"/>
|
||||||
<ColumnDefinition Width="90"/>
|
<ColumnDefinition Width="90"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Text="Employee" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}"/>
|
<TextBlock Grid.Column="0" Text="Employee" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}"/>
|
||||||
<TextBlock Grid.Column="1" Text="Scan ID" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}" HorizontalAlignment="Center"/>
|
<TextBlock Grid.Column="1" Text="Scan ID" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}" HorizontalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="2" Text="Time" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}" HorizontalAlignment="Right"/>
|
<TextBlock Grid.Column="2" Text="Order" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}" HorizontalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Column="3" Text="Time" FontSize="12" FontWeight="SemiBold" Foreground="{StaticResource MutedTextBrush}" HorizontalAlignment="Right"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ItemsControl ItemsSource="{Binding TodayOrderHistory}">
|
<ItemsControl ItemsSource="{Binding TodayOrderHistory}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
|
|
@ -468,18 +470,17 @@
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" MinWidth="0"/>
|
<ColumnDefinition Width="*" MinWidth="0"/>
|
||||||
<ColumnDefinition Width="110"/>
|
<ColumnDefinition Width="110"/>
|
||||||
|
<ColumnDefinition Width="140"/>
|
||||||
<ColumnDefinition Width="90"/>
|
<ColumnDefinition Width="90"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<Border Width="36" Height="36" Margin="0,0,10,0" CornerRadius="18" Background="{StaticResource AccentLightBrush}" BorderBrush="{StaticResource AccentBrush}" BorderThickness="1">
|
<Border Width="36" Height="36" Margin="0,0,10,0" CornerRadius="18" Background="{StaticResource AccentLightBrush}" BorderBrush="{StaticResource AccentBrush}" BorderThickness="1">
|
||||||
<TextBlock Text="—" FontSize="14" Foreground="{StaticResource AccentBrush}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<TextBlock Text="—" FontSize="14" Foreground="{StaticResource AccentBrush}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</Border>
|
</Border>
|
||||||
<StackPanel VerticalAlignment="Center">
|
<StackPanel VerticalAlignment="Center">
|
||||||
<!-- Employee column: show EmployeeId (top) + EmployeeName (middle) + Order item (bottom, muted) -->
|
<TextBlock Text="{Binding EmployeeId}" FontSize="14" FontWeight="Bold" Foreground="{StaticResource TitleTextBrush}" TextTrimming="CharacterEllipsis"/>
|
||||||
<TextBlock Text="{Binding EmployeeId}" FontSize="14" FontWeight="Bold" Foreground="{StaticResource TitleTextBrush}" TextTrimming="CharacterEllipsis"/>
|
<TextBlock Text="{Binding EmployeeName}" FontSize="12" Foreground="{StaticResource MutedTextBrush}" TextTrimming="CharacterEllipsis" Margin="0,1,0,0"/>
|
||||||
<TextBlock Text="{Binding EmployeeName}" FontSize="12" Foreground="{StaticResource MutedTextBrush}" TextTrimming="CharacterEllipsis" Margin="0,1,0,0"/>
|
</StackPanel>
|
||||||
<TextBlock Text="{Binding OrderItem, StringFormat='Order: {0}'}" FontSize="11" Foreground="{StaticResource MutedTextBrush}" TextTrimming="CharacterEllipsis" Margin="0,1,0,0"/>
|
|
||||||
</StackPanel>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
Text="{Binding ScanId}"
|
Text="{Binding ScanId}"
|
||||||
|
|
@ -489,7 +490,15 @@
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
TextTrimming="CharacterEllipsis"
|
TextTrimming="CharacterEllipsis"
|
||||||
MaxWidth="110"/>
|
MaxWidth="110"/>
|
||||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center">
|
<TextBlock Grid.Column="2"
|
||||||
|
Text="{Binding OrderItem}"
|
||||||
|
FontSize="13"
|
||||||
|
Foreground="{StaticResource TitleTextBrush}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
MaxWidth="140"/>
|
||||||
|
<StackPanel Grid.Column="3" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{Binding TimeDisplay}" FontSize="13" FontWeight="Bold" Foreground="{StaticResource TitleTextBrush}"/>
|
<TextBlock Text="{Binding TimeDisplay}" FontSize="13" FontWeight="Bold" Foreground="{StaticResource TitleTextBrush}"/>
|
||||||
<TextBlock Text="{Binding RelativeDateLabel}" FontSize="11" Foreground="{StaticResource MutedTextBrush}" Margin="0,1,0,0"/>
|
<TextBlock Text="{Binding RelativeDateLabel}" FontSize="11" Foreground="{StaticResource MutedTextBrush}" Margin="0,1,0,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue