234 lines
8.9 KiB
Dart
234 lines
8.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../../models/project_model.dart';
|
|
import '../../models/task_log_model.dart';
|
|
import '../../services/project_service.dart';
|
|
import '../../services/task_log_service.dart';
|
|
import '../project_detail_screen.dart';
|
|
import '../project_form_screen.dart';
|
|
|
|
class ProjectsTab extends StatelessWidget {
|
|
const ProjectsTab({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final service = context.read<ProjectService>();
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8FAFC),
|
|
body: StreamBuilder<List<ProjectModel>>(
|
|
stream: service.getProjects(),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
final projects = snapshot.data ?? [];
|
|
|
|
if (projects.isEmpty) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.folder_open_rounded, size: 100, color: Colors.grey.shade200),
|
|
const SizedBox(height: 16),
|
|
Text("No projects assigned yet.", style: TextStyle(color: Colors.grey.shade500, fontSize: 13)),
|
|
const SizedBox(height: 24),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const ProjectFormScreen())
|
|
),
|
|
style: ElevatedButton.styleFrom(minimumSize: const Size(200, 50)),
|
|
child: const Text("CREATE FIRST PROJECT"),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
return CustomScrollView(
|
|
slivers: [
|
|
const SliverPadding(
|
|
padding: EdgeInsets.fromLTRB(20, 20, 20, 10),
|
|
sliver: SliverToBoxAdapter(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Workspace",
|
|
style: TextStyle(fontSize: 14, color: Color(0xFF64748B), fontWeight: FontWeight.w500),
|
|
),
|
|
Text(
|
|
"Projects",
|
|
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Color(0xFF1E293B)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
SliverPadding(
|
|
padding: const EdgeInsets.all(20),
|
|
sliver: SliverList(
|
|
delegate: SliverChildBuilderDelegate(
|
|
(context, index) => _ProjectCard(p: projects[index]),
|
|
childCount: projects.length,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
heroTag: "projects_fab",
|
|
backgroundColor: const Color(0xFF6366F1),
|
|
onPressed: () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const ProjectFormScreen())
|
|
),
|
|
label: const Text("NEW PROJECT", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
|
icon: const Icon(Icons.add_rounded, color: Colors.white),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ProjectCard extends StatelessWidget {
|
|
final ProjectModel p;
|
|
const _ProjectCard({required this.p});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final taskService = context.watch<TaskLogService>();
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(24),
|
|
border: Border.all(color: Colors.grey.shade100),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.indigo.withOpacity(0.02),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: InkWell(
|
|
onTap: () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => ProjectDetailScreen(project: p))
|
|
),
|
|
borderRadius: BorderRadius.circular(24),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF6366F1).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: const Icon(Icons.folder_copy_rounded, color: Color(0xFF6366F1), size: 24),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
p.name,
|
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Color(0xFF1E293B)),
|
|
),
|
|
Text(
|
|
"Created ${DateFormat('MMM yyyy').format(p.createdAt)}",
|
|
style: TextStyle(color: Colors.grey.shade500, fontSize: 11),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const Icon(Icons.chevron_right_rounded, color: Color(0xFF94A3B8)),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
p.description,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(color: Color(0xFF475569), fontSize: 13, height: 1.5),
|
|
),
|
|
const SizedBox(height: 16),
|
|
if (p.flavors.isNotEmpty) ...[
|
|
Wrap(
|
|
spacing: 6,
|
|
runSpacing: 6,
|
|
children: p.flavors.map((flavor) => Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF1F5F9),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
flavor,
|
|
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold, color: Color(0xFF64748B)),
|
|
),
|
|
)).toList(),
|
|
),
|
|
const SizedBox(height: 20),
|
|
],
|
|
FutureBuilder<TaskLogModel?>(
|
|
future: taskService.getLatestLogForProject(p.id),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.hasError) {
|
|
return const Text(
|
|
"Index Error. Click project for link.",
|
|
style: TextStyle(fontSize: 10, color: Colors.red, fontWeight: FontWeight.bold)
|
|
);
|
|
}
|
|
final latestLog = snapshot.data;
|
|
if (latestLog == null) {
|
|
return const Text(
|
|
"No activity recorded yet.",
|
|
style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8), fontStyle: FontStyle.italic)
|
|
);
|
|
}
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF8FAFC),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
const Icon(Icons.auto_awesome_rounded, size: 14, color: Colors.amber),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
"Latest: ${latestLog.userName} on ${DateFormat('MM/dd').format(latestLog.date)}",
|
|
style: const TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: Color(0xFF334155)),
|
|
),
|
|
),
|
|
Text(
|
|
latestLog.moduleName,
|
|
style: const TextStyle(fontSize: 10, color: Color(0xFF64748B)),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|