loading indicater has been added
This commit is contained in:
@@ -51,19 +51,24 @@ class _FinanceScreenState extends State<FinanceScreen> {
|
|||||||
|
|
||||||
void _triggerLoad() {
|
void _triggerLoad() {
|
||||||
if (_employeeId != null && _employeeId!.isNotEmpty) {
|
if (_employeeId != null && _employeeId!.isNotEmpty) {
|
||||||
if (_financeBloc.state is FinanceInitial) {
|
_financeBloc.add(
|
||||||
_financeBloc.add(
|
LoadFinanceDataEvent(
|
||||||
LoadFinanceDataEvent(
|
employeeId: _employeeId!,
|
||||||
employeeId: _employeeId!,
|
category: currentCategory,
|
||||||
category: currentCategory,
|
month: selectedDate.month,
|
||||||
month: selectedDate.month,
|
year: selectedDate.year,
|
||||||
year: selectedDate.year,
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onRefresh() async {
|
||||||
|
_triggerLoad();
|
||||||
|
await _financeBloc.stream.firstWhere(
|
||||||
|
(state) => state is FinanceLoaded || state is FinanceError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
@@ -71,145 +76,153 @@ class _FinanceScreenState extends State<FinanceScreen> {
|
|||||||
child: Directionality(
|
child: Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: CustomScrollView(
|
child: RefreshIndicator(
|
||||||
controller: scrollController,
|
onRefresh: _onRefresh,
|
||||||
physics: const BouncingScrollPhysics(),
|
color: const Color(0xFF0A6B4A),
|
||||||
slivers: [
|
child: CustomScrollView(
|
||||||
SliverToBoxAdapter(
|
controller: scrollController,
|
||||||
child: SettingsBar(
|
physics: const AlwaysScrollableScrollPhysics(
|
||||||
selectedIndex: 0,
|
parent: BouncingScrollPhysics(),
|
||||||
showBackButton: false,
|
|
||||||
iconPaths: const [
|
|
||||||
'assets/images/user.svg',
|
|
||||||
'assets/images/ball.svg',
|
|
||||||
],
|
|
||||||
onTap: (index) {
|
|
||||||
if (index == 0) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const UserSettingsScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (index == 1) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const NotificationsScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
/// SUMMARY CARD
|
child: SettingsBar(
|
||||||
SliverToBoxAdapter(
|
selectedIndex: 0,
|
||||||
child: BlocBuilder<FinanceBloc, FinanceState>(
|
showBackButton: false,
|
||||||
buildWhen: (previous, current) => current is FinanceLoaded,
|
iconPaths: const [
|
||||||
builder: (context, state) {
|
'assets/images/user.svg',
|
||||||
String amount = "0";
|
'assets/images/ball.svg',
|
||||||
if (state is FinanceLoaded) {
|
],
|
||||||
amount = state.netSalary.toStringAsFixed(0);
|
onTap: (index) {
|
||||||
amount = amount.replaceAllMapped(
|
if (index == 0) {
|
||||||
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
|
Navigator.push(
|
||||||
(Match m) => '${m[1]},',
|
context,
|
||||||
);
|
MaterialPageRoute(
|
||||||
}
|
builder: (context) => const UserSettingsScreen(),
|
||||||
|
),
|
||||||
return FinanceSummaryCard(
|
|
||||||
totalAmount: amount,
|
|
||||||
currentCategory: currentCategory,
|
|
||||||
onCalendarTap: () async {
|
|
||||||
final date = await showDatePicker(
|
|
||||||
context: context,
|
|
||||||
initialDate: selectedDate,
|
|
||||||
firstDate: DateTime(2020),
|
|
||||||
lastDate: DateTime(2030),
|
|
||||||
);
|
);
|
||||||
if (date != null && mounted) {
|
} else if (index == 1) {
|
||||||
setState(() => selectedDate = date);
|
Navigator.push(
|
||||||
if (_employeeId != null && _employeeId!.isNotEmpty) {
|
context,
|
||||||
_financeBloc.add(
|
MaterialPageRoute(
|
||||||
LoadFinanceDataEvent(
|
builder: (context) => const NotificationsScreen(),
|
||||||
employeeId: _employeeId!,
|
),
|
||||||
category: currentCategory,
|
);
|
||||||
month: selectedDate.month,
|
}
|
||||||
year: selectedDate.year,
|
},
|
||||||
),
|
),
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onCategoryChanged: (category) {
|
|
||||||
if (category != null) {
|
|
||||||
setState(() => currentCategory = category);
|
|
||||||
if (_employeeId != null && _employeeId!.isNotEmpty) {
|
|
||||||
_financeBloc.add(
|
|
||||||
LoadFinanceDataEvent(
|
|
||||||
employeeId: _employeeId!,
|
|
||||||
category: currentCategory,
|
|
||||||
month: selectedDate.month,
|
|
||||||
year: selectedDate.year,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
||||||
|
|
||||||
/// DATA LIST
|
/// SUMMARY CARD
|
||||||
BlocBuilder<FinanceBloc, FinanceState>(
|
SliverToBoxAdapter(
|
||||||
builder: (context, state) {
|
child: BlocBuilder<FinanceBloc, FinanceState>(
|
||||||
if (state is FinanceLoading || state is FinanceInitial) {
|
buildWhen: (previous, current) => current is FinanceLoaded,
|
||||||
return const SliverToBoxAdapter(
|
builder: (context, state) {
|
||||||
child: Center(
|
String amount = "0";
|
||||||
child: Padding(
|
if (state is FinanceLoaded) {
|
||||||
padding: EdgeInsets.all(20.0),
|
amount = state.netSalary.toStringAsFixed(0);
|
||||||
child: CircularProgressIndicator(),
|
amount = amount.replaceAllMapped(
|
||||||
),
|
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
|
||||||
),
|
(Match m) => '${m[1]},',
|
||||||
);
|
);
|
||||||
} else if (state is FinanceLoaded) {
|
}
|
||||||
if (state.records.isEmpty) {
|
|
||||||
|
return FinanceSummaryCard(
|
||||||
|
totalAmount: amount,
|
||||||
|
currentCategory: currentCategory,
|
||||||
|
onCalendarTap: () async {
|
||||||
|
final date = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: selectedDate,
|
||||||
|
firstDate: DateTime(2020),
|
||||||
|
lastDate: DateTime(2030),
|
||||||
|
);
|
||||||
|
if (date != null && mounted) {
|
||||||
|
setState(() => selectedDate = date);
|
||||||
|
if (_employeeId != null &&
|
||||||
|
_employeeId!.isNotEmpty) {
|
||||||
|
_financeBloc.add(
|
||||||
|
LoadFinanceDataEvent(
|
||||||
|
employeeId: _employeeId!,
|
||||||
|
category: currentCategory,
|
||||||
|
month: selectedDate.month,
|
||||||
|
year: selectedDate.year,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCategoryChanged: (category) {
|
||||||
|
if (category != null) {
|
||||||
|
setState(() => currentCategory = category);
|
||||||
|
if (_employeeId != null &&
|
||||||
|
_employeeId!.isNotEmpty) {
|
||||||
|
_financeBloc.add(
|
||||||
|
LoadFinanceDataEvent(
|
||||||
|
employeeId: _employeeId!,
|
||||||
|
category: currentCategory,
|
||||||
|
month: selectedDate.month,
|
||||||
|
year: selectedDate.year,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// DATA LIST
|
||||||
|
BlocBuilder<FinanceBloc, FinanceState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is FinanceLoading || state is FinanceInitial) {
|
||||||
return const SliverToBoxAdapter(
|
return const SliverToBoxAdapter(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(20.0),
|
padding: EdgeInsets.all(20.0),
|
||||||
child: Text("لا توجد سجلات"),
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is FinanceLoaded) {
|
||||||
|
if (state.records.isEmpty) {
|
||||||
|
return const SliverToBoxAdapter(
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(20.0),
|
||||||
|
child: Text("لا توجد سجلات"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return WorkDayCard(record: state.records[index]);
|
||||||
|
}, childCount: state.records.length),
|
||||||
|
);
|
||||||
|
} else if (state is FinanceError) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(20.0),
|
||||||
|
child: Text(
|
||||||
|
state.message,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return SliverList(
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
},
|
||||||
return WorkDayCard(record: state.records[index]);
|
),
|
||||||
}, childCount: state.records.length),
|
|
||||||
);
|
|
||||||
} else if (state is FinanceError) {
|
|
||||||
return SliverToBoxAdapter(
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(20.0),
|
|
||||||
child: Text(
|
|
||||||
state.message,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -223,6 +223,10 @@ class _HolidayScreenState extends State<HolidayScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onRefresh() async {
|
||||||
|
await Future.wait([_loadVacationsFromAPI(), _loadAdvancesFromAPI()]);
|
||||||
|
}
|
||||||
|
|
||||||
AdvanceRequest _convertAdvanceToAdvanceRequest(AdvanceDataModel advance) {
|
AdvanceRequest _convertAdvanceToAdvanceRequest(AdvanceDataModel advance) {
|
||||||
// Convert state (0=waiting, 1=approved, 2=denied) to status string
|
// Convert state (0=waiting, 1=approved, 2=denied) to status string
|
||||||
String status = "waiting";
|
String status = "waiting";
|
||||||
@@ -248,123 +252,129 @@ class _HolidayScreenState extends State<HolidayScreen> {
|
|||||||
// ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW
|
// ⭐ MAIN CONTENT - CUSTOM SCROLL VIEW
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
SafeArea(
|
SafeArea(
|
||||||
child: CustomScrollView(
|
child: RefreshIndicator(
|
||||||
controller: _scrollController,
|
onRefresh: _onRefresh,
|
||||||
physics: const BouncingScrollPhysics(),
|
color: const Color(0xFF0A6B4A),
|
||||||
slivers: [
|
child: CustomScrollView(
|
||||||
// SETTINGS BAR
|
controller: _scrollController,
|
||||||
SliverToBoxAdapter(
|
physics: const AlwaysScrollableScrollPhysics(
|
||||||
child: Padding(
|
parent: BouncingScrollPhysics(),
|
||||||
padding: const EdgeInsets.only(top: 8),
|
),
|
||||||
// First, make sure you have your screens defined
|
slivers: [
|
||||||
|
// SETTINGS BAR
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
// First, make sure you have your screens defined
|
||||||
|
|
||||||
// Then in your main widget:
|
// Then in your main widget:
|
||||||
child: SettingsBar(
|
child: SettingsBar(
|
||||||
selectedIndex: 0,
|
selectedIndex: 0,
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
iconPaths: [
|
iconPaths: [
|
||||||
'assets/images/user.svg',
|
'assets/images/user.svg',
|
||||||
'assets/images/ball.svg',
|
'assets/images/ball.svg',
|
||||||
],
|
],
|
||||||
onTap: (index) {
|
onTap: (index) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => UserSettingsScreen(),
|
builder: (context) => UserSettingsScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (index == 1) {
|
} else if (index == 1) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => NotificationsScreen(),
|
builder: (context) => NotificationsScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
const SliverToBoxAdapter(child: SizedBox(height: 5)),
|
||||||
|
|
||||||
// TABS SECTION
|
// TABS SECTION
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 55,
|
height: 55,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// الأجازات
|
// الأجازات
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => setState(() => activeTab = 1),
|
onTap: () => setState(() => activeTab = 1),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"الأجازات",
|
"الأجازات",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color:
|
color:
|
||||||
activeTab == 1
|
activeTab == 1
|
||||||
? const Color(0xFF8EFDC2)
|
? const Color(0xFF8EFDC2)
|
||||||
: const Color(0x9EFFFFFF),
|
: const Color(0x9EFFFFFF),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
if (activeTab == 1)
|
||||||
if (activeTab == 1)
|
Container(
|
||||||
Container(
|
width: 60,
|
||||||
width: 60,
|
height: 2,
|
||||||
height: 2,
|
margin: const EdgeInsets.only(top: 4),
|
||||||
margin: const EdgeInsets.only(top: 4),
|
color: const Color(0xFF8EFDC2),
|
||||||
color: const Color(0xFF8EFDC2),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(width: 70),
|
const SizedBox(width: 70),
|
||||||
|
|
||||||
// السلف
|
// السلف
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => setState(() => activeTab = 0),
|
onTap: () => setState(() => activeTab = 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"السلف",
|
"السلف",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color:
|
color:
|
||||||
activeTab == 0
|
activeTab == 0
|
||||||
? const Color(0xFF8EFDC2)
|
? const Color(0xFF8EFDC2)
|
||||||
: const Color(0x9EFFFFFF),
|
: const Color(0x9EFFFFFF),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
if (activeTab == 0)
|
||||||
if (activeTab == 0)
|
Container(
|
||||||
Container(
|
width: 60,
|
||||||
width: 60,
|
height: 2,
|
||||||
height: 2,
|
margin: const EdgeInsets.only(top: 4),
|
||||||
margin: const EdgeInsets.only(top: 4),
|
color: const Color(0xFF8EFDC2),
|
||||||
color: const Color(0xFF8EFDC2),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 20)),
|
const SliverToBoxAdapter(child: SizedBox(height: 20)),
|
||||||
|
|
||||||
// CONTENT LISTS
|
// CONTENT LISTS
|
||||||
activeTab == 1
|
activeTab == 1
|
||||||
? _buildLeaveRequestsSliver()
|
? _buildLeaveRequestsSliver()
|
||||||
: _buildAdvanceRequestsSliver(),
|
: _buildAdvanceRequestsSliver(),
|
||||||
|
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
const SliverToBoxAdapter(child: SizedBox(height: 120)),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user