import 'job.dart'; enum TenantKind { lab, clinic } enum TenantRole { owner, admin, technician, // lab: işler + ürünler delivery, // lab: işler finance, // lab+clinic: finans doctor, // clinic: işler + hastalar member, // legacy — full access ; String get value => name; String get label => switch (this) { TenantRole.owner => 'Sahibi', TenantRole.admin => 'Yönetici', TenantRole.technician => 'Teknisyen', TenantRole.delivery => 'Teslimat Elemanı', TenantRole.finance => 'Finans Elemanı', TenantRole.doctor => 'Hekim', TenantRole.member => 'Üye', }; } enum TenantPlan { starter, pro, enterprise } class Tenant { const Tenant({ required this.id, required this.kind, required this.memberNumber, required this.companyName, this.companyAddress, this.city, this.district, this.latitude, this.longitude, this.logo, this.defaultCurrency = 'TRY', this.status = 'active', this.plan, this.maxMembers, this.workflowOverrideStepKeys = const [], }); final String id; final TenantKind kind; final String memberNumber; final String companyName; final String? companyAddress; final String? city; final String? district; final double? latitude; final double? longitude; final String? logo; final String defaultCurrency; final String status; final TenantPlan? plan; final int? maxMembers; final List workflowOverrideStepKeys; bool get isLab => kind == TenantKind.lab; bool get isClinic => kind == TenantKind.clinic; bool get hasLocation => latitude != null && longitude != null; List get workflowOverrideSteps => workflowOverrideStepKeys .map(Job.parseStepValue) .where((step) => step.isLabOptional) .toList(); String get locationLabel { final parts = [ if ((district ?? '').trim().isNotEmpty) district!.trim(), if ((city ?? '').trim().isNotEmpty) city!.trim(), ]; if (parts.isNotEmpty) return parts.join(' / '); return (companyAddress ?? '').trim(); } factory Tenant.fromJson(Map j) => Tenant( id: j['id'] as String, kind: j['kind'] == 'lab' ? TenantKind.lab : TenantKind.clinic, memberNumber: (j['member_number'] as String?) ?? '', companyName: j['company_name'] as String, companyAddress: j['company_address'] as String?, city: j['city'] as String?, district: j['district'] as String?, latitude: (j['latitude'] as num?)?.toDouble(), longitude: (j['longitude'] as num?)?.toDouble(), logo: j['logo'] as String?, defaultCurrency: (j['default_currency'] as String?) ?? 'TRY', status: (j['status'] as String?) ?? 'active', plan: _parsePlan(j['plan'] as String?), maxMembers: (j['max_members'] as num?)?.toInt(), workflowOverrideStepKeys: j['workflow_overrides'] is List ? (j['workflow_overrides'] as List) .map((e) => e.toString()) .toList() : const [], ); static TenantPlan? _parsePlan(String? p) => switch (p) { 'starter' => TenantPlan.starter, 'pro' => TenantPlan.pro, 'enterprise' => TenantPlan.enterprise, _ => null, }; static TenantPlan parsePlanValue(String? value) => _parsePlan(value) ?? TenantPlan.starter; } class TenantMembership { const TenantMembership({ required this.id, required this.tenant, required this.role, }); final String id; final Tenant tenant; final TenantRole role; // ── Access helpers ──────────────────────────────────────────────────────── bool get isOwner => role == TenantRole.owner; bool get isAdmin => role == TenantRole.admin || role == TenantRole.owner; bool get canManageUsers => role == TenantRole.owner || role == TenantRole.admin; bool get canManageJobs => role != TenantRole.finance; bool get canManageFinance => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.finance || role == TenantRole.member; bool get canManageProducts => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.technician || role == TenantRole.member; bool get canViewPatients => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.doctor || role == TenantRole.member; bool get canManageConnections => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.member; // ── Fine-grained job actions ────────────────────────────────────────────── /// Can create new jobs (clinic side: owner/admin/doctor/member; not delivery/finance) bool get canCreateJobs => role != TenantRole.delivery && role != TenantRole.finance; /// Can confirm physical delivery (delivery role + supervisors) bool get canDeliverJobs => role != TenantRole.finance; /// Can cancel or fully manage job lifecycle (not delivery-only or finance) bool get canCancelJobs => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.member || role == TenantRole.doctor; /// Primary focus is delivery — restrict to delivery-relevant UI bool get isDeliveryOnly => role == TenantRole.delivery; // ── Nav visibility ──────────────────────────────────────────────────────── bool get showDashboard => true; bool get showJobs => canManageJobs; bool get showProducts => tenant.isLab && canManageProducts; bool get showPatients => tenant.isClinic && canViewPatients; bool get showConnections => canManageConnections; bool get showFinance => canManageFinance; factory TenantMembership.fromJson(Map j) { final expand = j['expand'] as Map?; final tenantData = expand?['tenant_id'] as Map?; return TenantMembership( id: j['id'] as String, tenant: Tenant.fromJson(tenantData!), role: parseRole(j['role'] as String), ); } static TenantRole parseRole(String r) => switch (r) { 'owner' => TenantRole.owner, 'admin' => TenantRole.admin, 'technician' => TenantRole.technician, 'delivery' => TenantRole.delivery, 'finance' => TenantRole.finance, 'doctor' => TenantRole.doctor, _ => TenantRole.member, }; String get roleLabel => switch (role) { TenantRole.owner => 'Sahibi', TenantRole.admin => 'Yönetici', TenantRole.technician => 'Teknisyen', TenantRole.delivery => 'Teslimat Elemanı', TenantRole.finance => 'Finans Elemanı', TenantRole.doctor => 'Hekim', TenantRole.member => 'Üye', }; }