feat: Add custom skeleton loaders for employee list and daily progress report screens
This commit is contained in:
parent
97c873167f
commit
405916bb48
197
lib/helpers/widgets/my_custom_skeleton.dart
Normal file
197
lib/helpers/widgets/my_custom_skeleton.dart
Normal file
@ -0,0 +1,197 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:marco/helpers/widgets/my_card.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/utils/my_shadow.dart';
|
||||
|
||||
class SkeletonLoaders {
|
||||
// Employee List - Card Style
|
||||
static Widget employeeListSkeletonLoader() {
|
||||
return Column(
|
||||
children: List.generate(4, (index) {
|
||||
return MyCard.bordered(
|
||||
borderRadiusAll: 12,
|
||||
paddingAll: 10,
|
||||
margin: MySpacing.bottom(12),
|
||||
shadow: MyShadow(elevation: 3),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Avatar
|
||||
Container(
|
||||
width: 41,
|
||||
height: 41,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
MySpacing.width(16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(height: 14, width: 100, color: Colors.grey.shade300),
|
||||
MySpacing.width(8),
|
||||
Container(height: 12, width: 60, color: Colors.grey.shade300),
|
||||
],
|
||||
),
|
||||
MySpacing.height(8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.email, size: 16, color: Colors.grey.shade300),
|
||||
MySpacing.width(4),
|
||||
Container(height: 10, width: 140, color: Colors.grey.shade300),
|
||||
],
|
||||
),
|
||||
MySpacing.height(8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.phone, size: 16, color: Colors.grey.shade300),
|
||||
MySpacing.width(4),
|
||||
Container(height: 10, width: 100, color: Colors.grey.shade300),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Employee List - Compact Collapsed Style
|
||||
static Widget employeeListCollapsedSkeletonLoader() {
|
||||
return MyCard.bordered(
|
||||
borderRadiusAll: 4,
|
||||
paddingAll: 8,
|
||||
child: Column(
|
||||
children: List.generate(4, (index) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
// Avatar
|
||||
Container(
|
||||
width: 31,
|
||||
height: 31,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
MySpacing.width(16),
|
||||
// Name, Designation & Buttons
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(height: 12, width: 100, color: Colors.grey.shade300),
|
||||
MySpacing.height(8),
|
||||
Container(height: 10, width: 80, color: Colors.grey.shade300),
|
||||
MySpacing.height(12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(height: 28, width: 60, color: Colors.grey.shade300),
|
||||
MySpacing.width(8),
|
||||
Container(height: 28, width: 60, color: Colors.grey.shade300),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (index != 3)
|
||||
Divider(
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
thickness: 1,
|
||||
height: 1,
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Daily Progress Report Header Loader
|
||||
static Widget dailyProgressReportSkeletonLoader() {
|
||||
return MyCard.bordered(
|
||||
borderRadiusAll: 4,
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
|
||||
paddingAll: 8,
|
||||
child: Column(
|
||||
children: List.generate(3, (index) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(height: 14, width: 120, color: Colors.grey.shade300),
|
||||
Icon(Icons.add_circle, color: Colors.grey.shade300),
|
||||
],
|
||||
),
|
||||
if (index != 2) ...[
|
||||
MySpacing.height(12),
|
||||
Divider(color: Colors.grey.withOpacity(0.3), thickness: 1),
|
||||
MySpacing.height(12),
|
||||
],
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Daily Progress Planning (Collapsed View)
|
||||
static Widget dailyProgressPlanningSkeletonCollapsedOnly() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: List.generate(3, (index) {
|
||||
return MyCard.bordered(
|
||||
borderRadiusAll: 12,
|
||||
paddingAll: 16,
|
||||
margin: MySpacing.bottom(12),
|
||||
shadow: MyShadow(elevation: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
// Icon placeholder
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
MySpacing.width(12),
|
||||
// Text line
|
||||
Expanded(
|
||||
child: Container(height: 16, color: Colors.grey.shade300),
|
||||
),
|
||||
MySpacing.width(12),
|
||||
// Expand button placeholder
|
||||
Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import 'package:marco/model/attendance/attendence_action_button.dart';
|
||||
import 'package:marco/model/attendance/regualrize_action_button.dart';
|
||||
import 'package:marco/model/attendance/attendence_filter_sheet.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
|
||||
class AttendanceScreen extends StatefulWidget {
|
||||
AttendanceScreen({super.key});
|
||||
@ -304,7 +305,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
||||
),
|
||||
),
|
||||
if (isLoading)
|
||||
employeeListSkeletonLoader()
|
||||
SkeletonLoaders.employeeListSkeletonLoader()
|
||||
else if (employees.isEmpty)
|
||||
SizedBox(
|
||||
height: 120,
|
||||
@ -450,80 +451,6 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
||||
});
|
||||
}
|
||||
|
||||
Widget employeeListSkeletonLoader() {
|
||||
return MyCard.bordered(
|
||||
borderRadiusAll: 4,
|
||||
paddingAll: 8,
|
||||
child: Column(
|
||||
children: List.generate(4, (index) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
// Avatar placeholder
|
||||
Container(
|
||||
width: 31,
|
||||
height: 31,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
MySpacing.width(16),
|
||||
// Employee name/designation & buttons
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 12,
|
||||
width: 100,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
MySpacing.height(8),
|
||||
Container(
|
||||
height: 10,
|
||||
width: 80,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
MySpacing.height(12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
height: 28,
|
||||
width: 60,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
MySpacing.width(8),
|
||||
Container(
|
||||
height: 28,
|
||||
width: 60,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (index != 3)
|
||||
Divider(
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
thickness: 1,
|
||||
height: 1,
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget employeeLog() {
|
||||
return Obx(() {
|
||||
final logs = List.of(attendanceController.attendanceLogs);
|
||||
@ -574,7 +501,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
||||
),
|
||||
),
|
||||
if (attendanceController.isLoadingAttendanceLogs.value)
|
||||
employeeListSkeletonLoader()
|
||||
SkeletonLoaders.employeeListSkeletonLoader()
|
||||
else if (logs.isEmpty)
|
||||
SizedBox(
|
||||
height: 120,
|
||||
@ -759,7 +686,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
||||
Obx(() {
|
||||
final employees = attendanceController.regularizationLogs;
|
||||
if (attendanceController.isLoadingRegularizationLogs.value) {
|
||||
return employeeListSkeletonLoader();
|
||||
return SkeletonLoaders.employeeListSkeletonLoader();
|
||||
}
|
||||
|
||||
if (employees.isEmpty) {
|
||||
|
@ -14,7 +14,7 @@ import 'package:marco/controller/dashboard/employees_screen_controller.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/model/employees/employee_detail_bottom_sheet.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
class EmployeesScreen extends StatefulWidget {
|
||||
const EmployeesScreen({super.key});
|
||||
|
||||
@ -292,7 +292,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
final isLoading = employeeScreenController.isLoading.value;
|
||||
final employees = employeeScreenController.employees;
|
||||
if (isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return SkeletonLoaders.employeeListSkeletonLoader();
|
||||
}
|
||||
if (employees.isEmpty) {
|
||||
return Padding(
|
||||
|
@ -14,6 +14,7 @@ import 'package:marco/model/dailyTaskPlaning/daily_progress_report_filter.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/task_action_buttons.dart';
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
|
||||
class DailyProgressReportScreen extends StatefulWidget {
|
||||
const DailyProgressReportScreen({super.key});
|
||||
@ -297,7 +298,7 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
||||
final groupedTasks = dailyTaskController.groupedDailyTasks;
|
||||
|
||||
if (isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return SkeletonLoaders.dailyProgressReportSkeletonLoader();
|
||||
}
|
||||
|
||||
if (groupedTasks.isEmpty) {
|
||||
|
@ -11,6 +11,7 @@ import 'package:marco/controller/task_planing/daily_task_planing_controller.dart
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:percent_indicator/percent_indicator.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart';
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
|
||||
class DailyTaskPlaningScreen extends StatefulWidget {
|
||||
DailyTaskPlaningScreen({super.key});
|
||||
@ -170,7 +171,7 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
final dailyTasks = dailyTaskPlaningController.dailyTasks;
|
||||
|
||||
if (isLoading) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
return SkeletonLoaders.dailyProgressPlanningSkeletonCollapsedOnly();
|
||||
}
|
||||
|
||||
if (dailyTasks.isEmpty) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user