// ignore_for_file: use_build_context_synchronously, deprecated_member_use_from_same_package import 'dart:math'; import 'package:animated_custom_dropdown/custom_dropdown.dart'; import 'package:cross_file/cross_file.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:hoshan/core/gen/assets.gen.dart'; import 'package:hoshan/core/services/api/dio_service.dart'; import 'package:hoshan/core/services/file_manager/pick_file_services.dart'; import 'package:hoshan/core/utils/strings.dart'; import 'package:hoshan/data/model/ai/bots_model.dart'; import 'package:hoshan/data/model/assistant_personal_info_model.dart'; import 'package:hoshan/data/model/create_assistant_request_model.dart'; import 'package:hoshan/data/model/edittext_state_model.dart'; import 'package:hoshan/data/model/tools_categories_model.dart'; import 'package:hoshan/ui/screens/main/assistant/bloc/create_assistant_bloc.dart'; import 'package:hoshan/ui/screens/main/assistant/bloc/personal_assistants_bloc.dart'; import 'package:hoshan/ui/screens/main/assistant/cubit/delete_assistant_cubit.dart'; import 'package:hoshan/ui/screens/main/home/bloc/bots_bloc.dart'; import 'package:hoshan/ui/screens/main/forum/cubit/category_cubit.dart'; import 'package:hoshan/ui/theme/colors.dart'; import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart'; import 'package:hoshan/ui/theme/responsive.dart'; import 'package:hoshan/ui/theme/text.dart'; import 'package:hoshan/ui/widgets/components/button/loading_button.dart'; import 'package:hoshan/ui/widgets/components/dialog/dialog_handler.dart'; import 'package:hoshan/ui/widgets/components/snackbar/snackbar_manager.dart'; import 'package:hoshan/ui/widgets/components/text/filled_text_field.dart'; import 'package:hoshan/ui/widgets/components/text/labeled_text_field.dart'; import 'package:hoshan/ui/widgets/sections/header/reversible_appbar.dart'; import 'package:string_validator/string_validator.dart'; class CreateAssistantPage extends StatefulWidget { final AssistantPersonalInfo? info; const CreateAssistantPage({super.key, this.info}); @override State createState() => _CreateAssistantPageState(); } class _CreateAssistantPageState extends State { final ValueNotifier selectedImageFileLoading = ValueNotifier(false); final ValueNotifier> selectedFiles = ValueNotifier([]); final ValueNotifier> links = ValueNotifier([]); // int? botId; // final ValueNotifier botIdError = ValueNotifier(false); int? categoryId; ValueNotifier categoryIdError = ValueNotifier(false); Bots? initialItem; ValueNotifier isPublic = ValueNotifier(false); final EdittextStateModel linkFormState = EdittextStateModel(hintText: 'لینک‌ خود را وارد کنید.'); final EdittextStateModel userFormState = EdittextStateModel( label: 'خالق دستیار ', hintText: 'یک نام کاربری برای خود انتخاب کنید.', ); final EdittextStateModel nameFormState = EdittextStateModel( label: 'نام دستیار', hintText: 'یک نام برای دستیار خود انتخاب کنید.', tooltipHint: 'در این بخش باید نامی (شامل حروف فارسی و انگلیسی و علائم و اعداد) برای دستیار هوش مصنوعی خود انتخاب کنید. این نام میتواند متناسب با عملکرد یا هدف دستیار باشد. انتخاب نام مناسب به شما کمک می‌کند تا دستیارهای مختلف خود را به راحتی مدیریت و شناسایی کنید.', ); final EdittextStateModel promptFormState = EdittextStateModel( label: 'پرامپت و دستورالعمل ', hintText: 'این دستیار قرار است چه کارهایی انجام دهد؟', tooltipHint: 'در این قسمت باید دقیقاً مشخص کنید که دستیار شما چه کارهایی را باید انجام دهد. این شامل دستور العمل ها، قوانین و محدودیتهایی است که باید رعایت شود. به عنوان مثال اگر دستیار شما برای کمک در نوشتن مقالات استفاده می شود میتوانید توضیح دهید که دستیار باید از منابع معتبر استفاده کند و اطلاعات را به صورت ساده و قابل فهم ارائه دهد. همچنین می توانید مدل را محدود کنید که در مورد مسائل دیگر جوابی ندهد. این قسمت بسیار حیاتی است زیرا تعیین می کند که دستیار چگونه باید رفتار کند و چه نوع خروجی هایی ارائه دهد.', ); final EdittextStateModel descriptionFormState = EdittextStateModel( label: 'توضیحات', hintText: 'توضیح دهید چه کارهایی از این بات بر می‌آید.', tooltipHint: 'در این قسمت میتوانید به کاربرانی که قرار است از این دستیار استفاده کنند توضیحاتی راجع به دستیار خود بدهید.'); bool checkReqs() { // botIdError.value = botId == null; categoryIdError.value = categoryId == null; return // botIdError.value && categoryIdError.value; } @override void initState() { super.initState(); if (widget.info != null) { final info = widget.info!; nameFormState.formController.text = info.name ?? ''; descriptionFormState.formController.text = info.description ?? ''; promptFormState.formController.text = info.prompt ?? ''; if (info.links != null && info.links!.isNotEmpty) { links.value.addAll(info.links!); } if (info.docs != null && info.docs!.isNotEmpty) { for (var doc in info.docs!) { DioService.downloadFile(doc).then((file) { if (file != null) { selectedFiles.value = [...selectedFiles.value, file]; } }); } } isPublic.value = info.public ?? false; try { initialItem = BotsBloc.allBots.firstWhere( (element) => element.name == widget.info!.model, ); } catch (e) { if (kDebugMode) { print('Error is: $e'); } } // botId = initialItem?.id; // if (info.image != null) { // selectedImageFileLoading.value = true; // dio.downloadFile(info.image!).then((file) { // if (file != null) { // // selectedImageFile.value = file; // selectedImageFileLoading.value = false; // } // }); // } } } @override Widget build(BuildContext context) { return Scaffold( appBar: ReversibleAppbar( context, titleText: 'ساخت دستیار', ), body: Responsive(context).maxWidthInDesktop( child: (contxet, maxWidth) => BlocConsumer( listener: (mContext, state) async { if (state is CreateAssistantFail) { SnackBarManager(context, id: 'createAssistantError').show( status: SnackBarStatus.error, message: state.message ?? 'خطا از طرف سرور'); } if (state is CreateAssistantSuccess) { WidgetsBinding.instance.addPostFrameCallback((_) { context.read().add(GetAll()); context.pop(); DialogHandler(context: context).showCreateSuccess(); }); } }, builder: (context, state) => SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Theme.of(context).colorScheme.error, border: Border.all( color: Theme.of(context) .colorScheme .onError .withOpacity(0.3), ), ), child: Row( children: [ Icon( Icons.info_outline, color: Theme.of(context).colorScheme.onError, size: 20, ), const SizedBox(width: 8), Expanded( child: Text( 'دستیارهای ایجادی شما فقط قابلیت چت دارند', style: AppTextStyles.body4.copyWith( color: Theme.of(context) .colorScheme .onPrimaryContainer, ), textDirection: TextDirection.rtl, ), ), ], ), ), const SizedBox(height: 16), // if (UserInfoCubit.userInfoModel.username == null) // Padding( // padding: const EdgeInsets.symmetric(vertical: 16.0), // child: // BlocBuilder( // builder: (context, state) { // return Column( // children: [ // Stack( // children: [ // LabeledTextField( // maxLines: 1, // showLabel: false, // justEnglish: true, // stateController: userFormState, // hintStyle: AppTextStyles.body4 // .copyWith(color: AppColors.gray[700]), // success: state is CheckUsernameSuccess // ? Row( // crossAxisAlignment: // CrossAxisAlignment.start, // children: [ // Icon( // Icons.check_circle, // color: AppColors // .green.defaultShade, // size: 16, // ), // const SizedBox( // width: 8, // ), // Expanded( // child: Text( // 'نام کاربری در دسترس است', // style: AppTextStyles.body5 // .copyWith( // color: AppColors.green // .defaultShade), // ), // ), // ], // ) // : null, // error: state is CheckUsernameFail // ? Row( // crossAxisAlignment: // CrossAxisAlignment.start, // children: [ // Icon( // Icons.warning_amber_rounded, // color: // AppColors.red.defaultShade, // size: 16, // ), // const SizedBox( // width: 8, // ), // Expanded( // child: Text( // 'نام کاربری قبلا انتخاب شده است', // style: AppTextStyles.body5 // .copyWith( // color: AppColors.red // .defaultShade), // ), // ), // ], // ) // : null, // suffix: Padding( // padding: const EdgeInsets.all(8.0), // child: Assets.icon.outline.profileTick // .svg( // color: state is CheckUsernameFail // ? AppColors.red.defaultShade // : state // is CheckUsernameSuccess // ? AppColors // .green.defaultShade // : AppColors.primaryColor // .defaultShade), // ), // onChange: (usernameText) { // if (usernameText.isEmpty) { // return; // } // context // .read() // .loading(); // EasyDebounce.debounce( // 'my-username', // <-- An ID for this particular debouncer // const Duration( // seconds: // 1), // <-- The debounce duration // () { // context // .read() // .check(usernameText); // } // <-- The target method // ); // }, // ), // Positioned( // top: 0, // right: 16, // child: titleWithHint( // text: userFormState.label ?? '', // hint: userFormState.tooltipHint, // )) // ], // ), // if (state is CheckUsernameLoading) // Padding( // padding: const EdgeInsets.symmetric( // horizontal: 18, vertical: 4), // child: Row( // crossAxisAlignment: // CrossAxisAlignment.start, // children: [ // SizedBox( // width: 16, // height: 16, // child: CircularProgressIndicator( // color: AppColors // .primaryColor.defaultShade, // ), // ), // const SizedBox( // width: 8, // ), // Expanded( // child: Text( // 'درحال بررسی', // style: AppTextStyles.body5.copyWith( // color: AppColors // .primaryColor.defaultShade), // ), // ), // ], // ), // ) // ], // ); // }, // ), // ), // Padding( // padding: const EdgeInsets.symmetric(vertical: 16.0), // child: Column( // children: [ // GestureDetector( // onTap: () async => // await BottomSheetHandler(context).showPickImage( // onSelect: (file) { // selectedImageFile.value = file; // }, // ), // child: ValueListenableBuilder( // valueListenable: selectedImageFile, // builder: (context, img, _) { // return ValueListenableBuilder( // valueListenable: selectedImageFileLoading, // builder: (context, loading, _) { // return Container( // width: 120, // height: 120, // decoration: BoxDecoration( // color: Colors.white, // borderRadius: // BorderRadius.circular(16)), // child: ClipRRect( // borderRadius: // BorderRadius.circular(16), // child: loading // ? SpinKitThreeBounce( // color: AppColors.primaryColor // .defaultShade, // size: 18, // ) // : img != null // ? CustomeImage( // src: img.path, // fit: BoxFit.cover, // ) // : Padding( // padding: // const EdgeInsets.all( // 24), // child: Assets.icon.outline // .galleryAdd // .svg(), // ), // ), // ); // }); // }), // ), // Text( // 'انتخاب تصویر برای دستیار', // style: AppTextStyles.body4.copyWith( // color: AppColors.primaryColor.defaultShade), // ), // const SizedBox( // height: 16, // ), // if (selectedImageFileError) // Text( // 'باید برای دستیار خود یک عکس انتخاب کنید !', // style: AppTextStyles.body4.copyWith( // color: AppColors.red.defaultShade, // fontWeight: FontWeight.bold), // ), // ], // ), // ), Stack( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: LabeledTextField( showLabel: false, stateController: nameFormState, hintStyle: AppTextStyles.body4 .copyWith(color: AppColors.gray[700]), onChange: (value) {}, onValid: (value) { if (value != null && value.isEmpty) { return '! نام دستیار نباید خالی باشد'; } return null; }, maxLines: 1, ), ), Positioned( top: 0, right: 16, child: titleWithHint( text: nameFormState.label ?? '', hint: nameFormState.tooltipHint, )) ], ), BlocBuilder( builder: (context, state) { Categories? initialItem; if (widget.info != null && categoryId == null) { initialItem = state.categories.firstWhere( (element) => element.id == widget.info!.category?.id, ); categoryId = widget.info!.category?.id; } return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Column( children: [ ValueListenableBuilder( valueListenable: categoryIdError, builder: (context, err, _) { return labeledCard( error: err ? 'باید برای دستیار خود یک دسته بندی انتخاب کنید !' : null, label: 'دسته‌بندی', // hint: 'sssssssss', child: Directionality( textDirection: TextDirection.rtl, child: CustomDropdown( items: state.categories, initialItem: initialItem, // initialItems: ['item1'], hintText: 'نوع دستیار خود را بر اساس زمینه کاری تعیین کنید.', listItemBuilder: (context, item, isSelected, onItemSelect) { return Text(item.name ?? '', style: AppTextStyles.body4 .copyWith( color: Theme.of(context) .colorScheme .onSurface)); }, headerBuilder: (context, selectedItem, enabled) => Text( selectedItem.name ?? '', style: AppTextStyles.body4.copyWith( color: Theme.of(context) .colorScheme .onSurface), ), decoration: CustomDropdownDecoration( expandedSuffixIcon: Transform.rotate( angle: pi / -2, child: Assets .icon.outline.arrowRight .svg( color: Theme.of(context) .colorScheme .onSurface), ), closedSuffixIcon: Transform.rotate( angle: pi / 2, child: Assets .icon.outline.arrowRight .svg( color: Theme.of(context) .colorScheme .onSurface), ), hintStyle: AppTextStyles.body4 .copyWith( color: AppColors.gray[700]), expandedFillColor: Theme.of(context) .colorScheme .surface, closedFillColor: Theme.of(context) .scaffoldBackgroundColor), onChanged: (category) { categoryId = category?.id; }, ), ), ); }), ], ), ); }, ), // Padding( // padding: const EdgeInsets.symmetric(vertical: 16.0), // child: ValueListenableBuilder( // valueListenable: botIdError, // builder: (context, err, _) { // return labeledCard( // error: err // ? 'باید برای دستیار خود یک مدل انتخاب کنید !' // : null, // label: 'مدل زبانی LLM', // hint: // 'مدل‌های زبانی (LLM)، قلب دستیار هوش مصنوعی شما هستند. در این بخش از میان مدل‌های موجود در هوشان، مدلی را که می‌خواهید دستیار شما از آن برای پردازش زبان و تولید پاسخ‌ها استفاده کند، انتخاب کنید. برای مثال، اگر نیاز به دقت بالا در تولید محتوای علمی دارید، مدلی را انتخاب کنید که برای این نوع از سوالات بهینه شده باشد. دقت کنید که هر مدل ویژگی‌ها، توانایی‌ها و قیمت‌ها متفاوتی دارد. بنابراین انتخاب مدل بسیار مهم است.', // child: Directionality( // textDirection: TextDirection.rtl, // child: CustomDropdown( // initialItem: initialItem, // decoration: CustomDropdownDecoration( // expandedSuffixIcon: Transform.rotate( // angle: pi / -2, // child: Assets.icon.outline.arrowRight.svg( // color: Theme.of(context) // .colorScheme // .onSurface), // ), // closedSuffixIcon: Transform.rotate( // angle: pi / 2, // child: Assets.icon.outline.arrowRight.svg( // color: Theme.of(context) // .colorScheme // .onSurface), // ), // hintStyle: AppTextStyles.body4 // .copyWith(color: AppColors.gray[700]), // expandedFillColor: // Theme.of(context).colorScheme.surface, // closedFillColor: Theme.of(context) // .scaffoldBackgroundColor), // items: BotsBloc.createBots, // headerBuilder: // (context, selectedItem, enabled) => Row( // children: [ // ImageNetwork( // url: selectedItem.image, // radius: 360, // width: 32, // height: 32, // color: Theme.of(context) // .colorScheme // .onSurface, // ), // const SizedBox( // width: 8, // ), // Text( // selectedItem.name ?? '', // style: TextStyle( // color: Theme.of(context) // .colorScheme // .onSurface), // ) // ], // ), // listItemBuilder: // (context, item, isSelected, onItemSelect) { // return Row( // children: [ // ImageNetwork( // url: item.image, // radius: 360, // width: 32, // height: 32, // color: Theme.of(context) // .colorScheme // .onSurface, // ), // const SizedBox( // width: 8, // ), // Text(item.name ?? '', // style: TextStyle( // color: Theme.of(context) // .colorScheme // .onSurface)) // ], // ); // }, // hintText: // 'یک مدل زبانی بزرگ (LLM) انتخاب کنید.', // onChanged: (item) { // botId = item?.id; // }, // ), // ), // ); // }), // ), Stack( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: LabeledTextField( showLabel: false, stateController: descriptionFormState, onChange: (value) {}, onValid: (value) { if (value != null && value.isEmpty) { return '! توضیحات دستیار نباید خالی باشد'; } return null; }, maxLines: 6, minLines: 6, hintStyle: AppTextStyles.body4 .copyWith(color: AppColors.gray[700]), ), ), Positioned( top: 0, right: 16, child: titleWithHint( text: 'توضیحات', hint: 'در این قسمت میتوانید به کاربرانی که قرار است از این دستیار استفاده کنند توضیحاتی راجع به دستیار خود بدهید.', )) ], ), Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Row( children: [ const Expanded(child: Divider()), const SizedBox( width: 4, ), Text( 'پایگاه دانش دستیار', style: AppTextStyles.body4.copyWith( color: Theme.of(context).colorScheme.onSurface), ), ], ), ), Stack( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: LabeledTextField( showLabel: false, stateController: promptFormState, onChange: (value) {}, onValid: (value) { if (value != null && value.isEmpty) { return '! پرامپت و دستورالعمل دستیار نباید خالی باشد'; } return null; }, maxLines: 6, minLines: 6, hintStyle: AppTextStyles.body4 .copyWith(color: AppColors.gray[700]), ), ), Positioned( top: 0, right: 16, child: titleWithHint( text: promptFormState.label ?? '', hint: promptFormState.tooltipHint, )) ], ), labeledCard( label: 'فایل‌های مرتبط ', hint: 'اگر فایل هایی دارید که شامل اطلاعات مرتبط با سوالات کاربران است می‌توانید آن‌ها را در این بخش آپلود کنید. سیستم به جای تحلیل کل فایل، فقط بخش‌های مرتبط با سوال کاربر را استخراج و استفاده می‌کند. به این ترتیب اگر کاربران سوالی مطرح کنند که اطلاعات آن در فایل‌های بارگذاری شده موجود باشد. دستیار می‌تواند به صورت هوشمند تکه‌های مرتبط را پیدا کرده و پاسخ دهد. لطفا توجه داشته باشید که حجم فایل نباید از 5 مگابایت بیشتر باشد. docx. xlsx. .txt xls.pdf فرمت‌های مجاز شامل هستند. ', child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Icon(Icons.file_upload_outlined, size: 32, color: AppColors.gray[700]), ), Text( 'پسوندهای مجاز: pdf، xls، xlsx، docx، txt\nحداکثر حجم فایل‌: 5 مگابایت\nحداکثر تعداد فایل‌: 3 عدد', style: AppTextStyles.body4 .copyWith(color: AppColors.gray[700]), textDirection: TextDirection.rtl, textAlign: TextAlign.justify, ), const SizedBox( height: 8, ), LoadingButton( onPressed: () async { if (selectedFiles.value.length >= 5) return; List? files = [...selectedFiles.value]; final fs = await PickFileService(context) .getFile( fileType: FileType.custom, allowMultiple: true, allowedExtensions: [ 'pdf', 'docx', 'xls', 'xlsx', 'txt' ], maxSize: 5); if (fs != null) { files.addAll(fs); if (files.length > 5) { files = files.sublist(0, 5); } selectedFiles.value = [...files]; } }, color: AppColors.primaryColor.defaultShade, radius: 360, child: Text('افزودن فایل', style: AppTextStyles.body3 .copyWith(color: Colors.white))), ValueListenableBuilder( valueListenable: selectedFiles, builder: (context, files, _) { return ListView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: files.length, itemBuilder: (context, index) { final file = files[index]; return Container( margin: const EdgeInsets.symmetric( horizontal: 16, vertical: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: context .read() .isDark() ? AppColors.black[900] : AppColors.primaryColor[50]), child: Directionality( textDirection: file.name.startsWithPersian() ? TextDirection.rtl : TextDirection.ltr, child: ListTile( title: Text(file.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 16, color: AppColors .green.defaultShade)), subtitle: FutureBuilder( future: file.length(), builder: (context, size) { return Text( '${file.name.split('.').last}: ${size.hasData && size.data != null ? '${(size.data! / 1048576).toStringAsFixed(2)} MB' : ''}'); }), leading: GestureDetector( onTap: () { selectedFiles.value = List.from( selectedFiles.value) ..removeAt(index); }, child: Assets.icon.outline.trash .svg(width: 24, height: 24), ), ), ), ); }); }) ], )), labeledCard( label: 'لینک‌های مرتبط ', hint: 'در صورتی که دستیار شما باید از منابع آنلاین برای ارائه پاسخ‌ها استفاده کند، می‌توانید. لینک‌های مربوطه را در این بخش وارد کنید. به عنوان مثال اگر دستیار شما باید از یک وب سایت خاص برای جمع آوری داده‌ها استفاده کند، لینک آن سایت را اینجا قرار دهید. این لینک‌ها به عنوان منابع مرجع برای پاسخگویی به سوالات استفاده خواهند شد. برای اضافه کردن لینک از دکمه + استفاده کنید.', child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Icon(Icons.link, size: 32, color: AppColors.gray[700]), ), Padding( padding: const EdgeInsets.all(16), child: FilledTextField( stateController: linkFormState, hintText: linkFormState.hintText, onValid: (value) { if (value != null) { if (!value.isURL()) { return 'لینک نا معتبر است'; } } return null; }, ), ), LoadingButton( onPressed: () { if (!linkFormState.formState.currentState! .validate()) { return; } if (!linkFormState.formController.text .isURL()) { return; } links.value = [ ...links.value, linkFormState.formController.text ]; linkFormState.formController.clear(); }, color: AppColors.primaryColor.defaultShade, radius: 360, child: Text('تأیید', style: AppTextStyles.body3 .copyWith(color: Colors.white))), ValueListenableBuilder( valueListenable: links, builder: (context, ls, _) { return ListView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: ls.length, itemBuilder: (context, index) { final link = ls[index]; return Container( margin: const EdgeInsets.symmetric( horizontal: 16, vertical: 8), padding: const EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: context .read() .isDark() ? AppColors.black[900] : AppColors.primaryColor[50]), child: ListTile( title: Text(link, maxLines: 1, overflow: TextOverflow.ellipsis, textDirection: TextDirection.ltr, style: AppTextStyles.body4 .copyWith( color: AppColors .green.defaultShade)), leading: GestureDetector( onTap: () { links.value = List.from(links.value) ..removeAt(index); }, child: Assets.icon.outline.trash .svg(width: 24, height: 24), ), ), ); }); }) ], )), Container( margin: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: context.read().isDark() ? Theme.of(context).colorScheme.surface : AppColors.green[50].withValues(alpha: 0.2)), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ValueListenableBuilder( valueListenable: isPublic, builder: (context, enable, _) { return Transform.scale( scale: 0.8, child: Switch.adaptive( value: enable, onChanged: (value) { isPublic.value = value; }, ), ); }), Text( 'نمایش عمومی دستیار', style: AppTextStyles.body3.copyWith( fontWeight: FontWeight.bold, color: context.read().isDark() ? Theme.of(context).colorScheme.onSurface : AppColors.primaryColor.defaultShade), ) ], ), const SizedBox( height: 12, ), Directionality( textDirection: TextDirection.rtl, child: Text( 'اگر این گزینه رو فعال کنی، دستیار هوش مصنوعی که ساختی برای سایر کاربران هم قابل مشاهده و استفاده می‌شه! ', style: AppTextStyles.body4.copyWith( color: Theme.of(context).colorScheme.onSurface), ), ) ], ), ), LoadingButton( loading: state is CreateAssistantLoading, onPressed: () { if (context.read().state is DeleteAssistantLoading) { return; } // botIdError.value = false; categoryIdError.value = false; final List valids = []; valids.add( nameFormState.formState.currentState!.validate()); valids.add(descriptionFormState.formState.currentState! .validate()); valids.add( promptFormState.formState.currentState!.validate()); if (!checkReqs() && valids.contains(false)) { SnackBarManager(context, id: 'fill-form').show( message: 'مقادیر خواسته شده را به درستی وارد کنید!', status: SnackBarStatus.error); return; } context.read().add( CreateAnAssistant( id: widget.info?.id, model: CreateAssistantRequestModel( // botId: botId!, categoryId: categoryId!, name: nameFormState.formController.text, description: descriptionFormState .formController.text, prompt: promptFormState.formController.text, public: isPublic.value, // image: selectedImageFile.value!, files: selectedFiles.value, links: links.value.isEmpty ? null : links.value))); }, color: AppColors.primaryColor.defaultShade, radius: 360, height: 48, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( widget.info != null ? 'تایید و ویرایش' : 'تایید و ساخت', style: AppTextStyles.body3 .copyWith(color: Colors.white), ), ), const SizedBox( width: 4, ), const Icon( CupertinoIcons.check_mark, color: Colors.white, size: 18, ) ], )), if (widget.info != null) Column( children: [ const SizedBox( height: 8, ), BlocConsumer( listener: (context, state) { if (state is DeleteAssistantSuccess) { context .read() .add(GetAll()); context.pop(); } if (state is DeleteAssistantFail) { SnackBarManager(context, id: 'createAssistantError') .show( status: SnackBarStatus.error, message: state.message ?? 'خطا از طرف سرور'); } }, builder: (context, state) { return LoadingButton( loading: state is DeleteAssistantLoading, onPressed: () async { DialogHandler(context: context) .showDeleteItem( title: 'دستیار حذف شود؟', description: 'با این کار اطلاعات شما ازبین خواهد رفت.', onConfirm: () async { context .read() .delete(widget.info!.id!); }, ); }, color: Theme.of(context).colorScheme.secondary, radius: 360, isOutlined: true, height: 48, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( 'حذف دستیار', style: AppTextStyles.body3.copyWith( color: Theme.of(context) .colorScheme .secondary), ), ), const SizedBox( width: 4, ), Icon( CupertinoIcons.delete, color: Theme.of(context) .colorScheme .secondary, size: 18, ) ], )); }, ), ], ), const SizedBox( height: 16, ), ], ), ), ), ), ), ); } Stack labeledCard( {required final String label, required final Widget child, final String? error, final String? hint}) { return Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( width: MediaQuery.sizeOf(context).width, margin: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all( color: error != null ? AppColors.red.defaultShade : AppColors.gray[700])), child: child), if (error != null) Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( error, style: AppTextStyles.body4.copyWith( color: AppColors.red.defaultShade, fontWeight: FontWeight.bold), ), ), ], ), Positioned( top: 0, right: 24, child: Container( color: Theme.of(context).scaffoldBackgroundColor, padding: const EdgeInsets.symmetric(horizontal: 8), child: hint == null ? Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( label, style: AppTextStyles.body4.copyWith( color: Theme.of(context).colorScheme.onSurface), ), ) : titleWithHint( text: label, hint: hint, textStyle: AppTextStyles.body4.copyWith( color: Theme.of(context).colorScheme.onSurface)))) ], ); } Widget titleWithHint( {required final String text, final String? hint, final TextStyle? textStyle}) { return Tooltip( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: context.read().isDark() ? AppColors.gray[900] : AppColors.primaryColor[50], borderRadius: BorderRadius.circular(8)), triggerMode: TooltipTriggerMode.tap, enableTapToDismiss: true, enableFeedback: true, preferBelow: true, showDuration: const Duration(minutes: 2), richMessage: WidgetSpan( alignment: PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic, child: Container( padding: const EdgeInsets.all(10), constraints: const BoxConstraints(maxWidth: 300), child: Text( hint ?? '', style: AppTextStyles.body4 .copyWith(color: Theme.of(context).colorScheme.onSurface), textDirection: TextDirection.rtl, textAlign: TextAlign.justify, ), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( text, style: textStyle ?? AppTextStyles.body4 .copyWith(color: Theme.of(context).colorScheme.onSurface), ), ), if (hint != null) Row( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( width: 4, ), Assets.icon.outline.warning2.svg( color: context.read().isDark() ? Colors.white : AppColors.primaryColor.defaultShade) ], ) ], ), ); } }