Initial commit — DLS lab-app Flutter project
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import '../api/pocketbase_client.dart';
|
||||
import '../../models/tenant.dart';
|
||||
|
||||
class AiContextBuilder {
|
||||
AiContextBuilder._();
|
||||
static final instance = AiContextBuilder._();
|
||||
|
||||
PocketBase get _pb => PocketBaseClient.instance.pb;
|
||||
|
||||
Future<String> build(TenantMembership membership) async {
|
||||
final tenant = membership.tenant;
|
||||
final tenantId = tenant.id;
|
||||
final isLab = tenant.kind == TenantKind.lab;
|
||||
|
||||
final now = DateTime.now();
|
||||
final dateStr = '${now.day}.${now.month}.${now.year}';
|
||||
|
||||
final results = await Future.wait([
|
||||
_fetchActiveJobs(tenantId, isLab),
|
||||
_fetchRecentDelivered(tenantId, isLab),
|
||||
_fetchFinance(tenantId, isLab),
|
||||
_fetchTeam(tenantId),
|
||||
]);
|
||||
|
||||
final actions = _actionsPrompt(isLab);
|
||||
|
||||
return 'Sen DLS (Dental Lab System) uygulamasinin akilli asistanisin.\n'
|
||||
'${tenant.companyName} adli ${isLab ? 'dental laboratuvarinin' : 'dis kliniginin'} verilerine erisebilirsin.\n'
|
||||
'Kullanici rolu: ${isLab ? 'LABORATUVAR' : 'KLINIK'}\n'
|
||||
'\n'
|
||||
'Tarih: $dateStr\n'
|
||||
'\n'
|
||||
'${results[0]}\n'
|
||||
'\n'
|
||||
'${results[1]}\n'
|
||||
'\n'
|
||||
'${results[2]}\n'
|
||||
'\n'
|
||||
'${results[3]}\n'
|
||||
'\n'
|
||||
'$actions\n'
|
||||
'\n'
|
||||
'Yanit kurallari:\n'
|
||||
'- Turkce, kisa ve net yaz\n'
|
||||
'- Sadece yukaridaki verilerden hareketle yorum yap\n'
|
||||
'- Listelerde madde isareti (- ) kullan\n'
|
||||
'- Onemli bilgileri **kalin** yaz\n'
|
||||
'- Aksiyon etiketlerini HERZAMAN metnin sonuna koy\n'
|
||||
'- ${isLab ? 'Is kodlari icin [ID:...] formatini kullan' : 'Hasta kodlari ve is durumlarini net belirt'}\n';
|
||||
}
|
||||
|
||||
static String _actionsPrompt(bool isLab) {
|
||||
final buf = StringBuffer();
|
||||
buf.writeln('## EYLEM YETKILERIN');
|
||||
buf.writeln('Kullanici bir islem yapmak istediginde asagidaki XML etiketlerini yanita ekle:');
|
||||
buf.writeln('');
|
||||
buf.writeln('Is dosyalarini gostermek:');
|
||||
buf.writeln('<dls-action type="job_files" job_id="JOB_ID" label="AB001 dosyalarini goster"/>');
|
||||
buf.writeln('');
|
||||
buf.writeln('Is iptal etmek:');
|
||||
buf.writeln('<dls-action type="cancel_job" job_id="JOB_ID" label="AB001 isini iptal et"/>');
|
||||
if (!isLab) {
|
||||
buf.writeln('');
|
||||
buf.writeln('Teslim edildi isaretlemek (sadece klinik):');
|
||||
buf.writeln('<dls-action type="mark_delivered" job_id="JOB_ID" label="AB001 teslim edildi"/>');
|
||||
}
|
||||
buf.writeln('');
|
||||
buf.writeln('Ekip uyesi eklemek (TUM bilgiler alindiktan sonra):');
|
||||
buf.writeln('<dls-action type="add_member" email="..." first_name="..." last_name="..." role="technician|admin|doctor|delivery|finance|member" password="..." label="Ad Soyad ekle"/>');
|
||||
buf.writeln('');
|
||||
buf.writeln('KURALLAR:');
|
||||
buf.writeln('- Etiketi SADECE kullanici acikca islem istediginde ekle');
|
||||
buf.writeln('- Sifre sorulursa kullanicidan al, ASLA uydurma');
|
||||
buf.writeln('- iptal gibi geri alinmaz islemleri acikca belirt');
|
||||
buf.writeln('- Etiket icindeki job_id degerini yukaridaki is listesinden al');
|
||||
buf.writeln('- <dls-action> etiketlerini KESINLİKLE kod blogu (```xml veya ```) icine ALMA, duz metin olarak yaz');
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
Future<String> _fetchActiveJobs(String tenantId, bool isLab) async {
|
||||
try {
|
||||
final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id';
|
||||
final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id';
|
||||
final result = await _pb.collection('jobs').getList(
|
||||
filter: '$tenantField = "$tenantId" && status != "delivered" && status != "cancelled"',
|
||||
perPage: 60,
|
||||
sort: '-created',
|
||||
expand: counterpartField,
|
||||
);
|
||||
|
||||
if (result.items.isEmpty) return '## Aktif Isler\nSu an aktif is yok.';
|
||||
|
||||
final counterpartLabel = isLab ? 'Klinik' : 'Lab';
|
||||
final lines = result.items.map((r) {
|
||||
final j = r.toJson();
|
||||
final jobId = j['id'] as String? ?? '';
|
||||
final expand = j['expand'] as Map<String, dynamic>?;
|
||||
final counterpart =
|
||||
(expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-';
|
||||
final status = _statusTr(j['status'] as String? ?? '');
|
||||
final prosthetic = j['prosthetic_type'] as String? ?? '-';
|
||||
final patient = j['patient_code'] as String? ?? '-';
|
||||
final step = j['current_step'] as String?;
|
||||
final stepPart = (step != null && step.isNotEmpty) ? ' | Adim: $step' : '';
|
||||
final due = j['due_date'] as String? ?? '';
|
||||
final duePart = due.isNotEmpty ? ' | Termin: ${due.substring(0, 10)}' : '';
|
||||
return '- [ID:$jobId] Hasta: $patient | $prosthetic | $status$stepPart | $counterpartLabel: $counterpart$duePart';
|
||||
}).join('\n');
|
||||
|
||||
return '## Aktif Isler (${result.items.length})\n$lines';
|
||||
} catch (e) {
|
||||
return '## Aktif Isler\n(Veri alinamadi: $e)';
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _fetchRecentDelivered(String tenantId, bool isLab) async {
|
||||
try {
|
||||
final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id';
|
||||
final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id';
|
||||
final result = await _pb.collection('jobs').getList(
|
||||
filter: '$tenantField = "$tenantId" && status = "delivered"',
|
||||
perPage: 10,
|
||||
sort: '-updated',
|
||||
expand: counterpartField,
|
||||
);
|
||||
|
||||
if (result.items.isEmpty) return '## Son Teslim Edilenler\nHenuz teslim edilen is yok.';
|
||||
|
||||
final counterpartLabel = isLab ? 'Klinik' : 'Lab';
|
||||
final lines = result.items.map((r) {
|
||||
final j = r.toJson();
|
||||
final jobId = j['id'] as String? ?? '';
|
||||
final expand = j['expand'] as Map<String, dynamic>?;
|
||||
final counterpart =
|
||||
(expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-';
|
||||
final prosthetic = j['prosthetic_type'] as String? ?? '-';
|
||||
final patient = j['patient_code'] as String? ?? '-';
|
||||
final updated = (j['updated'] as String? ?? '');
|
||||
final datePart = updated.length >= 10 ? updated.substring(0, 10) : '';
|
||||
return '- [ID:$jobId] Hasta: $patient | $prosthetic | $counterpartLabel: $counterpart${datePart.isNotEmpty ? ' | Tarih: $datePart' : ''}';
|
||||
}).join('\n');
|
||||
|
||||
return '## Son Teslim Edilenler (son 10)\n$lines';
|
||||
} catch (_) {
|
||||
return '## Son Teslim Edilenler\n(Veri alinamadi)';
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _fetchFinance(String tenantId, bool isLab) async {
|
||||
try {
|
||||
final type = isLab ? 'receivable' : 'payable';
|
||||
final result = await _pb.collection('finance_entries').getList(
|
||||
filter: 'tenant_id = "$tenantId" && type = "$type"',
|
||||
perPage: 200,
|
||||
);
|
||||
|
||||
double pending = 0, paid = 0;
|
||||
for (final r in result.items) {
|
||||
final j = r.toJson();
|
||||
final amount = (j['amount'] as num?)?.toDouble() ?? 0;
|
||||
if (j['status'] == 'pending') {
|
||||
pending += amount;
|
||||
} else {
|
||||
paid += amount;
|
||||
}
|
||||
}
|
||||
|
||||
final label = isLab ? 'alacak' : 'borc';
|
||||
return '## Finans\n'
|
||||
'- Bekleyen $label: ${pending.toStringAsFixed(0)} TL\n'
|
||||
'- Tahsil edilen: ${paid.toStringAsFixed(0)} TL';
|
||||
} catch (_) {
|
||||
return '## Finans\n(Veri alinamadi)';
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _fetchTeam(String tenantId) async {
|
||||
try {
|
||||
final result = await _pb.collection('tenant_members').getList(
|
||||
filter: 'tenant_id = "$tenantId"',
|
||||
expand: 'user_id',
|
||||
perPage: 50,
|
||||
);
|
||||
|
||||
if (result.items.isEmpty) return '## Ekip\nUye yok.';
|
||||
|
||||
final lines = result.items.map((r) {
|
||||
final j = r.toJson();
|
||||
final expand = j['expand'] as Map<String, dynamic>?;
|
||||
final user = expand?['user_id'] as Map<String, dynamic>?;
|
||||
final first = (user?['first_name'] as String?) ?? '';
|
||||
final last = (user?['last_name'] as String?) ?? '';
|
||||
final email = (user?['email'] as String?) ?? '';
|
||||
final name =
|
||||
'$first $last'.trim().isNotEmpty ? '$first $last'.trim() : email;
|
||||
final role = _roleTr(j['role'] as String? ?? '');
|
||||
return '- $name ($role)';
|
||||
}).join('\n');
|
||||
|
||||
return '## Ekip (${result.items.length} uye)\n$lines';
|
||||
} catch (_) {
|
||||
return '## Ekip\n(Veri alinamadi)';
|
||||
}
|
||||
}
|
||||
|
||||
static String _statusTr(String s) => switch (s) {
|
||||
'pending' => 'Bekliyor',
|
||||
'in_progress' => 'Devam ediyor',
|
||||
'sent' => 'Gonderildi',
|
||||
'revision' => 'Revizyon',
|
||||
'delivered' => 'Teslim edildi',
|
||||
'cancelled' => 'Iptal',
|
||||
_ => s,
|
||||
};
|
||||
|
||||
static String _roleTr(String s) => switch (s) {
|
||||
'owner' => 'Sahibi',
|
||||
'admin' => 'Yonetici',
|
||||
'technician' => 'Teknisyen',
|
||||
'delivery' => 'Teslimat',
|
||||
'finance' => 'Finans',
|
||||
'doctor' => 'Hekim',
|
||||
_ => 'Uye',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user