From dcfe9f6dcf3c4b882b319b9d2b541433462eaf65 Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Mon, 29 Dec 2025 10:09:57 +0330 Subject: [PATCH] feat: add stepper and page view components to SDUI, enhance form handling with dynamic visibility conditions and improved error handling --- .../widget/sdui/example_stepper.json | 9 + .../widget/sdui/form/sdui_form_widget.dart | 204 +++- .../widget/sdui/model/sdui_widget_model.dart | 1 - .../lib/presentation/widget/sdui/sdui.json | 1017 ++++++++++++++++- .../widget/sdui/widgets/page_view/README.md | 76 ++ .../widgets/page_view/example_page_view.json | 46 + .../page_view/model/page_view_sdui_model.dart | 18 + .../model/page_view_sdui_model.freezed.dart | 288 +++++ .../model/page_view_sdui_model.g.dart | 23 + .../widgets/page_view/page_view_sdui.dart | 61 + .../sdui/widgets/stepper/example_stepper.json | 9 + .../stepper/model/stepper_sdui_model.dart | 16 + .../model/stepper_sdui_model.freezed.dart | 283 +++++ .../stepper/model/stepper_sdui_model.g.dart | 21 + .../sdui/widgets/stepper/stepper_sdui.dart | 75 ++ 15 files changed, 2116 insertions(+), 31 deletions(-) create mode 100644 packages/chicken/lib/presentation/widget/sdui/example_stepper.json create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/page_view/README.md create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/page_view/example_page_view.json create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.freezed.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.g.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/stepper/example_stepper.json create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.freezed.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.g.dart create mode 100644 packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart diff --git a/packages/chicken/lib/presentation/widget/sdui/example_stepper.json b/packages/chicken/lib/presentation/widget/sdui/example_stepper.json new file mode 100644 index 0000000..839f434 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/example_stepper.json @@ -0,0 +1,9 @@ +{ + "type": "stepper", + "visible": true, + "data": { + "key": "activeStepperIndex", + "totalSteps": 5, + "activeStep": 0 + } +} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart b/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart index fa1b6e5..66174a4 100644 --- a/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart +++ b/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart @@ -10,6 +10,10 @@ import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/image_picker/i import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.dart'; import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/text_form_filed/model/text_form_field_sdui_model.dart'; import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart'; import 'package:rasadyar_core/core.dart'; class SDUIFormWidget extends StatelessWidget { @@ -19,6 +23,7 @@ class SDUIFormWidget extends StatelessWidget { final Function(String key, dynamic value)? onStateChanged; final Map>? images; final Function(String key, RxList images)? onImagesChanged; + final Map? pageControllers; const SDUIFormWidget({ super.key, @@ -28,6 +33,7 @@ class SDUIFormWidget extends StatelessWidget { this.onStateChanged, this.images, this.onImagesChanged, + this.pageControllers, }); @override @@ -74,6 +80,10 @@ class SDUIFormWidget extends StatelessWidget { return _buildRow(widgetModel); case 'sized_box': return _buildSizedBox(widgetModel); + case 'stepper': + return _buildStepper(widgetModel); + case 'page_view': + return _buildPageView(widgetModel); default: iLog('Unknown SDUI widget type: $type'); return const SizedBox.shrink(); @@ -113,10 +123,57 @@ class SDUIFormWidget extends StatelessWidget { Widget _buildCardLabelItem(SDUIWidgetModel widgetModel) { try { + // Check visible_condition if present in data + if (widgetModel.data != null) { + final visibleCondition = + widgetModel.data!['visible_condition'] as String?; + if (visibleCondition != null && visibleCondition.isNotEmpty) { + if (state != null) { + return Obx(() { + if (!_evaluateVisibleCondition(visibleCondition)) { + return const SizedBox.shrink(); + } + return _buildCardLabelItemInternal(widgetModel); + }); + } else { + // If state is null, only show first step (0) by default + // This allows the form to work even without state initialization + if (visibleCondition.contains('activeStepperIndex == 0')) { + return _buildCardLabelItemInternal(widgetModel); + } + return const SizedBox.shrink(); + } + } + } + + return _buildCardLabelItemInternal(widgetModel); + } catch (e, stackTrace) { + iLog('Error building card_label_item: $e'); + iLog('Stack trace: $stackTrace'); + iLog('WidgetModel data: ${widgetModel.data}'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.orange.withOpacity(0.1), + child: Text('Card Error: $e'), + ); + } + } + + Widget _buildCardLabelItemInternal(SDUIWidgetModel widgetModel) { + try { + // Remove visible_condition from data before creating CardLabelItemData + // because it's not part of the model + final dataWithoutCondition = widgetModel.data != null + ? Map.from(widgetModel.data!) + : null; + if (dataWithoutCondition != null) { + dataWithoutCondition.remove('visible_condition'); + } + final cardModel = CardLabelItemSDUI.fromJson({ 'type': widgetModel.type, 'visible': widgetModel.visible, - 'data': widgetModel.data, + 'data': dataWithoutCondition, 'child': widgetModel.child, }); @@ -203,6 +260,7 @@ class SDUIFormWidget extends StatelessWidget { onStateChanged: onStateChanged, images: images, onImagesChanged: onImagesChanged, + pageControllers: pageControllers, ); } catch (e) { iLog('Error building column child: $e'); @@ -415,4 +473,148 @@ class SDUIFormWidget extends StatelessWidget { return MainAxisAlignment.start; } } + + Widget _buildStepper(SDUIWidgetModel widgetModel) { + if (widgetModel.data == null) { + return const SizedBox.shrink(); + } + + try { + final stepperModel = StepperSDUIModel.fromJson(widgetModel.data!); + + return StepperSDUI(model: stepperModel, state: state); + } catch (e, stackTrace) { + iLog('Error building stepper: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.orange.withOpacity(0.1), + child: Text('Stepper Error: $e'), + ); + } + } + + Widget _buildPageView(SDUIWidgetModel widgetModel) { + if (widgetModel.data == null) { + return const SizedBox.shrink(); + } + + try { + final pageViewModel = PageViewSDUIModel.fromJson(widgetModel.data!); + + // Get PageController from map if key is provided + PageController? pageController; + if (pageViewModel.key != null && + pageViewModel.key!.isNotEmpty && + pageControllers != null && + pageControllers!.containsKey(pageViewModel.key!)) { + pageController = pageControllers![pageViewModel.key!]; + } + + // Build children if they exist + List pageChildren = []; + if (widgetModel.children != null && widgetModel.children!.isNotEmpty) { + pageChildren = widgetModel.children!.map((child) { + try { + return SDUIFormWidget( + model: SDUIWidgetModel.fromJson(child), + controllers: controllers, + state: state, + onStateChanged: onStateChanged, + images: images, + onImagesChanged: onImagesChanged, + pageControllers: pageControllers, + ); + } catch (e) { + iLog('Error building page_view child: $e'); + iLog('Child data: $child'); + return Container( + padding: EdgeInsets.all(8), + color: Colors.yellow.withOpacity(0.1), + child: Text('Child Error'), + ); + } + }).toList(); + } + + return PageViewSDUI( + model: pageViewModel, + controller: pageController, + children: pageChildren, + state: state, + controllers: controllers, + onStateChanged: onStateChanged, + images: images, + onImagesChanged: onImagesChanged, + ); + } catch (e, stackTrace) { + iLog('Error building page_view: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.blue.withOpacity(0.1), + child: Text('PageView Error: $e'), + ); + } + } + + bool _evaluateVisibleCondition(String condition) { + if (state == null) { + // If state is null, return true for first step (0) by default + // This allows the form to work even without state initialization + if (condition.contains('activeStepperIndex == 0')) { + return true; + } + return false; + } + + try { + // Simple condition evaluation + // Supports: variable == value + + if (condition.contains(' == ')) { + final parts = condition.split(' == '); + if (parts.length == 2) { + final variable = parts[0].trim(); + var value = parts[1].trim(); + + // Remove quotes if present + if ((value.startsWith("'") && value.endsWith("'")) || + (value.startsWith('"') && value.endsWith('"'))) { + value = value.substring(1, value.length - 1); + } + + final stateValue = state![variable]; + if (stateValue == null) { + // If variable doesn't exist in state, default to showing first step (0) + if (variable == 'activeStepperIndex' && value == '0') { + return true; + } + return false; + } + + // Handle int comparison + final intValue = int.tryParse(value); + if (intValue != null) { + if (stateValue is int) { + return stateValue == intValue; + } + if (stateValue is num) { + return stateValue.toInt() == intValue; + } + } + + // Handle string comparison + return stateValue.toString() == value; + } + } + + // If condition format is not recognized, return false + iLog('Unsupported visible_condition format: $condition'); + return false; + } catch (e) { + iLog('Error evaluating visible_condition: $e'); + return false; + } + } } diff --git a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart index 0ff4408..53be8d8 100644 --- a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart +++ b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart @@ -16,4 +16,3 @@ abstract class SDUIWidgetModel with _$SDUIWidgetModel { factory SDUIWidgetModel.fromJson(Map json) => _$SDUIWidgetModelFromJson(json); } - diff --git a/packages/chicken/lib/presentation/widget/sdui/sdui.json b/packages/chicken/lib/presentation/widget/sdui/sdui.json index 923ddfb..b738bb4 100644 --- a/packages/chicken/lib/presentation/widget/sdui/sdui.json +++ b/packages/chicken/lib/presentation/widget/sdui/sdui.json @@ -3,17 +3,26 @@ "type": "column", "visible": true, "data": { - "spacing": 10.0, - "crossAxisAlignment": "start" + "spacing": 30.0 }, "children": [ + { + "type": "stepper", + "visible": true, + "data": { + "key": "activeStepperIndex", + "totalSteps": 11, + "activeStep": 0 + } + }, { "type": "card_label_item", "visible": true, "data": { "title": "اطلاعات پایه واحد", "padding_horizontal": 12.0, - "padding_vertical": 11.0 + "padding_vertical": 11.0, + "visible_condition": "activeStepperIndex == 0" }, "child": { "type": "column", @@ -28,7 +37,8 @@ "data": { "key": "unit_name", "label": "نام واحد مرغداری", - "keyboard_type": "text" + "keyboard_type": "text", + "readonly": true } }, { @@ -36,18 +46,9 @@ "visible": true, "data": { "key": "breeding_unique_id", - "label": "(عدد معمولی)کد یکتا / شناسه واحد", - "keyboard_type": "number" - } - }, - { - "type": "text_form_field", - "visible": true, - "data": { - "key": "health", - "label": "پروانه بهداشتی", - "keyboard_type": "number" - + "label": "کد یکتا / شناسه واحد", + "keyboard_type": "text", + "readonly": true } }, { @@ -55,30 +56,581 @@ "visible": true, "data": { "key": "health_license", - "label": "عدد اعشاری ", + "label": "پروانه بهداشتی", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "tenant_status", + "label": "وضعیت مستاجر", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "tenant_name", + "label": "نام مستاجر", + "keyboard_type": "text", + "visible_condition": "tenant_status == 'دارد'" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "tenant_national_id", + "label": "کد ملی مستاجر", + "keyboard_type": "text", + "visible_condition": "tenant_status == 'دارد'" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "tenant_phone_number", + "label": "شماره تماس مستاجر", + "keyboard_type": "text", + "visible_condition": "tenant_status == 'دارد'" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "owner_national_code", + "label": "کد ملی بهره‌بردار", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "owner_phone_number", + "label": "شماره تماس بهره‌بردار", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "total_capacity", + "label": "ظرفیت اسمی سالن‌ها", + "keyboard_type": "number", + "comma_separator": true + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "اطلاعات جوجه ریزی", + "visible_condition": "activeStepperIndex == 1", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_date", + "label": "تاریخ جوجه ریزی", + "keyboard_type": "text", + "readonly": true, + "type": "date_picker" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "visit_date", + "label": "تاریخ بازدید", + "keyboard_type": "text", + "readonly": true, + "type": "date_picker" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_count", + "label": "تعداد جوجه‌ریزی اولیه", + "keyboard_type": "number", + "readonly": true, + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_breed", + "label": "نوع نژاد", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_average_weight", + "label": "میانگین وزن جوجه", + "keyboard_type": "number", + "comma_separator": true + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "وضعیت عمومی سالن", + "visible_condition": "activeStepperIndex == 2", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "image_picker", + "visible": true, + "data": { + "key": "pultry_images", + "label": "تعداد موجود فعلی" + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "sanitary_condition_of_the_hall", + "label": "وضعیت بهداشتی سالن", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "عالی", + "value": "عالی" + }, + { + "index": 1, + "label": "خوب", + "value": "خوب" + }, + { + "index": 2, + "label": "متوسط", + "value": "متوسط" + }, + { + "index": 3, + "label": "ضعیف", + "value": "ضعیف" + } + ] + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "ventilation_status", + "label": "وضعیت تهویه", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "عالی", + "value": "عالی" + }, + { + "index": 1, + "label": "خوب", + "value": "خوب" + }, + { + "index": 2, + "label": "متوسط", + "value": "متوسط" + }, + { + "index": 3, + "label": "ضعیف", + "value": "ضعیف" + } + ] + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "bedding_status", + "label": "وضعیت بستر", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "خشک", + "value": "خشک" + }, + { + "index": 1, + "label": "نیمه‌مرطوب", + "value": "نیمه‌مرطوب" + }, + { + "index": 2, + "label": "مرطوب", + "value": "مرطوب" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_temperature", + "label": "دمای سالن", + "keyboard_type": "number" + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "water_quality", + "label": "آب مصرفی", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "چاه", + "value": "چاه" + }, + { + "index": 1, + "label": "شهری", + "value": "شهری" + }, + { + "index": 2, + "label": "تصفیه‌شده", + "value": "تصفیه‌شده" + }, + { + "index": 3, + "label": "حمل تانکر", + "value": "حمل تانکر" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "water_hardness", + "label": "درصد سختی آب (PPM)", "keyboard_type": "number", "decimal": true } - }, + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "تلفات", + "visible_condition": "activeStepperIndex == 3", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ { "type": "text_form_field", "visible": true, "data": { - "key": "health_q", - "label": "عدد اعشاری با 2 رقم اعشار", + "key": "normal_losses", + "label": "تعداد تلفات عادی دوره", "keyboard_type": "number", - "decimal": true, - "decimal_places": 2 + "comma_separator": true } }, { "type": "text_form_field", "visible": true, "data": { - "key": "unit_date", - "label": "نام تاریییییییییییخ", + "key": "abnormal_losses", + "label": "تلفات غیرعادی", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "cause_of_unusual_casualties", + "label": "علت تلفات غیرعادی", + "placeholder": "علت تلفات غیرعادی", + "items": [ + "بیماری", + "قطعی برق", + "استرس گرمایی", + "مشکلات دان", + "کیفیت جوجه", + "سایر (شرح…)" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "other_cause_of_unusual_casualties", + "label": "علت تلفات غیرعادی", "keyboard_type": "text", - "type": "date_picker" + "visible_condition": "cause_of_unusual_casualties == 'سایر (شرح…)'" + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "type_of_disease", + "label": "نوع بیماری تشخیص داده‌شده / مشکوک", + "placeholder": "نوع بیماری تشخیص داده‌شده / مشکوک", + "items": [ + "آنفلوانزا", + "نیوکاسل", + "IB", + "عفونت‌های باکتریایی", + "مشکلات گوارشی", + "سایر (شرح)" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "other_type_of_disease", + "label": "نوع بیماری", + "keyboard_type": "text", + "visible_condition": "type_of_disease == 'سایر (شرح)'" + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "sampling_done", + "label": "نمونه‌برداری انجام‌شده", + "placeholder": "نمونه‌برداری انجام‌شده", + "items": [ + "انجام شد", + "انجام نشد" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "sample_type", + "label": "نوع نمونه", + "selectedIndex": -1, + "visible_condition": "sampling_done == 'انجام شد'", + "options": [ + { + "index": 0, + "label": "لاشه", + "value": "لاشه" + }, + { + "index": 1, + "label": "آب", + "value": "آب" + }, + { + "index": 2, + "label": "دانه", + "value": "دانه" + } + ] + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "مسئول فنی", + "visible_condition": "activeStepperIndex == 4", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "technical_health_officer_name", + "label": "نام مسئول فنی بهداشتی", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "technical_engineering_officer_name", + "label": "نام مسئول فنی نظام مهندسی", + "keyboard_type": "text" + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "نهاده و خوراک", + "visible_condition": "activeStepperIndex == 5", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "dropdown", + "visible": true, + "data": { + "key": "input_status", + "label": "وضعیت نهاده", + "placeholder": "وضعیت نهاده", + "items": [ + "وابسته", + "مستقل" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "company_name", + "label": "نام کارخانه", + "keyboard_type": "text", + "visible_condition": "input_status == 'وابسته'" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "tracking_code", + "label": "شناسه خرید یا کد پیگیری", + "keyboard_type": "text", + "visible_condition": "input_status == 'مستقل'" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "input_inventory_until_visit", + "label": "موجودی نهاده مصرفی تا روز بازدید (کیلوگرم)", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "type_of_grain", + "label": "نوع دان", + "placeholder": "نوع دان", + "items": [ + "آردی", + "پلت", + "کرامبل و اکسترود", + "مش", + "پوره و وال" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "input_inventory_in_warehouse", + "label": "موجودی نهاده موجود در انبار (کیلوگرم)", + "keyboard_type": "number", + "comma_separator": true } }, { @@ -106,6 +658,235 @@ } ] } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "زیرساخت و انرژی", + "visible_condition": "activeStepperIndex == 6", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "generator_type", + "label": "نوع ژنراتور", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "generator_model", + "label": "مدل ژنراتور", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "generator_count", + "label": "تعداد ژنراتور", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "fuel_type", + "label": "نوع سوخت", + "placeholder": "نوع سوخت", + "items": [ + "گازوئیل", + "نفت گاز" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "generator_capacity", + "label": "ظرفیت (KVA)", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "generator_operating_status", + "label": "وضعیت عملکرد ژنراتور", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "سالم", + "value": "سالم" + }, + { + "index": 1, + "label": "نیمه‌سالم", + "value": "نیمه‌سالم" + }, + { + "index": 2, + "label": "معیوب", + "value": "معیوب" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "emergency_fuel_inventory", + "label": "میزان موجودی سوخت اضطراری (لیتر)", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "power_cut_history", + "label": "سابقه قطعی برق دوره جاری", + "placeholder": "سابقه قطعی برق دوره جاری", + "items": [ + "ندارد", + "دارد" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "power_cut_duration", + "label": "مدت قطعی", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "power_cut_hour", + "label": "ساعت قطعی", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "additional_notes", + "label": "توضیحات تکمیلی", + "keyboard_type": "text", + "max_line": 5, + "min_line": 5 + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "نیروی انسانی", + "visible_condition": "activeStepperIndex == 7", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "employed_workers_count", + "label": "تعداد افراد شاغل", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "native_workers_count", + "label": "تعداد افراد بومی", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "non_native_workers_count", + "label": "تعداد افراد غیر بومی", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "worker_contract_status", + "label": "وضعیت قرارداد کارگران", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "دائم", + "value": "دائم" + }, + { + "index": 1, + "label": "موقت", + "value": "موقت" + }, + { + "index": 2, + "label": "روزمزدی", + "value": "روزمزدی" + } + ] + } }, { "type": "dropdown", @@ -121,7 +902,122 @@ "selectedValue": null, "enabled": true } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "تسهیلات و حمایت‌ها", + "visible_condition": "activeStepperIndex == 8", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "dropdown", + "visible": true, + "data": { + "key": "has_facilities", + "label": "تسهیلات دریافتی فعال", + "placeholder": "تسهیلات دریافتی فعال", + "items": [ + "دارد", + "ندارد" + ], + "selectedValue": null, + "enabled": true + } }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "facility_type", + "label": "نوع تسهیلات", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "facility_amount", + "label": "مبلغ", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "payment_year", + "label": "سال دریافت", + "keyboard_type": "text", + "readonly": true, + "type": "date_picker" + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "overdue_status", + "label": "وضعیت بازپرداخت", + "placeholder": "وضعیت بازپرداخت", + "items": [ + "دارای معوقه", + "منظم" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "dropdown", + "visible": true, + "data": { + "key": "new_beneficiary_request", + "label": "درخواست جدید بهره بردار", + "placeholder": "درخواست جدید بهره بردار", + "items": [ + "نهاده", + "تسهیلات", + "واکسن", + "تجهیزات" + ], + "selectedValue": null, + "enabled": true + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "مستندات", + "visible_condition": "activeStepperIndex == 9", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ { "type": "image_picker", "visible": true, @@ -135,10 +1031,73 @@ "type": "image_picker", "visible": true, "data": { - "key": "hall_images_klllll", - "label": "ثبت عکس", - "required": true, - "max_images": 3 + "key": "input_warehouse_images", + "label": "ثبت عکس انبار نهاده‌ها" + } + }, + { + "type": "image_picker", + "visible": true, + "data": { + "key": "losses_images", + "label": "ثبت عکس تلفات" + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "data": { + "title": "جمع‌بندی بازرس", + "visible_condition": "activeStepperIndex == 10", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "chip_selection", + "visible": true, + "data": { + "key": "inspector_conclusion", + "label": "وضعیت کلی واحد", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "تایید شده", + "value": "تایید شده" + }, + { + "index": 1, + "label": "نیازمند اصلاح", + "value": "نیازمند اصلاح" + }, + { + "index": 2, + "label": "پرریسک", + "value": "پرریسک" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "inspector_conclusion_description", + "label": "توصیه‌ها / اخطارها / اقدامات اصلاحی ...", + "hint_text": "توصیه‌ها / اخطارها / اقدامات اصلاحی ...", + "keyboard_type": "text", + "max_line": 5, + "min_line": 5 } } ] diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/README.md b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/README.md new file mode 100644 index 0000000..db29636 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/README.md @@ -0,0 +1,76 @@ +# PageView SDUI Widget + +این ویجت برای استفاده از PageView در ساختار SDUI طراحی شده است. + +## استفاده در کد + +```dart +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart'; + +// ایجاد مدل +final pageViewModel = PageViewSDUIModel( + key: 'pageController', // کلید برای دسترسی به PageController + reverse: true, + physics: 'neverScrollable', // 'neverScrollable', 'bouncing', 'clamping', 'alwaysScrollable' + itemCount: 5, +); + +// استفاده از ویجت +PageViewSDUI( + model: pageViewModel, + controller: controller.pageController, // PageController از logic + children: pages, // لیست صفحات +) +``` + +## استفاده در JSON + +```json +{ + "type": "page_view", + "visible": true, + "data": { + "key": "pageController", + "reverse": true, + "physics": "neverScrollable", + "itemCount": 5 + }, + "children": [ + { + "type": "column", + "visible": true, + "children": [...] + } + ] +} +``` + +## پارامترها + +- `key`: کلید برای دسترسی به PageController از map +- `reverse`: جهت معکوس (پیش‌فرض: true) +- `physics`: نوع فیزیک اسکرول ('neverScrollable', 'bouncing', 'clamping', 'alwaysScrollable') +- `itemCount`: تعداد آیتم‌ها (پیش‌فرض: تعداد children) + +## مثال استفاده در create_inspection_bottom_sheet.dart + +```dart +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart'; + +// در build method: +Expanded( + child: PageViewSDUI( + model: PageViewSDUIModel( + key: 'pageController', + reverse: true, + physics: 'neverScrollable', + itemCount: pages.length, + ), + controller: controller.pageController, + children: pages, + ), +), +``` + diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/example_page_view.json b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/example_page_view.json new file mode 100644 index 0000000..a432a55 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/example_page_view.json @@ -0,0 +1,46 @@ +{ + "type": "page_view", + "visible": true, + "data": { + "key": "pageController", + "reverse": true, + "physics": "neverScrollable", + "itemCount": 5 + }, + "children": [ + { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "field1", + "label": "Field 1" + } + } + ] + }, + { + "type": "column", + "visible": true, + "data": { + "spacing": 10.0 + }, + "children": [ + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "field2", + "label": "Field 2" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart new file mode 100644 index 0000000..21ec13a --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'page_view_sdui_model.freezed.dart'; +part 'page_view_sdui_model.g.dart'; + +@freezed +abstract class PageViewSDUIModel with _$PageViewSDUIModel { + const factory PageViewSDUIModel({ + String? key, + bool? reverse, + String? physics, // 'neverScrollable', 'bouncing', 'clamping', 'alwaysScrollable' + int? itemCount, + }) = _PageViewSDUIModel; + + factory PageViewSDUIModel.fromJson(Map json) => + _$PageViewSDUIModelFromJson(json); +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.freezed.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.freezed.dart new file mode 100644 index 0000000..2fd550a --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.freezed.dart @@ -0,0 +1,288 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'page_view_sdui_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$PageViewSDUIModel { + + String? get key; bool? get reverse; String? get physics;// 'neverScrollable', 'bouncing', 'clamping', 'alwaysScrollable' + int? get itemCount; +/// Create a copy of PageViewSDUIModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$PageViewSDUIModelCopyWith get copyWith => _$PageViewSDUIModelCopyWithImpl(this as PageViewSDUIModel, _$identity); + + /// Serializes this PageViewSDUIModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is PageViewSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.reverse, reverse) || other.reverse == reverse)&&(identical(other.physics, physics) || other.physics == physics)&&(identical(other.itemCount, itemCount) || other.itemCount == itemCount)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,reverse,physics,itemCount); + +@override +String toString() { + return 'PageViewSDUIModel(key: $key, reverse: $reverse, physics: $physics, itemCount: $itemCount)'; +} + + +} + +/// @nodoc +abstract mixin class $PageViewSDUIModelCopyWith<$Res> { + factory $PageViewSDUIModelCopyWith(PageViewSDUIModel value, $Res Function(PageViewSDUIModel) _then) = _$PageViewSDUIModelCopyWithImpl; +@useResult +$Res call({ + String? key, bool? reverse, String? physics, int? itemCount +}); + + + + +} +/// @nodoc +class _$PageViewSDUIModelCopyWithImpl<$Res> + implements $PageViewSDUIModelCopyWith<$Res> { + _$PageViewSDUIModelCopyWithImpl(this._self, this._then); + + final PageViewSDUIModel _self; + final $Res Function(PageViewSDUIModel) _then; + +/// Create a copy of PageViewSDUIModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? key = freezed,Object? reverse = freezed,Object? physics = freezed,Object? itemCount = freezed,}) { + return _then(_self.copyWith( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,reverse: freezed == reverse ? _self.reverse : reverse // ignore: cast_nullable_to_non_nullable +as bool?,physics: freezed == physics ? _self.physics : physics // ignore: cast_nullable_to_non_nullable +as String?,itemCount: freezed == itemCount ? _self.itemCount : itemCount // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [PageViewSDUIModel]. +extension PageViewSDUIModelPatterns on PageViewSDUIModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _PageViewSDUIModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _PageViewSDUIModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _PageViewSDUIModel value) $default,){ +final _that = this; +switch (_that) { +case _PageViewSDUIModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _PageViewSDUIModel value)? $default,){ +final _that = this; +switch (_that) { +case _PageViewSDUIModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? key, bool? reverse, String? physics, int? itemCount)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _PageViewSDUIModel() when $default != null: +return $default(_that.key,_that.reverse,_that.physics,_that.itemCount);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? key, bool? reverse, String? physics, int? itemCount) $default,) {final _that = this; +switch (_that) { +case _PageViewSDUIModel(): +return $default(_that.key,_that.reverse,_that.physics,_that.itemCount);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? key, bool? reverse, String? physics, int? itemCount)? $default,) {final _that = this; +switch (_that) { +case _PageViewSDUIModel() when $default != null: +return $default(_that.key,_that.reverse,_that.physics,_that.itemCount);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _PageViewSDUIModel implements PageViewSDUIModel { + const _PageViewSDUIModel({this.key, this.reverse, this.physics, this.itemCount}); + factory _PageViewSDUIModel.fromJson(Map json) => _$PageViewSDUIModelFromJson(json); + +@override final String? key; +@override final bool? reverse; +@override final String? physics; +// 'neverScrollable', 'bouncing', 'clamping', 'alwaysScrollable' +@override final int? itemCount; + +/// Create a copy of PageViewSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$PageViewSDUIModelCopyWith<_PageViewSDUIModel> get copyWith => __$PageViewSDUIModelCopyWithImpl<_PageViewSDUIModel>(this, _$identity); + +@override +Map toJson() { + return _$PageViewSDUIModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PageViewSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.reverse, reverse) || other.reverse == reverse)&&(identical(other.physics, physics) || other.physics == physics)&&(identical(other.itemCount, itemCount) || other.itemCount == itemCount)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,reverse,physics,itemCount); + +@override +String toString() { + return 'PageViewSDUIModel(key: $key, reverse: $reverse, physics: $physics, itemCount: $itemCount)'; +} + + +} + +/// @nodoc +abstract mixin class _$PageViewSDUIModelCopyWith<$Res> implements $PageViewSDUIModelCopyWith<$Res> { + factory _$PageViewSDUIModelCopyWith(_PageViewSDUIModel value, $Res Function(_PageViewSDUIModel) _then) = __$PageViewSDUIModelCopyWithImpl; +@override @useResult +$Res call({ + String? key, bool? reverse, String? physics, int? itemCount +}); + + + + +} +/// @nodoc +class __$PageViewSDUIModelCopyWithImpl<$Res> + implements _$PageViewSDUIModelCopyWith<$Res> { + __$PageViewSDUIModelCopyWithImpl(this._self, this._then); + + final _PageViewSDUIModel _self; + final $Res Function(_PageViewSDUIModel) _then; + +/// Create a copy of PageViewSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? key = freezed,Object? reverse = freezed,Object? physics = freezed,Object? itemCount = freezed,}) { + return _then(_PageViewSDUIModel( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,reverse: freezed == reverse ? _self.reverse : reverse // ignore: cast_nullable_to_non_nullable +as bool?,physics: freezed == physics ? _self.physics : physics // ignore: cast_nullable_to_non_nullable +as String?,itemCount: freezed == itemCount ? _self.itemCount : itemCount // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.g.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.g.dart new file mode 100644 index 0000000..6849c99 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'page_view_sdui_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_PageViewSDUIModel _$PageViewSDUIModelFromJson(Map json) => + _PageViewSDUIModel( + key: json['key'] as String?, + reverse: json['reverse'] as bool?, + physics: json['physics'] as String?, + itemCount: (json['item_count'] as num?)?.toInt(), + ); + +Map _$PageViewSDUIModelToJson(_PageViewSDUIModel instance) => + { + 'key': instance.key, + 'reverse': instance.reverse, + 'physics': instance.physics, + 'item_count': instance.itemCount, + }; diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart new file mode 100644 index 0000000..9951265 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/page_view/page_view_sdui.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/page_view/model/page_view_sdui_model.dart'; +import 'package:rasadyar_core/core.dart'; + +class PageViewSDUI extends StatelessWidget { + final PageViewSDUIModel model; + final PageController? controller; + final List children; + final RxMap? state; + final Map? controllers; + final Function(String key, dynamic value)? onStateChanged; + final Map>? images; + final Function(String key, RxList images)? onImagesChanged; + + const PageViewSDUI({ + super.key, + required this.model, + required this.children, + this.controller, + this.state, + this.controllers, + this.onStateChanged, + this.images, + this.onImagesChanged, + }); + + ScrollPhysics? _parsePhysics(String? physics) { + switch (physics) { + case 'neverScrollable': + return const NeverScrollableScrollPhysics(); + case 'bouncing': + return const BouncingScrollPhysics(); + case 'clamping': + return const ClampingScrollPhysics(); + case 'alwaysScrollable': + return const AlwaysScrollableScrollPhysics(); + default: + return const NeverScrollableScrollPhysics(); + } + } + + @override + Widget build(BuildContext context) { + final physics = _parsePhysics(model.physics); + final reverse = model.reverse ?? true; + final itemCount = model.itemCount ?? children.length; + + return PageView.builder( + physics: physics, + reverse: reverse, + controller: controller, + itemCount: itemCount, + itemBuilder: (context, index) { + if (index < children.length) { + return children[index]; + } + return const SizedBox.shrink(); + }, + ); + } +} diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/example_stepper.json b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/example_stepper.json new file mode 100644 index 0000000..839f434 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/example_stepper.json @@ -0,0 +1,9 @@ +{ + "type": "stepper", + "visible": true, + "data": { + "key": "activeStepperIndex", + "totalSteps": 5, + "activeStep": 0 + } +} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart new file mode 100644 index 0000000..08a639b --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'stepper_sdui_model.freezed.dart'; +part 'stepper_sdui_model.g.dart'; + +@freezed +abstract class StepperSDUIModel with _$StepperSDUIModel { + const factory StepperSDUIModel({ + String? key, + int? totalSteps, + int? activeStep, + }) = _StepperSDUIModel; + + factory StepperSDUIModel.fromJson(Map json) => + _$StepperSDUIModelFromJson(json); +} diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.freezed.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.freezed.dart new file mode 100644 index 0000000..4291d92 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.freezed.dart @@ -0,0 +1,283 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stepper_sdui_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$StepperSDUIModel { + + String? get key; int? get totalSteps; int? get activeStep; +/// Create a copy of StepperSDUIModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$StepperSDUIModelCopyWith get copyWith => _$StepperSDUIModelCopyWithImpl(this as StepperSDUIModel, _$identity); + + /// Serializes this StepperSDUIModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is StepperSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.totalSteps, totalSteps) || other.totalSteps == totalSteps)&&(identical(other.activeStep, activeStep) || other.activeStep == activeStep)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,totalSteps,activeStep); + +@override +String toString() { + return 'StepperSDUIModel(key: $key, totalSteps: $totalSteps, activeStep: $activeStep)'; +} + + +} + +/// @nodoc +abstract mixin class $StepperSDUIModelCopyWith<$Res> { + factory $StepperSDUIModelCopyWith(StepperSDUIModel value, $Res Function(StepperSDUIModel) _then) = _$StepperSDUIModelCopyWithImpl; +@useResult +$Res call({ + String? key, int? totalSteps, int? activeStep +}); + + + + +} +/// @nodoc +class _$StepperSDUIModelCopyWithImpl<$Res> + implements $StepperSDUIModelCopyWith<$Res> { + _$StepperSDUIModelCopyWithImpl(this._self, this._then); + + final StepperSDUIModel _self; + final $Res Function(StepperSDUIModel) _then; + +/// Create a copy of StepperSDUIModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? key = freezed,Object? totalSteps = freezed,Object? activeStep = freezed,}) { + return _then(_self.copyWith( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,totalSteps: freezed == totalSteps ? _self.totalSteps : totalSteps // ignore: cast_nullable_to_non_nullable +as int?,activeStep: freezed == activeStep ? _self.activeStep : activeStep // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [StepperSDUIModel]. +extension StepperSDUIModelPatterns on StepperSDUIModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _StepperSDUIModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _StepperSDUIModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _StepperSDUIModel value) $default,){ +final _that = this; +switch (_that) { +case _StepperSDUIModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _StepperSDUIModel value)? $default,){ +final _that = this; +switch (_that) { +case _StepperSDUIModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? key, int? totalSteps, int? activeStep)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _StepperSDUIModel() when $default != null: +return $default(_that.key,_that.totalSteps,_that.activeStep);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? key, int? totalSteps, int? activeStep) $default,) {final _that = this; +switch (_that) { +case _StepperSDUIModel(): +return $default(_that.key,_that.totalSteps,_that.activeStep);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? key, int? totalSteps, int? activeStep)? $default,) {final _that = this; +switch (_that) { +case _StepperSDUIModel() when $default != null: +return $default(_that.key,_that.totalSteps,_that.activeStep);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _StepperSDUIModel implements StepperSDUIModel { + const _StepperSDUIModel({this.key, this.totalSteps, this.activeStep}); + factory _StepperSDUIModel.fromJson(Map json) => _$StepperSDUIModelFromJson(json); + +@override final String? key; +@override final int? totalSteps; +@override final int? activeStep; + +/// Create a copy of StepperSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$StepperSDUIModelCopyWith<_StepperSDUIModel> get copyWith => __$StepperSDUIModelCopyWithImpl<_StepperSDUIModel>(this, _$identity); + +@override +Map toJson() { + return _$StepperSDUIModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _StepperSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.totalSteps, totalSteps) || other.totalSteps == totalSteps)&&(identical(other.activeStep, activeStep) || other.activeStep == activeStep)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,totalSteps,activeStep); + +@override +String toString() { + return 'StepperSDUIModel(key: $key, totalSteps: $totalSteps, activeStep: $activeStep)'; +} + + +} + +/// @nodoc +abstract mixin class _$StepperSDUIModelCopyWith<$Res> implements $StepperSDUIModelCopyWith<$Res> { + factory _$StepperSDUIModelCopyWith(_StepperSDUIModel value, $Res Function(_StepperSDUIModel) _then) = __$StepperSDUIModelCopyWithImpl; +@override @useResult +$Res call({ + String? key, int? totalSteps, int? activeStep +}); + + + + +} +/// @nodoc +class __$StepperSDUIModelCopyWithImpl<$Res> + implements _$StepperSDUIModelCopyWith<$Res> { + __$StepperSDUIModelCopyWithImpl(this._self, this._then); + + final _StepperSDUIModel _self; + final $Res Function(_StepperSDUIModel) _then; + +/// Create a copy of StepperSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? key = freezed,Object? totalSteps = freezed,Object? activeStep = freezed,}) { + return _then(_StepperSDUIModel( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,totalSteps: freezed == totalSteps ? _self.totalSteps : totalSteps // ignore: cast_nullable_to_non_nullable +as int?,activeStep: freezed == activeStep ? _self.activeStep : activeStep // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.g.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.g.dart new file mode 100644 index 0000000..e7d003c --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stepper_sdui_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_StepperSDUIModel _$StepperSDUIModelFromJson(Map json) => + _StepperSDUIModel( + key: json['key'] as String?, + totalSteps: (json['total_steps'] as num?)?.toInt(), + activeStep: (json['active_step'] as num?)?.toInt(), + ); + +Map _$StepperSDUIModelToJson(_StepperSDUIModel instance) => + { + 'key': instance.key, + 'total_steps': instance.totalSteps, + 'active_step': instance.activeStep, + }; diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart new file mode 100644 index 0000000..8060ba1 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart'; +import 'package:rasadyar_core/core.dart'; + +class StepperSDUI extends StatelessWidget { + final StepperSDUIModel model; + final RxMap? state; + + const StepperSDUI({super.key, required this.model, this.state}); + + @override + Widget build(BuildContext context) { + final totalSteps = model.totalSteps ?? 5; + + return Obx(() { + final activeStep = state?[model.key] as int? ?? model.activeStep ?? 0; + + return Directionality( + textDirection: TextDirection.ltr, + child: SizedBox( + height: 24, + width: Get.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildSteps(totalSteps, activeStep), + ), + ), + ); + }); + } + + List _buildSteps(int totalSteps, int activeStep) { + final List widgets = []; + + for (int i = 0; i < totalSteps; i++) { + // Add step circle + widgets.add( + Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: activeStep >= i + ? AppColor.greenNormalHover + : AppColor.whiteNormalActive, + shape: BoxShape.circle, + ), + width: 24.w, + height: 24.h, + child: Text( + '${i + 1}', + textAlign: TextAlign.center, + style: AppFonts.yekan16.copyWith( + color: activeStep >= i ? Colors.white : AppColor.iconColor, + ), + ), + ), + ); + + // Add divider between steps (except after last step) + if (i < totalSteps - 1) { + widgets.add( + Expanded( + child: Divider( + color: activeStep >= i + 1 + ? AppColor.greenNormalHover + : AppColor.whiteNormalActive, + thickness: 8, + ), + ), + ); + } + } + + return widgets; + } +}