tasq/lib/screens/admin/offices_screen.dart

186 lines
6.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../models/office.dart';
import '../../providers/profile_provider.dart';
import '../../providers/tickets_provider.dart';
import '../../widgets/responsive_body.dart';
class OfficesScreen extends ConsumerWidget {
const OfficesScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isAdmin = ref.watch(isAdminProvider);
final officesAsync = ref.watch(officesProvider);
return Scaffold(
body: ResponsiveBody(
maxWidth: 800,
child: !isAdmin
? const Center(child: Text('Admin access required.'))
: officesAsync.when(
data: (offices) {
if (offices.isEmpty) {
return const Center(child: Text('No offices found.'));
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.only(top: 16, bottom: 8),
child: Row(
children: [
Expanded(
child: Text(
'Office Management',
style: Theme.of(context).textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.w700),
),
),
TextButton.icon(
onPressed: () => context.go('/settings/users'),
icon: const Icon(Icons.group),
label: const Text('User access'),
),
],
),
),
Expanded(
child: ListView.separated(
padding: const EdgeInsets.only(bottom: 24),
itemCount: offices.length,
separatorBuilder: (_, index) =>
const SizedBox(height: 12),
itemBuilder: (context, index) {
final office = offices[index];
return ListTile(
leading: const Icon(Icons.apartment_outlined),
title: Text(office.name),
trailing: Wrap(
spacing: 8,
children: [
IconButton(
tooltip: 'Edit',
icon: const Icon(Icons.edit),
onPressed: () => _showOfficeDialog(
context,
ref,
office: office,
),
),
IconButton(
tooltip: 'Delete',
icon: const Icon(Icons.delete),
onPressed: () =>
_confirmDelete(context, ref, office),
),
],
),
);
},
),
),
],
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) =>
Center(child: Text('Failed to load offices: $error')),
),
),
floatingActionButton: isAdmin
? FloatingActionButton.extended(
onPressed: () => _showOfficeDialog(context, ref),
icon: const Icon(Icons.add),
label: const Text('New Office'),
)
: null,
);
}
Future<void> _showOfficeDialog(
BuildContext context,
WidgetRef ref, {
Office? office,
}) async {
final nameController = TextEditingController(text: office?.name ?? '');
await showDialog<void>(
context: context,
builder: (dialogContext) {
return AlertDialog(
title: Text(office == null ? 'Create Office' : 'Edit Office'),
content: TextField(
controller: nameController,
decoration: const InputDecoration(labelText: 'Office name'),
),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () async {
final name = nameController.text.trim();
if (name.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Name is required.')),
);
return;
}
final controller = ref.read(officesControllerProvider);
if (office == null) {
await controller.createOffice(name: name);
} else {
await controller.updateOffice(id: office.id, name: name);
}
ref.invalidate(officesProvider);
if (context.mounted) {
Navigator.of(dialogContext).pop();
}
},
child: Text(office == null ? 'Create' : 'Save'),
),
],
);
},
);
}
Future<void> _confirmDelete(
BuildContext context,
WidgetRef ref,
Office office,
) async {
await showDialog<void>(
context: context,
builder: (dialogContext) {
return AlertDialog(
title: const Text('Delete Office'),
content: Text('Delete ${office.name}? This cannot be undone.'),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () async {
await ref
.read(officesControllerProvider)
.deleteOffice(id: office.id);
ref.invalidate(officesProvider);
if (context.mounted) {
Navigator.of(dialogContext).pop();
}
},
child: const Text('Delete'),
),
],
);
},
);
}
}