This commit is contained in:
Phuoc Nguyen
2025-11-14 11:50:40 +07:00
parent 0093b62c29
commit 4738553d2e
6 changed files with 1270 additions and 432 deletions

View File

@@ -193,6 +193,7 @@ class AuthRemoteDataSource {
'customer': 1,
},
'limit_page_length': 0,
'order_by': 'custom_line_no asc'
},
options: Options(
headers: {

View File

@@ -71,14 +71,50 @@ class _BusinessUnitSelectionPageState extends State<BusinessUnitSelectionPage> {
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
const BusinessUnit(
id: '2',
code: 'HSKD',
name: 'HSKD',
description: 'Đơn vị kinh doanh HSKD',
),
const BusinessUnit(
id: '3',
code: 'LPKD',
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
const BusinessUnit(
id: '2',
code: 'HSKD',
name: 'HSKD',
description: 'Đơn vị kinh doanh HSKD',
),
const BusinessUnit(
id: '3',
code: 'LPKD',
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
const BusinessUnit(
id: '2',
code: 'HSKD',
name: 'HSKD',
description: 'Đơn vị kinh doanh HSKD',
),
const BusinessUnit(
id: '3',
code: 'LPKD',
name: 'LPKD',
description: 'Đơn vị kinh doanh LPKD',
),
];
}
void _handleContinue() {
if (_selectedUnit == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Vui lòng chọn đơn vị kinh doanh'),
const SnackBar(
content: Text('Vui lòng chọn đơn vị kinh doanh'),
backgroundColor: AppColors.danger,
),
);
@@ -146,249 +182,252 @@ class _BusinessUnitSelectionPageState extends State<BusinessUnitSelectionPage> {
const SizedBox(width: AppSpacing.sm),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(AppSpacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo Section
Center(
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.primaryBlue, AppColors.lightBlue],
body: Padding(
padding: const EdgeInsets.all(AppSpacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo Section
Center(
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.primaryBlue, AppColors.lightBlue],
),
borderRadius: BorderRadius.circular(20),
),
child: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'DBIZ',
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.w700,
),
),
borderRadius: BorderRadius.circular(20),
),
child: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'DBIZ',
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.w700,
),
),
Text(
'Worker App',
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
),
Text(
'Worker App',
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
),
),
),
const SizedBox(height: AppSpacing.xl),
const SizedBox(height: AppSpacing.xl),
// Welcome Message
const Text(
'Chọn đơn vị kinh doanh để tiếp tục',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.grey500, fontSize: 14),
// Welcome Message
const Text(
'Chọn đơn vị kinh doanh để tiếp tục',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.grey500, fontSize: 14),
),
const SizedBox(height: 40),
const Padding(
padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm),
child: Text(
'Đơn vị kinh doanh',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.grey900,
),
),
),
const SizedBox(height: AppSpacing.md),
const SizedBox(height: 40),
// Business Unit Selection List
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm),
child: Text(
'Đơn vị kinh doanh',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.grey900,
),
),
),
const SizedBox(height: AppSpacing.md),
// Business Unit List Tiles
...(_availableUnits.asMap().entries.map((entry) {
final index = entry.key;
final unit = entry.value;
final isSelected = _selectedUnit?.id == unit.id;
final isFirst = index == 0;
final isLast = index == _availableUnits.length - 1;
return Container(
margin: EdgeInsets.only(
bottom: isLast ? 0 : AppSpacing.xs,
),
decoration: BoxDecoration(
color: AppColors.white,
border: Border.all(
color: isSelected
? AppColors.primaryBlue
: AppColors.grey100,
width: isSelected ? 2 : 1,
// Business Unit Selection List
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Business Unit List Tiles
...(_availableUnits.asMap().entries.map((entry) {
final index = entry.key;
final unit = entry.value;
final isSelected = _selectedUnit?.id == unit.id;
final isFirst = index == 0;
final isLast = index == _availableUnits.length - 1;
return Container(
margin: EdgeInsets.only(
bottom: isLast ? 0 : AppSpacing.xs,
),
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
boxShadow: isSelected
? [
BoxShadow(
color: AppColors.primaryBlue.withValues(
alpha: 0.1,
),
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
child: InkWell(
onTap: () {
setState(() {
_selectedUnit = unit;
});
},
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.md,
decoration: BoxDecoration(
color: AppColors.white,
border: Border.all(
color: isSelected
? AppColors.primaryBlue
: AppColors.grey100,
width: isSelected ? 2 : 1,
),
child: Row(
children: [
// Icon
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryBlue.withValues(
alpha: 0.1,
)
: AppColors.grey50,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.business,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey500,
size: 20,
),
),
const SizedBox(width: AppSpacing.md),
// Unit Name
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
unit.name,
style: TextStyle(
fontSize: 16,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey900,
),
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
boxShadow: isSelected
? [
BoxShadow(
color: AppColors.primaryBlue.withValues(
alpha: 0.1,
),
if (unit.description != null) ...[
const SizedBox(height: 2),
Text(
unit.description!,
style: const TextStyle(
fontSize: 12,
color: AppColors.grey500,
),
),
],
],
),
),
// Radio indicator
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
child: InkWell(
onTap: () {
setState(() {
_selectedUnit = unit;
});
},
borderRadius: BorderRadius.vertical(
top: isFirst
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
bottom: isLast
? const Radius.circular(
InputFieldSpecs.borderRadius,
)
: Radius.zero,
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.md,
),
child: Row(
children: [
// Icon
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryBlue.withValues(
alpha: 0.1,
)
: AppColors.grey50,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.business,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey500,
width: 2,
size: 20,
),
color: isSelected
? AppColors.primaryBlue
: Colors.transparent,
),
child: isSelected
? const Icon(
Icons.circle,
size: 10,
color: AppColors.white,
)
: null,
),
],
const SizedBox(width: AppSpacing.md),
// Unit Name
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
unit.name,
style: TextStyle(
fontSize: 16,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? AppColors.primaryBlue
: AppColors.grey900,
),
),
if (unit.description != null) ...[
const SizedBox(height: 2),
Text(
unit.description!,
style: const TextStyle(
fontSize: 12,
color: AppColors.grey500,
),
),
],
],
),
),
// Radio indicator
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: isSelected
? AppColors.primaryBlue
: AppColors.grey500,
width: 2,
),
color: isSelected
? AppColors.primaryBlue
: Colors.transparent,
),
child: isSelected
? const Icon(
Icons.circle,
size: 10,
color: AppColors.white,
)
: null,
),
],
),
),
),
),
);
}).toList()),
],
),
const SizedBox(height: AppSpacing.xl),
// Continue Button
SizedBox(
height: ButtonSpecs.height,
child: ElevatedButton(
onPressed: _handleContinue,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
ButtonSpecs.borderRadius,
),
),
),
child: const Text(
'Tiếp tục',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
);
}).toList()),
],
),
),
],
),
),
const SizedBox(height: AppSpacing.xl),
// Continue Button
SizedBox(
height: ButtonSpecs.height,
child: ElevatedButton(
onPressed: _handleContinue,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
ButtonSpecs.borderRadius,
),
),
),
child: const Text(
'Tiếp tục',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
),
const SizedBox(height: AppSpacing.xl),
],
),
),
);