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/regualrize_action_button.dart';
|
||||||
import 'package:marco/model/attendance/attendence_filter_sheet.dart';
|
import 'package:marco/model/attendance/attendence_filter_sheet.dart';
|
||||||
import 'package:marco/controller/project_controller.dart';
|
import 'package:marco/controller/project_controller.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||||
|
|
||||||
class AttendanceScreen extends StatefulWidget {
|
class AttendanceScreen extends StatefulWidget {
|
||||||
AttendanceScreen({super.key});
|
AttendanceScreen({super.key});
|
||||||
@ -304,7 +305,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isLoading)
|
if (isLoading)
|
||||||
employeeListSkeletonLoader()
|
SkeletonLoaders.employeeListSkeletonLoader()
|
||||||
else if (employees.isEmpty)
|
else if (employees.isEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 120,
|
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() {
|
Widget employeeLog() {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final logs = List.of(attendanceController.attendanceLogs);
|
final logs = List.of(attendanceController.attendanceLogs);
|
||||||
@ -574,7 +501,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (attendanceController.isLoadingAttendanceLogs.value)
|
if (attendanceController.isLoadingAttendanceLogs.value)
|
||||||
employeeListSkeletonLoader()
|
SkeletonLoaders.employeeListSkeletonLoader()
|
||||||
else if (logs.isEmpty)
|
else if (logs.isEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 120,
|
height: 120,
|
||||||
@ -759,7 +686,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
Obx(() {
|
Obx(() {
|
||||||
final employees = attendanceController.regularizationLogs;
|
final employees = attendanceController.regularizationLogs;
|
||||||
if (attendanceController.isLoadingRegularizationLogs.value) {
|
if (attendanceController.isLoadingRegularizationLogs.value) {
|
||||||
return employeeListSkeletonLoader();
|
return SkeletonLoaders.employeeListSkeletonLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (employees.isEmpty) {
|
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/helpers/widgets/avatar.dart';
|
||||||
import 'package:marco/model/employees/employee_detail_bottom_sheet.dart';
|
import 'package:marco/model/employees/employee_detail_bottom_sheet.dart';
|
||||||
import 'package:marco/controller/project_controller.dart';
|
import 'package:marco/controller/project_controller.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||||
class EmployeesScreen extends StatefulWidget {
|
class EmployeesScreen extends StatefulWidget {
|
||||||
const EmployeesScreen({super.key});
|
const EmployeesScreen({super.key});
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
|||||||
final isLoading = employeeScreenController.isLoading.value;
|
final isLoading = employeeScreenController.isLoading.value;
|
||||||
final employees = employeeScreenController.employees;
|
final employees = employeeScreenController.employees;
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return SkeletonLoaders.employeeListSkeletonLoader();
|
||||||
}
|
}
|
||||||
if (employees.isEmpty) {
|
if (employees.isEmpty) {
|
||||||
return Padding(
|
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/helpers/widgets/avatar.dart';
|
||||||
import 'package:marco/controller/project_controller.dart';
|
import 'package:marco/controller/project_controller.dart';
|
||||||
import 'package:marco/model/dailyTaskPlaning/task_action_buttons.dart';
|
import 'package:marco/model/dailyTaskPlaning/task_action_buttons.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||||
|
|
||||||
class DailyProgressReportScreen extends StatefulWidget {
|
class DailyProgressReportScreen extends StatefulWidget {
|
||||||
const DailyProgressReportScreen({super.key});
|
const DailyProgressReportScreen({super.key});
|
||||||
@ -297,7 +298,7 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
final groupedTasks = dailyTaskController.groupedDailyTasks;
|
final groupedTasks = dailyTaskController.groupedDailyTasks;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return SkeletonLoaders.dailyProgressReportSkeletonLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupedTasks.isEmpty) {
|
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:marco/controller/project_controller.dart';
|
||||||
import 'package:percent_indicator/percent_indicator.dart';
|
import 'package:percent_indicator/percent_indicator.dart';
|
||||||
import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart';
|
import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||||
|
|
||||||
class DailyTaskPlaningScreen extends StatefulWidget {
|
class DailyTaskPlaningScreen extends StatefulWidget {
|
||||||
DailyTaskPlaningScreen({super.key});
|
DailyTaskPlaningScreen({super.key});
|
||||||
@ -170,7 +171,7 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
|||||||
final dailyTasks = dailyTaskPlaningController.dailyTasks;
|
final dailyTasks = dailyTaskPlaningController.dailyTasks;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return Center(child: CircularProgressIndicator());
|
return SkeletonLoaders.dailyProgressPlanningSkeletonCollapsedOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dailyTasks.isEmpty) {
|
if (dailyTasks.isEmpty) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user