Feature_Report_Action #48
@ -319,7 +319,7 @@ class ApiService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
final response = await _postRequest(
|
final response = await _postRequest(
|
||||||
ApiEndpoints.commentTask,
|
ApiEndpoints.reportTask,
|
||||||
body,
|
body,
|
||||||
customTimeout: extendedTimeout,
|
customTimeout: extendedTimeout,
|
||||||
);
|
);
|
||||||
|
@ -133,8 +133,21 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
return GetBuilder<ProjectController>(
|
return GetBuilder<ProjectController>(
|
||||||
id: 'dashboard_controller',
|
id: 'dashboard_controller',
|
||||||
builder: (controller) {
|
builder: (controller) {
|
||||||
|
final bool isLoading = controller.isLoading.value;
|
||||||
final bool isProjectSelected = controller.selectedProject != null;
|
final bool isProjectSelected = controller.selectedProject != null;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return Wrap(
|
||||||
|
spacing: 10,
|
||||||
|
runSpacing: 10,
|
||||||
|
children: List.generate(
|
||||||
|
4,
|
||||||
|
(index) =>
|
||||||
|
_buildStatCardSkeleton(MediaQuery.of(context).size.width / 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -184,6 +197,33 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildStatCardSkeleton(double width) {
|
||||||
|
return MyCard.bordered(
|
||||||
|
width: width,
|
||||||
|
height: 100,
|
||||||
|
paddingAll: 5,
|
||||||
|
borderRadiusAll: 10,
|
||||||
|
border: Border.all(color: Colors.grey.withOpacity(0.15)),
|
||||||
|
shadow: MyShadow(elevation: 1.5, position: MyShadowPosition.bottom),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
MyContainer.rounded(
|
||||||
|
paddingAll: 12,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
child: const SizedBox(width: 18, height: 18),
|
||||||
|
),
|
||||||
|
MySpacing.height(8),
|
||||||
|
Container(
|
||||||
|
height: 12,
|
||||||
|
width: 60,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildStatCard(_StatItem statItem, double width, bool isEnabled) {
|
Widget _buildStatCard(_StatItem statItem, double width, bool isEnabled) {
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: isEnabled ? 1.0 : 0.4,
|
opacity: isEnabled ? 1.0 : 0.4,
|
||||||
|
@ -68,7 +68,6 @@ class _LayoutState extends State<Layout> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// Project dropdown overlay
|
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (!projectController.isProjectSelectionExpanded.value) {
|
if (!projectController.isProjectSelectionExpanded.value) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
@ -102,6 +101,12 @@ class _LayoutState extends State<Layout> {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
|
final isLoading = projectController.isLoading.value;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return _buildLoadingSkeleton();
|
||||||
|
}
|
||||||
|
|
||||||
final isExpanded = projectController.isProjectSelectionExpanded.value;
|
final isExpanded = projectController.isProjectSelectionExpanded.value;
|
||||||
final selectedProjectId = projectController.selectedProjectId?.value;
|
final selectedProjectId = projectController.selectedProjectId?.value;
|
||||||
final selectedProject = projectController.projects.firstWhereOrNull(
|
final selectedProject = projectController.projects.firstWhereOrNull(
|
||||||
@ -220,8 +225,6 @@ class _LayoutState extends State<Layout> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Expanded Project List inside card — only show if projects exist
|
|
||||||
if (isExpanded && hasProjects)
|
if (isExpanded && hasProjects)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 70,
|
top: 70,
|
||||||
@ -240,6 +243,56 @@ class _LayoutState extends State<Layout> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildLoadingSkeleton() {
|
||||||
|
return Card(
|
||||||
|
elevation: 4,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
width: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 18,
|
||||||
|
width: 140,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Container(
|
||||||
|
height: 14,
|
||||||
|
width: 100,
|
||||||
|
color: Colors.grey.shade200,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Container(
|
||||||
|
height: 30,
|
||||||
|
width: 30,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildProjectList(BuildContext context, bool isMobile) {
|
Widget _buildProjectList(BuildContext context, bool isMobile) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user