feat(client): 登录/注册页照原型重建,ds 真相源组件族统一全部屏
- 登录/注册(login.html/register.html 1:1):两栏卡片+品牌渐变面板+主题小衣服 pill(onSurface 变体)+记住我(记录并预填最近账号)+原型式 toast 校验; 登录 fidelity 1.4–2.1% 三主题全绿;注册暂不入闸(少 门店编号/兑换券 字段, 已记 CONTRACT,screens.mjs 留存根) - ds 原子补齐:DsToast(.toast 单例)/DsCheck(.check/.agree)/DsButton lg 档/ DsSelect 替换全部旧 DropdownButton/DsField label 在上/DsInput 后缀与密码形态 - 全屏统一:对话框按钮全 DsButton、盒式输入主题钉死(visualDensity.standard、 h38、InputDecorationTheme 渗漏修复)、图标全 lucide、JetBrains Mono 三端同源、 BrandMark 真相源 logo、只读模式写操作全量守卫(WriteGuard+DsToast) - 出入库列表:版式对齐原型(卡片对齐+搜索框居中)、KPI 近30天滚动、结清后 失效财务应收应付表;商品编辑抽屉介绍库改搜索下拉、图片双击全屏预览 - 删除旧 UI 死代码:DataTableCard/FormDialog/PageScaffold/SearchChip/ SelectProductDialog/tabStateProvider - golden/fidelity:stock-in/out 补注册(9%)、login 入册(8%)、goldens 全量重打; 修复 pubspec flutter_web_plugins 非法声明(CI pub get 阻断) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -40,6 +40,17 @@ class FinanceRecord {
|
||||
}
|
||||
}
|
||||
|
||||
/// 单据抬头(近期单据列表用):收付款显示类型,应收/应付回溯到来源单据
|
||||
String get docTitle {
|
||||
final ref = switch (refType) {
|
||||
'stock_in' => '入库单',
|
||||
'stock_out' => '出库单',
|
||||
_ => null,
|
||||
};
|
||||
if (ref != null) return ref;
|
||||
return typeLabel;
|
||||
}
|
||||
|
||||
factory FinanceRecord.fromJson(Map<String, dynamic> json) => FinanceRecord(
|
||||
id: (json['id'] as num).toInt(),
|
||||
type: json['type'] as String,
|
||||
@@ -47,13 +58,57 @@ class FinanceRecord {
|
||||
balance: (json['balance'] as num).toDouble(),
|
||||
status: json['status'] as String? ?? 'open',
|
||||
refType: json['ref_type'] as String?,
|
||||
refId:
|
||||
json['ref_id'] != null ? (json['ref_id'] as num).toInt() : null,
|
||||
refId: json['ref_id'] != null ? (json['ref_id'] as num).toInt() : null,
|
||||
partnerName:
|
||||
(json['partner'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
partnerId:
|
||||
json['partner_id'] != null ? (json['partner_id'] as num).toInt() : null,
|
||||
partnerId: json['partner_id'] != null
|
||||
? (json['partner_id'] as num).toInt()
|
||||
: null,
|
||||
recordDate: json['record_date'] as String?,
|
||||
remark: json['remark'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// GET /finance/summary 行:按往来单位分组的未结清应收/应付聚合
|
||||
class PartnerFinanceSummary {
|
||||
final int? partnerId;
|
||||
final String partnerName;
|
||||
final String type; // payable | receivable
|
||||
final int recordCount;
|
||||
final double totalAmount;
|
||||
|
||||
const PartnerFinanceSummary({
|
||||
this.partnerId,
|
||||
this.partnerName = '',
|
||||
required this.type,
|
||||
this.recordCount = 0,
|
||||
required this.totalAmount,
|
||||
});
|
||||
|
||||
factory PartnerFinanceSummary.fromJson(Map<String, dynamic> json) =>
|
||||
PartnerFinanceSummary(
|
||||
partnerId: json['partner_id'] != null
|
||||
? (json['partner_id'] as num).toInt()
|
||||
: null,
|
||||
partnerName: json['partner_name'] as String? ?? '',
|
||||
type: json['type'] as String,
|
||||
recordCount: (json['record_count'] as num?)?.toInt() ?? 0,
|
||||
totalAmount: (json['total_amount'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
/// GET /finance/trend 点:月度收支(in=销售额、out=采购额,单位元)。
|
||||
class TrendPoint {
|
||||
final String month; // "2026-01"
|
||||
final double inAmount;
|
||||
final double outAmount;
|
||||
|
||||
const TrendPoint(
|
||||
{required this.month, required this.inAmount, required this.outAmount});
|
||||
|
||||
factory TrendPoint.fromJson(Map<String, dynamic> json) => TrendPoint(
|
||||
month: json['month'] as String? ?? '',
|
||||
inAmount: (json['in'] as num?)?.toDouble() ?? 0,
|
||||
outAmount: (json['out'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,12 +79,19 @@ class InventorySummary {
|
||||
final double inStockQty;
|
||||
final int shortageCount;
|
||||
final int warningCount;
|
||||
// 月初快照(KPI 环比):由 inventory_logs 反推
|
||||
final int lastMonthSku;
|
||||
final double lastMonthQty;
|
||||
final double lastMonthValue;
|
||||
const InventorySummary({
|
||||
this.skuCount = 0,
|
||||
this.stockValue = 0,
|
||||
this.inStockQty = 0,
|
||||
this.shortageCount = 0,
|
||||
this.warningCount = 0,
|
||||
this.lastMonthSku = 0,
|
||||
this.lastMonthQty = 0,
|
||||
this.lastMonthValue = 0,
|
||||
});
|
||||
factory InventorySummary.fromJson(Map<String, dynamic> j) => InventorySummary(
|
||||
skuCount: (j['sku_count'] as num?)?.toInt() ?? 0,
|
||||
@@ -92,6 +99,9 @@ class InventorySummary {
|
||||
inStockQty: (j['in_stock_qty'] as num?)?.toDouble() ?? 0,
|
||||
shortageCount: (j['shortage_count'] as num?)?.toInt() ?? 0,
|
||||
warningCount: (j['warning_count'] as num?)?.toInt() ?? 0,
|
||||
lastMonthSku: (j['last_month_sku'] as num?)?.toInt() ?? 0,
|
||||
lastMonthQty: (j['last_month_qty'] as num?)?.toDouble() ?? 0,
|
||||
lastMonthValue: (j['last_month_value'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -138,8 +148,7 @@ class InventoryLog {
|
||||
? (json['qty_after'] as num).toDouble()
|
||||
: null,
|
||||
refType: json['ref_type'] as String?,
|
||||
refId:
|
||||
json['ref_id'] != null ? (json['ref_id'] as num).toInt() : null,
|
||||
refId: json['ref_id'] != null ? (json['ref_id'] as num).toInt() : null,
|
||||
createdAt: json['created_at'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ class LicenseInfo {
|
||||
id: (json['id'] as num?)?.toInt() ?? 0,
|
||||
type: json['type'] as String? ?? 'trial',
|
||||
isActive: json['is_active'] as bool? ?? false,
|
||||
maxDevices:
|
||||
(json['max_devices'] as num?)?.toInt() ?? AppConstants.defaultMaxDevices,
|
||||
maxDevices: (json['max_devices'] as num?)?.toInt() ??
|
||||
AppConstants.defaultMaxDevices,
|
||||
expiresAt: json['expires_at'] != null
|
||||
? DateTime.tryParse(json['expires_at'] as String)
|
||||
: null,
|
||||
@@ -44,8 +44,7 @@ class LicenseInfo {
|
||||
}
|
||||
}
|
||||
|
||||
bool get isExpired =>
|
||||
expiresAt != null && DateTime.now().isAfter(expiresAt!);
|
||||
bool get isExpired => expiresAt != null && DateTime.now().isAfter(expiresAt!);
|
||||
|
||||
int? get daysRemaining {
|
||||
if (expiresAt == null) return null;
|
||||
@@ -62,5 +61,6 @@ class LicenseInfo {
|
||||
|
||||
bool get isReadOnlyPhase => phase == 'readonly' || phase == 'locked';
|
||||
bool get isLockedPhase => phase == 'locked';
|
||||
bool get needsAttention => phase == 'grace' || phase == 'readonly' || phase == 'locked';
|
||||
bool get needsAttention =>
|
||||
phase == 'grace' || phase == 'readonly' || phase == 'locked';
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ class NumberRule {
|
||||
|
||||
String get exampleNo {
|
||||
final now = DateTime.now();
|
||||
final date = '${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}';
|
||||
final date =
|
||||
'${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}';
|
||||
return '$prefix$date${(currentNo + 1).toString().padLeft(3, '0')}';
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ class Partner {
|
||||
final int id;
|
||||
final String? code;
|
||||
final String name;
|
||||
final String type; // supplier | customer
|
||||
final String type; // supplier | customer | supplier,customer(两者)
|
||||
final String? contact;
|
||||
final String? phone;
|
||||
final String? address;
|
||||
final String? bankAccount;
|
||||
final double balance; // 期初余额(存档字段,不参与应收应付聚合)
|
||||
final String status;
|
||||
final String? remark;
|
||||
|
||||
@@ -19,10 +20,19 @@ class Partner {
|
||||
this.phone,
|
||||
this.address,
|
||||
this.bankAccount,
|
||||
this.balance = 0,
|
||||
this.status = 'enabled',
|
||||
this.remark,
|
||||
});
|
||||
|
||||
/// 类型徽章文案(原型 b-供应商/b-客户/b-两者):SET 双值 = 两者
|
||||
String get typeLabel {
|
||||
final isSupplier = type.contains('supplier');
|
||||
final isCustomer = type.contains('customer');
|
||||
if (isSupplier && isCustomer) return '两者';
|
||||
return isCustomer ? '客户' : '供应商';
|
||||
}
|
||||
|
||||
factory Partner.fromJson(Map<String, dynamic> json) => Partner(
|
||||
id: (json['id'] as num).toInt(),
|
||||
code: json['code'] as String?,
|
||||
@@ -32,6 +42,7 @@ class Partner {
|
||||
phone: json['phone'] as String?,
|
||||
address: json['address'] as String?,
|
||||
bankAccount: json['bank_account'] as String?,
|
||||
balance: (json['balance'] as num?)?.toDouble() ?? 0,
|
||||
status: json['status'] as String? ?? 'enabled',
|
||||
remark: json['remark'] as String?,
|
||||
);
|
||||
|
||||
@@ -82,12 +82,18 @@ class Product {
|
||||
?.map((e) => ProductImage.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
originName: (json['origin'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
shelfLifeName: (json['shelf_life'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
storageName: (json['storage'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
categoryName: (json['category'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
descriptionDocTitle: (json['description_doc'] as Map<String, dynamic>?)?['title'] as String?,
|
||||
descriptionDocContent: (json['description_doc'] as Map<String, dynamic>?)?['content'] as String?,
|
||||
originName:
|
||||
(json['origin'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
shelfLifeName:
|
||||
(json['shelf_life'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
storageName:
|
||||
(json['storage'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
categoryName:
|
||||
(json['category'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
descriptionDocTitle: (json['description_doc']
|
||||
as Map<String, dynamic>?)?['title'] as String?,
|
||||
descriptionDocContent: (json['description_doc']
|
||||
as Map<String, dynamic>?)?['content'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
||||
@@ -52,29 +52,30 @@ class StockInItem {
|
||||
}
|
||||
|
||||
return StockInItem(
|
||||
id: json['id'] != null ? (json['id'] as num).toInt() : null,
|
||||
orderId: json['order_id'] != null
|
||||
? (json['order_id'] as num).toInt()
|
||||
: null,
|
||||
productId: (json['product_id'] as num).toInt(),
|
||||
quantity: (json['quantity'] as num).toDouble(),
|
||||
unitPrice: (json['unit_price'] as num).toDouble(),
|
||||
totalPrice: (json['total_price'] as num).toDouble(),
|
||||
returnedQuantity: (json['returned_quantity'] as num?)?.toDouble() ?? 0,
|
||||
batchNo: json['batch_no'] as String?,
|
||||
productionDate: json['production_date'] as String?,
|
||||
productName: lineOrProduct('product_name', 'name'),
|
||||
productCode: lineOrProduct('product_code', 'code'),
|
||||
productSeries: lineOrProduct('series', 'series'),
|
||||
productSpec: lineOrProduct('spec', 'spec'),
|
||||
productUnit: (json['product'] as Map<String, dynamic>?)?['unit'] as String?,
|
||||
productOrigin: ((json['product'] as Map<String, dynamic>?)?['origin']
|
||||
as Map<String, dynamic>?)?['name'] as String?,
|
||||
productShelfLife: ((json['product'] as Map<String, dynamic>?)?['shelf_life']
|
||||
as Map<String, dynamic>?)?['name'] as String?,
|
||||
productStorage: ((json['product'] as Map<String, dynamic>?)?['storage']
|
||||
as Map<String, dynamic>?)?['name'] as String?,
|
||||
);
|
||||
id: json['id'] != null ? (json['id'] as num).toInt() : null,
|
||||
orderId:
|
||||
json['order_id'] != null ? (json['order_id'] as num).toInt() : null,
|
||||
productId: (json['product_id'] as num).toInt(),
|
||||
quantity: (json['quantity'] as num).toDouble(),
|
||||
unitPrice: (json['unit_price'] as num).toDouble(),
|
||||
totalPrice: (json['total_price'] as num).toDouble(),
|
||||
returnedQuantity: (json['returned_quantity'] as num?)?.toDouble() ?? 0,
|
||||
batchNo: json['batch_no'] as String?,
|
||||
productionDate: json['production_date'] as String?,
|
||||
productName: lineOrProduct('product_name', 'name'),
|
||||
productCode: lineOrProduct('product_code', 'code'),
|
||||
productSeries: lineOrProduct('series', 'series'),
|
||||
productSpec: lineOrProduct('spec', 'spec'),
|
||||
productUnit:
|
||||
(json['product'] as Map<String, dynamic>?)?['unit'] as String?,
|
||||
productOrigin: ((json['product'] as Map<String, dynamic>?)?['origin']
|
||||
as Map<String, dynamic>?)?['name'] as String?,
|
||||
productShelfLife:
|
||||
((json['product'] as Map<String, dynamic>?)?['shelf_life']
|
||||
as Map<String, dynamic>?)?['name'] as String?,
|
||||
productStorage: ((json['product'] as Map<String, dynamic>?)?['storage']
|
||||
as Map<String, dynamic>?)?['name'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
@@ -133,19 +134,23 @@ class StockInOrder {
|
||||
orderNo: json['order_no'] as String,
|
||||
type: json['type'] as String?,
|
||||
warehouseId: (json['warehouse_id'] as num).toInt(),
|
||||
warehouseName: (json['warehouse'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
warehouseName:
|
||||
(json['warehouse'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
partnerId: json['partner_id'] != null
|
||||
? (json['partner_id'] as num).toInt()
|
||||
: null,
|
||||
partnerName: (json['partner'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
partnerName:
|
||||
(json['partner'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
operatorId: json['operator_id'] != null
|
||||
? (json['operator_id'] as num).toInt()
|
||||
: null,
|
||||
operatorName: (json['operator'] as Map<String, dynamic>?)?['real_name'] as String?,
|
||||
operatorName: (json['operator'] as Map<String, dynamic>?)?['real_name']
|
||||
as String?,
|
||||
reviewerId: json['reviewer_id'] != null
|
||||
? (json['reviewer_id'] as num).toInt()
|
||||
: null,
|
||||
reviewerName: (json['reviewer'] as Map<String, dynamic>?)?['real_name'] as String?,
|
||||
reviewerName: (json['reviewer'] as Map<String, dynamic>?)?['real_name']
|
||||
as String?,
|
||||
status: json['status'] as String,
|
||||
returnState: json['return_state'] as String? ?? 'none',
|
||||
orderDate: json['order_date'] as String?,
|
||||
@@ -156,8 +161,7 @@ class StockInOrder {
|
||||
remark: json['remark'] as String?,
|
||||
items: json['items'] != null
|
||||
? (json['items'] as List)
|
||||
.map((e) =>
|
||||
StockInItem.fromJson(e as Map<String, dynamic>))
|
||||
.map((e) => StockInItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: [],
|
||||
);
|
||||
|
||||
@@ -46,25 +46,25 @@ class StockOutItem {
|
||||
}
|
||||
|
||||
return StockOutItem(
|
||||
id: json['id'] != null ? (json['id'] as num).toInt() : null,
|
||||
orderId: json['order_id'] != null
|
||||
? (json['order_id'] as num).toInt()
|
||||
: null,
|
||||
productId: (json['product_id'] as num).toInt(),
|
||||
quantity: (json['quantity'] as num).toDouble(),
|
||||
unitPrice: (json['unit_price'] as num).toDouble(),
|
||||
salePrice: (json['sale_price'] as num?)?.toDouble() ?? 0,
|
||||
totalPrice: (json['total_price'] as num).toDouble(),
|
||||
returnedQuantity: (json['returned_quantity'] as num?)?.toDouble() ?? 0,
|
||||
productName: lineOrProduct('product_name', 'name'),
|
||||
productCode: lineOrProduct('product_code', 'code'),
|
||||
productSeries: lineOrProduct('series', 'series'),
|
||||
productSpec: lineOrProduct('spec', 'spec'),
|
||||
productUnit: (json['product'] as Map<String, dynamic>?)?['unit'] as String?,
|
||||
// 批次号/生产日期:优先明细行快照,回退 product 关联(App 新建单据快照列常为空)
|
||||
batchNo: lineOrProduct('batch_no', 'batch_no'),
|
||||
productionDate: lineOrProduct('production_date', 'production_date'),
|
||||
);
|
||||
id: json['id'] != null ? (json['id'] as num).toInt() : null,
|
||||
orderId:
|
||||
json['order_id'] != null ? (json['order_id'] as num).toInt() : null,
|
||||
productId: (json['product_id'] as num).toInt(),
|
||||
quantity: (json['quantity'] as num).toDouble(),
|
||||
unitPrice: (json['unit_price'] as num).toDouble(),
|
||||
salePrice: (json['sale_price'] as num?)?.toDouble() ?? 0,
|
||||
totalPrice: (json['total_price'] as num).toDouble(),
|
||||
returnedQuantity: (json['returned_quantity'] as num?)?.toDouble() ?? 0,
|
||||
productName: lineOrProduct('product_name', 'name'),
|
||||
productCode: lineOrProduct('product_code', 'code'),
|
||||
productSeries: lineOrProduct('series', 'series'),
|
||||
productSpec: lineOrProduct('spec', 'spec'),
|
||||
productUnit:
|
||||
(json['product'] as Map<String, dynamic>?)?['unit'] as String?,
|
||||
// 批次号/生产日期:优先明细行快照,回退 product 关联(App 新建单据快照列常为空)
|
||||
batchNo: lineOrProduct('batch_no', 'batch_no'),
|
||||
productionDate: lineOrProduct('production_date', 'production_date'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,19 +116,23 @@ class StockOutOrder {
|
||||
orderNo: json['order_no'] as String,
|
||||
type: json['type'] as String?,
|
||||
warehouseId: (json['warehouse_id'] as num).toInt(),
|
||||
warehouseName: (json['warehouse'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
warehouseName:
|
||||
(json['warehouse'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
partnerId: json['partner_id'] != null
|
||||
? (json['partner_id'] as num).toInt()
|
||||
: null,
|
||||
partnerName: (json['partner'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
partnerName:
|
||||
(json['partner'] as Map<String, dynamic>?)?['name'] as String?,
|
||||
operatorId: json['operator_id'] != null
|
||||
? (json['operator_id'] as num).toInt()
|
||||
: null,
|
||||
operatorName: (json['operator'] as Map<String, dynamic>?)?['real_name'] as String?,
|
||||
operatorName: (json['operator'] as Map<String, dynamic>?)?['real_name']
|
||||
as String?,
|
||||
reviewerId: json['reviewer_id'] != null
|
||||
? (json['reviewer_id'] as num).toInt()
|
||||
: null,
|
||||
reviewerName: (json['reviewer'] as Map<String, dynamic>?)?['real_name'] as String?,
|
||||
reviewerName: (json['reviewer'] as Map<String, dynamic>?)?['real_name']
|
||||
as String?,
|
||||
status: json['status'] as String,
|
||||
returnState: json['return_state'] as String? ?? 'none',
|
||||
orderDate: json['order_date'] as String?,
|
||||
@@ -140,8 +144,7 @@ class StockOutOrder {
|
||||
remark: json['remark'] as String?,
|
||||
items: json['items'] != null
|
||||
? (json['items'] as List)
|
||||
.map((e) =>
|
||||
StockOutItem.fromJson(e as Map<String, dynamic>))
|
||||
.map((e) => StockOutItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: [],
|
||||
);
|
||||
|
||||
@@ -24,10 +24,12 @@ class StockSummary {
|
||||
);
|
||||
|
||||
/// 笔数环比(%);上月为 0 时返回 null(无从比较)。
|
||||
double? get countDeltaPct =>
|
||||
lastMonthCount == 0 ? null : (monthCount - lastMonthCount) / lastMonthCount * 100;
|
||||
double? get countDeltaPct => lastMonthCount == 0
|
||||
? null
|
||||
: (monthCount - lastMonthCount) / lastMonthCount * 100;
|
||||
|
||||
/// 金额环比(%);上月为 0 时返回 null。
|
||||
double? get amountDeltaPct =>
|
||||
lastMonthAmount == 0 ? null : (monthAmount - lastMonthAmount) / lastMonthAmount * 100;
|
||||
double? get amountDeltaPct => lastMonthAmount == 0
|
||||
? null
|
||||
: (monthAmount - lastMonthAmount) / lastMonthAmount * 100;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ class AppUser {
|
||||
final String username;
|
||||
final String? realName;
|
||||
final String? phone;
|
||||
final String role; // admin | operator | readonly
|
||||
final String role; // superadmin | admin | operator | readonly
|
||||
final bool isActive;
|
||||
final String? createdAt;
|
||||
final String? lastLoginAt;
|
||||
|
||||
const AppUser({
|
||||
required this.id,
|
||||
@@ -15,6 +16,7 @@ class AppUser {
|
||||
required this.role,
|
||||
required this.isActive,
|
||||
this.createdAt,
|
||||
this.lastLoginAt,
|
||||
});
|
||||
|
||||
String get roleLabel {
|
||||
@@ -40,5 +42,6 @@ class AppUser {
|
||||
role: json['role'] as String? ?? 'operator',
|
||||
isActive: json['is_active'] as bool? ?? true,
|
||||
createdAt: json['created_at'] as String?,
|
||||
lastLoginAt: json['last_login_at'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user