padmanto
2 years ago
26 changed files with 801 additions and 188 deletions
@ -0,0 +1,48 @@ |
|||||||
|
class AcDoctorResponseModel { |
||||||
|
late String fullName; |
||||||
|
late String mDoctorID; |
||||||
|
AcDoctorResponseModel({ |
||||||
|
required this.fullName, |
||||||
|
required this.mDoctorID, |
||||||
|
}); |
||||||
|
|
||||||
|
AcDoctorResponseModel.fromJson(Map<String, dynamic> json) { |
||||||
|
fullName = json['FullName']; |
||||||
|
mDoctorID = json['M_DoctorID']; |
||||||
|
} |
||||||
|
|
||||||
|
Map<String, dynamic> toJson() { |
||||||
|
final Map<String, dynamic> data = <String, dynamic>{}; |
||||||
|
data['FullName'] = fullName; |
||||||
|
data['M_DoctorID'] = mDoctorID; |
||||||
|
return data; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class AcDoctorAddressResponseModel { |
||||||
|
late String mDoctorAddressDescription; |
||||||
|
late String mDoctorAddressID; |
||||||
|
late bool isCheck; |
||||||
|
|
||||||
|
AcDoctorAddressResponseModel({ |
||||||
|
required this.mDoctorAddressDescription, |
||||||
|
required this.mDoctorAddressID, |
||||||
|
}); |
||||||
|
|
||||||
|
void setIsCheck(bool val) { |
||||||
|
isCheck = val; |
||||||
|
} |
||||||
|
|
||||||
|
AcDoctorAddressResponseModel.fromJson(Map<String, dynamic> json) { |
||||||
|
mDoctorAddressDescription = json['M_DoctorAddressDescription']; |
||||||
|
mDoctorAddressID = json['M_DoctorAddressID']; |
||||||
|
} |
||||||
|
|
||||||
|
Map<String, dynamic> toJson() { |
||||||
|
final Map<String, dynamic> data = <String, dynamic>{}; |
||||||
|
data['M_DoctorAddressDescription'] = mDoctorAddressDescription; |
||||||
|
data['M_DoctorAddressID'] = mDoctorAddressID; |
||||||
|
data['isCheck'] = isCheck; |
||||||
|
return data; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
// ignore_for_file: must_be_immutable |
||||||
|
|
||||||
|
import 'dart:async'; |
||||||
|
|
||||||
|
import 'package:equatable/equatable.dart'; |
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart'; |
||||||
|
|
||||||
|
final timerProvider = |
||||||
|
StateNotifierProvider.autoDispose<TimerNotifier, TimerState>( |
||||||
|
(ref) => TimerNotifier()); |
||||||
|
|
||||||
|
class TimerNotifier extends StateNotifier<TimerState> { |
||||||
|
TimerNotifier() : super(TimerStateInit()) { |
||||||
|
Timer.periodic(const Duration(seconds: 5), (tmr) { |
||||||
|
state = TimerStateTick(); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class TimerState extends Equatable { |
||||||
|
late DateTime dateTime; |
||||||
|
|
||||||
|
TimerState() { |
||||||
|
dateTime = DateTime.now(); |
||||||
|
} |
||||||
|
@override |
||||||
|
List<Object?> get props => [dateTime]; |
||||||
|
} |
||||||
|
|
||||||
|
class TimerStateInit extends TimerState {} |
||||||
|
|
||||||
|
class TimerStateTick extends TimerState {} |
@ -0,0 +1,90 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart'; |
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart'; |
||||||
|
import 'package:onemd/model/ac_doctor_model.dart'; |
||||||
|
|
||||||
|
import 'fx_data_mitra.dart'; |
||||||
|
import 'provider/doctor_address_lookup_provider.dart'; |
||||||
|
import 'provider/doctor_lookup_provider.dart'; |
||||||
|
import 'provider/selectedDoctorProvider.dart'; |
||||||
|
|
||||||
|
class FxDoctorAddress extends HookConsumerWidget { |
||||||
|
final double? width; |
||||||
|
const FxDoctorAddress({ |
||||||
|
Key? key, |
||||||
|
this.width, |
||||||
|
}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context, WidgetRef ref) { |
||||||
|
final listAddress = |
||||||
|
useState<List<AcDoctorAddressResponseModel>>(List.empty()); |
||||||
|
|
||||||
|
final doctorModel = ref.watch(selectedAcDoctorProvider); |
||||||
|
String doctorName = doctorModel?.fullName ?? ""; |
||||||
|
ref.listen(doctorAddressLookupProvider, (prev, next) { |
||||||
|
if (next is DoctorAddressLookupStateDone) { |
||||||
|
for (int idx = 0; idx < next.list.length; idx++) { |
||||||
|
next.list[idx].isCheck = false; |
||||||
|
} |
||||||
|
listAddress.value = next.list; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return Container( |
||||||
|
width: double.infinity, |
||||||
|
decoration: BoxDecoration( |
||||||
|
borderRadius: BorderRadius.circular(10), |
||||||
|
border: Border.all(color: Colors.blue.shade700), |
||||||
|
color: Colors.blue.shade100.withOpacity(0.3), |
||||||
|
), |
||||||
|
child: ConstrainedBox( |
||||||
|
constraints: BoxConstraints.loose(const Size(double.infinity, 150)), |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
crossAxisAlignment: CrossAxisAlignment.start, |
||||||
|
children: [ |
||||||
|
FxNormalBlueText( |
||||||
|
title: "Alamat dari $doctorName", |
||||||
|
isBold: true, |
||||||
|
), |
||||||
|
const SizedBox(height: 10), |
||||||
|
if (listAddress.value.isNotEmpty) |
||||||
|
ConstrainedBox( |
||||||
|
constraints: |
||||||
|
BoxConstraints.loose(const Size(double.infinity, 100)), |
||||||
|
child: ListView.builder( |
||||||
|
shrinkWrap: true, |
||||||
|
itemCount: listAddress.value.length, |
||||||
|
itemBuilder: (context, idx) { |
||||||
|
final model = listAddress.value[idx]; |
||||||
|
return Row( |
||||||
|
children: [ |
||||||
|
Checkbox( |
||||||
|
value: model.isCheck, |
||||||
|
onChanged: ((val) { |
||||||
|
final List<AcDoctorAddressResponseModel> list = |
||||||
|
List.empty(growable: true); |
||||||
|
list.addAll(listAddress.value); |
||||||
|
list[idx].isCheck = val ?? false; |
||||||
|
listAddress.value = list; |
||||||
|
}), |
||||||
|
), |
||||||
|
const SizedBox(width: 10), |
||||||
|
Expanded( |
||||||
|
child: FxNormalBlueText( |
||||||
|
title: model.mDoctorAddressDescription), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
}, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
import 'package:dio/dio.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart'; |
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart'; |
||||||
|
import 'package:onemd/widget/provider/selectedDoctorProvider.dart'; |
||||||
|
|
||||||
|
import '../model/ac_doctor_model.dart'; |
||||||
|
import '../provider/dio_provider.dart'; |
||||||
|
import '../repository/mitra_repository.dart'; |
||||||
|
import 'fx_data_mitra.dart'; |
||||||
|
import 'fx_text_field.dart'; |
||||||
|
import 'provider/doctor_address_lookup_provider.dart'; |
||||||
|
|
||||||
|
// ignore: must_be_immutable |
||||||
|
class FxAcDoctor extends HookConsumerWidget { |
||||||
|
FxAcDoctor({Key? key}) : super(key: key); |
||||||
|
CancelToken? cancelToken; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context, WidgetRef ref) { |
||||||
|
final errorMessage = useState(""); |
||||||
|
final ctrl = useTextEditingController(text: ""); |
||||||
|
|
||||||
|
cancelToken = CancelToken(); |
||||||
|
final fc = FocusNode(); |
||||||
|
final selectedDoctor = ref.read(selectedAcDoctorProvider); |
||||||
|
if (selectedDoctor != null) { |
||||||
|
ctrl.text = selectedDoctor.fullName; |
||||||
|
} |
||||||
|
ref.listen<AcDoctorResponseModel?>(selectedAcDoctorProvider, ((prev, next) { |
||||||
|
if (next != null) { |
||||||
|
print("Calling for " + next.fullName); |
||||||
|
ref |
||||||
|
.read(doctorAddressLookupProvider.notifier) |
||||||
|
.lookup(doctorID: next.mDoctorID); |
||||||
|
} |
||||||
|
})); |
||||||
|
|
||||||
|
return RawAutocomplete<AcDoctorResponseModel>( |
||||||
|
textEditingController: ctrl, |
||||||
|
displayStringForOption: (model) => model.fullName, |
||||||
|
focusNode: fc, |
||||||
|
fieldViewBuilder: (context, ctrl, fc, onChange) { |
||||||
|
return FxTextField( |
||||||
|
label: "Doctor", |
||||||
|
hint: "Doctor", |
||||||
|
fc: fc, |
||||||
|
ctrl: ctrl, |
||||||
|
); |
||||||
|
}, |
||||||
|
optionsBuilder: (tv) async { |
||||||
|
try { |
||||||
|
final dio = ref.read(dioProvider); |
||||||
|
await Future.delayed(const Duration(milliseconds: 300)); |
||||||
|
final resp = await MitraRepository(dio: dio) |
||||||
|
.lookupDoctor(query: ctrl.text, cancelToken: cancelToken); |
||||||
|
return resp; |
||||||
|
} catch (e) { |
||||||
|
errorMessage.value = "lookupDoctor Error"; |
||||||
|
} |
||||||
|
return []; |
||||||
|
}, |
||||||
|
optionsViewBuilder: (context, onSelect, listModel) { |
||||||
|
return Align( |
||||||
|
alignment: Alignment.topLeft, |
||||||
|
child: SizedBox( |
||||||
|
width: 400, |
||||||
|
child: Material( |
||||||
|
child: ListView.builder( |
||||||
|
itemCount: listModel.length, |
||||||
|
itemBuilder: (context, idx) { |
||||||
|
final model = listModel.elementAt(idx); |
||||||
|
return InkWell( |
||||||
|
onTap: () { |
||||||
|
ref.read(selectedAcDoctorProvider.notifier).state = |
||||||
|
model; |
||||||
|
onSelect(model); |
||||||
|
}, |
||||||
|
child: Container( |
||||||
|
color: (idx % 2 == 1) |
||||||
|
? Colors.blue.shade100.withOpacity(0.2) |
||||||
|
: null, |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Column( |
||||||
|
crossAxisAlignment: CrossAxisAlignment.start, |
||||||
|
children: [ |
||||||
|
FxNormalBlueText( |
||||||
|
title: model.fullName, |
||||||
|
isBold: true, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
)); |
||||||
|
}), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
|
||||||
|
class FxTextField extends StatelessWidget { |
||||||
|
final TextEditingController? ctrl; |
||||||
|
final FocusNode? fc; |
||||||
|
final String label; |
||||||
|
final String hint; |
||||||
|
final String? errorMessage; |
||||||
|
final bool? isReadOnly; |
||||||
|
final bool? isEnabled; |
||||||
|
final String? suffixText; |
||||||
|
|
||||||
|
const FxTextField({ |
||||||
|
Key? key, |
||||||
|
this.ctrl, |
||||||
|
this.fc, |
||||||
|
required this.label, |
||||||
|
required this.hint, |
||||||
|
this.errorMessage, |
||||||
|
this.isReadOnly, |
||||||
|
this.isEnabled, |
||||||
|
this.suffixText, |
||||||
|
}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return TextField( |
||||||
|
decoration: InputDecoration( |
||||||
|
border: OutlineInputBorder( |
||||||
|
borderRadius: BorderRadius.circular(10), |
||||||
|
), |
||||||
|
hintText: hint, |
||||||
|
labelText: label, |
||||||
|
errorText: errorMessage, |
||||||
|
suffixIcon: Padding( |
||||||
|
padding: const EdgeInsets.only(right: 10.0, top: 10.0), |
||||||
|
child: Text(suffixText ?? ""), |
||||||
|
), |
||||||
|
), |
||||||
|
controller: ctrl, |
||||||
|
focusNode: fc, |
||||||
|
readOnly: isReadOnly ?? false, |
||||||
|
enabled: isEnabled ?? true, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
import 'package:dio/dio.dart'; |
||||||
|
import 'package:equatable/equatable.dart'; |
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart'; |
||||||
|
|
||||||
|
import '../../model/ac_doctor_model.dart'; |
||||||
|
import '../../provider/dio_provider.dart'; |
||||||
|
import '../../repository/base_repository.dart'; |
||||||
|
import '../../repository/mitra_repository.dart'; |
||||||
|
|
||||||
|
final doctorAddressLookupProvider = StateNotifierProvider< |
||||||
|
DoctorAddressLookupNotifier, DoctorAddressLookupState>( |
||||||
|
(ref) => DoctorAddressLookupNotifier(ref: ref), |
||||||
|
); |
||||||
|
|
||||||
|
class DoctorAddressLookupNotifier |
||||||
|
extends StateNotifier<DoctorAddressLookupState> { |
||||||
|
final Ref ref; |
||||||
|
CancelToken? cancelToken; |
||||||
|
DoctorAddressLookupNotifier({ |
||||||
|
required this.ref, |
||||||
|
}) : super(DoctorAddressLookupStateInit()); |
||||||
|
void reset() { |
||||||
|
state = DoctorAddressLookupStateInit(); |
||||||
|
} |
||||||
|
|
||||||
|
void lookup({required String doctorID}) async { |
||||||
|
if (cancelToken == null) { |
||||||
|
cancelToken = CancelToken(); |
||||||
|
} else { |
||||||
|
cancelToken!.cancel(); |
||||||
|
cancelToken = CancelToken(); |
||||||
|
} |
||||||
|
try { |
||||||
|
state = DoctorAddressLookupStateLoading(); |
||||||
|
final dio = ref.read(dioProvider); |
||||||
|
final resp = await MitraRepository(dio: dio) |
||||||
|
.lookupDoctorAddress(doctorID: doctorID, cancelToken: cancelToken); |
||||||
|
state = DoctorAddressLookupStateDone(list: resp); |
||||||
|
} catch (e) { |
||||||
|
if (e is BaseRepositoryException) { |
||||||
|
state = DoctorAddressLookupStateError(message: e.message); |
||||||
|
} else { |
||||||
|
state = DoctorAddressLookupStateError(message: "Unknown Error"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract class DoctorAddressLookupState extends Equatable { |
||||||
|
final DateTime date; |
||||||
|
DoctorAddressLookupState() : date = DateTime.now(); |
||||||
|
@override |
||||||
|
List<Object?> get props => throw [date]; |
||||||
|
} |
||||||
|
|
||||||
|
class DoctorAddressLookupStateInit extends DoctorAddressLookupState {} |
||||||
|
|
||||||
|
class DoctorAddressLookupStateLoading extends DoctorAddressLookupState {} |
||||||
|
|
||||||
|
class DoctorAddressLookupStateError extends DoctorAddressLookupState { |
||||||
|
final String message; |
||||||
|
|
||||||
|
DoctorAddressLookupStateError({ |
||||||
|
required this.message, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
class DoctorAddressLookupStateDone extends DoctorAddressLookupState { |
||||||
|
final List<AcDoctorAddressResponseModel> list; |
||||||
|
|
||||||
|
DoctorAddressLookupStateDone({ |
||||||
|
required this.list, |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
import 'package:dio/dio.dart'; |
||||||
|
import 'package:equatable/equatable.dart'; |
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart'; |
||||||
|
|
||||||
|
import '../../model/ac_doctor_model.dart'; |
||||||
|
import '../../provider/dio_provider.dart'; |
||||||
|
import '../../repository/base_repository.dart'; |
||||||
|
import '../../repository/mitra_repository.dart'; |
||||||
|
|
||||||
|
final doctorLookupProvider = |
||||||
|
StateNotifierProvider<DoctorLookupNotifier, DoctorLookupState>( |
||||||
|
(ref) => DoctorLookupNotifier(ref: ref), |
||||||
|
); |
||||||
|
|
||||||
|
class DoctorLookupNotifier extends StateNotifier<DoctorLookupState> { |
||||||
|
final Ref ref; |
||||||
|
CancelToken? cancelToken; |
||||||
|
DoctorLookupNotifier({ |
||||||
|
required this.ref, |
||||||
|
}) : super(DoctorLookupStateInit()); |
||||||
|
void reset() { |
||||||
|
state = DoctorLookupStateInit(); |
||||||
|
} |
||||||
|
|
||||||
|
void lookup({required String query}) async { |
||||||
|
if (cancelToken == null) { |
||||||
|
cancelToken = CancelToken(); |
||||||
|
} else { |
||||||
|
cancelToken!.cancel(); |
||||||
|
cancelToken = CancelToken(); |
||||||
|
} |
||||||
|
try { |
||||||
|
state = DoctorLookupStateLoading(); |
||||||
|
final dio = ref.read(dioProvider); |
||||||
|
final resp = await MitraRepository(dio: dio) |
||||||
|
.lookupDoctor(query: query, cancelToken: cancelToken); |
||||||
|
state = DoctorLookupStateDone(list: resp); |
||||||
|
} catch (e) { |
||||||
|
if (e is BaseRepositoryException) { |
||||||
|
state = DoctorLookupStateError(message: e.message); |
||||||
|
} else { |
||||||
|
state = DoctorLookupStateError(message: "Unknown Error "); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract class DoctorLookupState extends Equatable { |
||||||
|
final DateTime date; |
||||||
|
DoctorLookupState() : date = DateTime.now(); |
||||||
|
@override |
||||||
|
List<Object?> get props => throw [date]; |
||||||
|
} |
||||||
|
|
||||||
|
class DoctorLookupStateInit extends DoctorLookupState {} |
||||||
|
|
||||||
|
class DoctorLookupStateLoading extends DoctorLookupState {} |
||||||
|
|
||||||
|
class DoctorLookupStateError extends DoctorLookupState { |
||||||
|
final String message; |
||||||
|
|
||||||
|
DoctorLookupStateError({ |
||||||
|
required this.message, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
class DoctorLookupStateDone extends DoctorLookupState { |
||||||
|
final List<AcDoctorResponseModel> list; |
||||||
|
|
||||||
|
DoctorLookupStateDone({ |
||||||
|
required this.list, |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart'; |
||||||
|
import 'package:onemd/model/ac_doctor_model.dart'; |
||||||
|
|
||||||
|
final selectedAcDoctorProvider = |
||||||
|
StateProvider<AcDoctorResponseModel?>((ref) => null); |
@ -1,16 +1,22 @@ |
|||||||
<!DOCTYPE html> |
<!DOCTYPE html> |
||||||
<html lang="en"> |
<html lang="en"> |
||||||
<head> |
|
||||||
<meta charset="UTF-8" /> |
<head> |
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
<meta charset="UTF-8" /> |
||||||
<title>Mitra Lab MasterData</title> |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||||
<script> |
<title>Mitra Lab MasterData</title> |
||||||
function fx_initial_route() { |
<script> |
||||||
return "/mdLabMitra"; |
function fx_initial_route() { |
||||||
} |
return "/mdLabMitra"; |
||||||
</script> |
} |
||||||
</head> |
function parent_home() { |
||||||
<body> |
parent.window.location.href = "/one-ui/"; |
||||||
<script src="main.dart.js" type="application/javascript"></script> |
} |
||||||
</body> |
</script> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
<script src="main.dart.js" type="application/javascript"></script> |
||||||
|
</body> |
||||||
|
|
||||||
</html> |
</html> |
||||||
|
Loading…
Reference in new issue