Added Service
This commit is contained in:
parent
6238c701c0
commit
46a84b4d95
|
|
@ -1,10 +1,15 @@
|
|||
class Office {
|
||||
Office({required this.id, required this.name});
|
||||
Office({required this.id, required this.name, this.serviceId});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String? serviceId;
|
||||
|
||||
factory Office.fromMap(Map<String, dynamic> map) {
|
||||
return Office(id: map['id'] as String, name: map['name'] as String? ?? '');
|
||||
return Office(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String? ?? '',
|
||||
serviceId: map['service_id'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
lib/models/service.dart
Normal file
10
lib/models/service.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
class Service {
|
||||
Service({required this.id, required this.name});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
|
||||
factory Service.fromMap(Map<String, dynamic> map) {
|
||||
return Service(id: map['id'] as String, name: map['name'] as String? ?? '');
|
||||
}
|
||||
}
|
||||
21
lib/providers/services_provider.dart
Normal file
21
lib/providers/services_provider.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../models/service.dart';
|
||||
import 'supabase_provider.dart';
|
||||
|
||||
final servicesProvider = StreamProvider<List<Service>>((ref) {
|
||||
final client = ref.watch(supabaseClientProvider);
|
||||
return client
|
||||
.from('services')
|
||||
.stream(primaryKey: ['id'])
|
||||
.order('name')
|
||||
.map((rows) => rows.map((r) => Service.fromMap(r)).toList());
|
||||
});
|
||||
|
||||
final servicesOnceProvider = FutureProvider<List<Service>>((ref) async {
|
||||
final client = ref.watch(supabaseClientProvider);
|
||||
final rows = await client.from('services').select().order('name');
|
||||
return (rows as List<dynamic>)
|
||||
.map((r) => Service.fromMap(r as Map<String, dynamic>))
|
||||
.toList();
|
||||
});
|
||||
|
|
@ -377,12 +377,20 @@ class OfficesController {
|
|||
|
||||
final SupabaseClient _client;
|
||||
|
||||
Future<void> createOffice({required String name}) async {
|
||||
await _client.from('offices').insert({'name': name});
|
||||
Future<void> createOffice({required String name, String? serviceId}) async {
|
||||
final payload = {'name': name};
|
||||
if (serviceId != null) payload['service_id'] = serviceId;
|
||||
await _client.from('offices').insert(payload);
|
||||
}
|
||||
|
||||
Future<void> updateOffice({required String id, required String name}) async {
|
||||
await _client.from('offices').update({'name': name}).eq('id', id);
|
||||
Future<void> updateOffice({
|
||||
required String id,
|
||||
required String name,
|
||||
String? serviceId,
|
||||
}) async {
|
||||
final payload = {'name': name};
|
||||
if (serviceId != null) payload['service_id'] = serviceId;
|
||||
await _client.from('offices').update(payload).eq('id', id);
|
||||
}
|
||||
|
||||
Future<void> deleteOffice({required String id}) async {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import '../../models/office.dart';
|
||||
import '../../providers/profile_provider.dart';
|
||||
import '../../providers/tickets_provider.dart';
|
||||
import '../../providers/services_provider.dart';
|
||||
import '../../widgets/mono_text.dart';
|
||||
import '../../widgets/responsive_body.dart';
|
||||
import '../../theme/app_surfaces.dart';
|
||||
|
|
@ -163,16 +164,59 @@ class _OfficesScreenState extends ConsumerState<OfficesScreen> {
|
|||
Office? office,
|
||||
}) async {
|
||||
final nameController = TextEditingController(text: office?.name ?? '');
|
||||
String? selectedServiceId = office?.serviceId;
|
||||
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
final servicesAsync = ref.watch(servicesOnceProvider);
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
shape: AppSurfaces.of(context).dialogShape,
|
||||
title: Text(office == null ? 'Create Office' : 'Edit Office'),
|
||||
content: TextField(
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nameController,
|
||||
decoration: const InputDecoration(labelText: 'Office name'),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Office name',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
servicesAsync.when(
|
||||
data: (services) {
|
||||
return DropdownButtonFormField<String?>(
|
||||
initialValue: selectedServiceId,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Service',
|
||||
),
|
||||
items: [
|
||||
const DropdownMenuItem<String?>(
|
||||
value: null,
|
||||
child: Text('None'),
|
||||
),
|
||||
...services.map(
|
||||
(s) => DropdownMenuItem<String?>(
|
||||
value: s.id,
|
||||
child: Text(s.name),
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: (v) =>
|
||||
setState(() => selectedServiceId = v),
|
||||
);
|
||||
},
|
||||
loading: () => const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: LinearProgressIndicator(),
|
||||
),
|
||||
error: (e, _) => Text('Failed to load services: $e'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
|
|
@ -190,9 +234,16 @@ class _OfficesScreenState extends ConsumerState<OfficesScreen> {
|
|||
}
|
||||
final controller = ref.read(officesControllerProvider);
|
||||
if (office == null) {
|
||||
await controller.createOffice(name: name);
|
||||
await controller.createOffice(
|
||||
name: name,
|
||||
serviceId: selectedServiceId,
|
||||
);
|
||||
} else {
|
||||
await controller.updateOffice(id: office.id, name: name);
|
||||
await controller.updateOffice(
|
||||
id: office.id,
|
||||
name: name,
|
||||
serviceId: selectedServiceId,
|
||||
);
|
||||
}
|
||||
ref.invalidate(officesProvider);
|
||||
if (context.mounted) {
|
||||
|
|
@ -205,6 +256,8 @@ class _OfficesScreenState extends ConsumerState<OfficesScreen> {
|
|||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _confirmDelete(
|
||||
|
|
|
|||
30
supabase/migrations/20260221150000_add_services_table.sql
Normal file
30
supabase/migrations/20260221150000_add_services_table.sql
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
-- Add services table and link offices to services
|
||||
|
||||
-- 1) Create services table
|
||||
CREATE TABLE IF NOT EXISTS services (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name text NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
-- 2) Add service_id to offices
|
||||
ALTER TABLE IF EXISTS offices
|
||||
ADD COLUMN IF NOT EXISTS service_id uuid REFERENCES services(id) ON DELETE SET NULL;
|
||||
|
||||
-- 3) Insert default services
|
||||
INSERT INTO services (name) VALUES
|
||||
('Medical Center Chief'),
|
||||
('Medical Service'),
|
||||
('Nursing Service'),
|
||||
('Hospital Operations and Patient Support Service'),
|
||||
('Finance Service'),
|
||||
('Allied/Ancillary')
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- 4) Make existing offices default to Medical Center Chief (for now)
|
||||
UPDATE offices
|
||||
SET service_id = s.id
|
||||
FROM services s
|
||||
WHERE s.name = 'Medical Center Chief';
|
||||
|
||||
-- 5) (Optional) Add index for faster lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_offices_service_id ON offices(service_id);
|
||||
|
|
@ -68,6 +68,7 @@ void main() {
|
|||
final task = Task(
|
||||
id: 'TSK-1',
|
||||
ticketId: 'TCK-1',
|
||||
taskNumber: '2026-02-00001',
|
||||
title: 'Reboot printer',
|
||||
description: 'Clear queue and reboot',
|
||||
officeId: 'office-1',
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ void main() {
|
|||
final emptyTask = Task(
|
||||
id: 'tsk-1',
|
||||
ticketId: null,
|
||||
taskNumber: '2026-02-00002',
|
||||
title: 'No metadata',
|
||||
description: '',
|
||||
officeId: 'office-1',
|
||||
|
|
@ -159,6 +160,7 @@ void main() {
|
|||
final task = Task(
|
||||
id: 'tsk-1',
|
||||
ticketId: null,
|
||||
taskNumber: '2026-02-00002',
|
||||
title: 'Has metadata',
|
||||
description: '',
|
||||
officeId: 'office-1',
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user