"Updated BotAssistantsReqModel to include description, modified file picker to allow any file type, added description field to CreateBotAssistantsPage, and made various other UI and logic changes."

This commit is contained in:
OkaykOrhmn 2024-11-17 10:48:55 +03:30
parent a2c0c97337
commit dfd57937fb
8 changed files with 277 additions and 76 deletions

View File

@ -4,6 +4,7 @@ class BotAssistantsReqModel {
final String type; final String type;
final XFile? image; final XFile? image;
final String name; final String name;
final String description;
final int botId; final int botId;
final String prompt; final String prompt;
final List<XFile>? files; final List<XFile>? files;
@ -16,6 +17,7 @@ class BotAssistantsReqModel {
required this.name, required this.name,
required this.botId, required this.botId,
required this.prompt, required this.prompt,
required this.description,
this.image, this.image,
this.files, this.files,
this.youtubeLink, this.youtubeLink,
@ -28,6 +30,7 @@ class BotAssistantsReqModel {
data['name'] = name; data['name'] = name;
data['botId'] = botId; data['botId'] = botId;
data['prompt'] = prompt; data['prompt'] = prompt;
data['description'] = description;
data['isPrivate'] = isPrivate; data['isPrivate'] = isPrivate;
if (youtubeLink != null) { if (youtubeLink != null) {
data['youtubeLink'] = youtubeLink; data['youtubeLink'] = youtubeLink;

View File

@ -0,0 +1,10 @@
import 'package:image_picker/image_picker.dart';
class FileCreateAssistantsModel {
final bool fromNetwork;
final XFile? file;
final String? url;
FileCreateAssistantsModel(
{required this.fromNetwork, required this.file, required this.url});
}

View File

@ -141,22 +141,22 @@ class MediaService {
try { try {
return await FilePicker.platform return await FilePicker.platform
.pickFiles( .pickFiles(
type: FileType.custom, type: FileType.any,
allowedExtensions: [ // allowedExtensions: [
'pdf', // 'pdf',
'doc', // 'doc',
'docx', // 'docx',
'xls', // 'xls',
'xlsx', // 'xlsx',
'ppt', // 'ppt',
'pptx', // 'pptx',
'txt', // 'txt',
'mp3', // 'mp3',
'wav', // 'wav',
'aac', // 'aac',
'ogg', // 'ogg',
'flac' // 'flac'
], // You can specify allowed extensions if needed // ], // You can specify allowed extensions if needed
allowMultiple: true, allowMultiple: true,
// Note: The maxFiles parameter is not directly supported by FilePicker. // Note: The maxFiles parameter is not directly supported by FilePicker.
// You will need to handle the limit after selection if necessary. // You will need to handle the limit after selection if necessary.

View File

@ -238,6 +238,7 @@ class RequestHelper {
static String updateAssistants(int id) => '$baseUrl/ai/bot/$id'; static String updateAssistants(int id) => '$baseUrl/ai/bot/$id';
static String getAssistant(int id) => '$baseUrl/ai/bot/user/$id'; static String getAssistant(int id) => '$baseUrl/ai/bot/user/$id';
static String deleteAssistant(int id) => '$baseUrl/ai/bot/user/$id'; static String deleteAssistant(int id) => '$baseUrl/ai/bot/user/$id';
static String nameOfAssistant() => '$baseUrl/ai/bot/name';
static String _urlConcatGenerator(List<MapEntry<String, dynamic>> additions) { static String _urlConcatGenerator(List<MapEntry<String, dynamic>> additions) {
String result = ''; String result = '';

View File

@ -227,9 +227,9 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
if (assistants.description != null)
DidvanText( DidvanText(
assistants.description ?? assistants.description!,
'dsadsadsadadsadaddadadadsdadsad dsadsadsadadsadaddadadadsdadsad dsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsad vdsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsaddsadsadsadadsadaddadadadsdadsad',
fontSize: 12, fontSize: 12,
color: Theme.of(context).colorScheme.disabledText, color: Theme.of(context).colorScheme.disabledText,
maxLines: 2, maxLines: 2,

View File

@ -1,18 +1,22 @@
// ignore_for_file: deprecated_member_use // ignore_for_file: deprecated_member_use
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:animated_custom_dropdown/custom_dropdown.dart'; import 'package:animated_custom_dropdown/custom_dropdown.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/ai/bot_assistants_model.dart'; import 'package:didvan/models/ai/bot_assistants_model.dart';
import 'package:didvan/models/ai/bot_assistants_req_model.dart'; import 'package:didvan/models/ai/bot_assistants_req_model.dart';
import 'package:didvan/models/ai/bots_model.dart'; import 'package:didvan/models/ai/bots_model.dart';
import 'package:didvan/models/ai/file_create_assistants_model.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/media/media.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/extension.dart';
import 'package:didvan/views/ai/bot_assistants_state.dart'; import 'package:didvan/views/ai/bot_assistants_state.dart';
import 'package:didvan/views/ai/create_bot_assistants_state.dart'; import 'package:didvan/views/ai/create_bot_assistants_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart';
@ -22,6 +26,7 @@ import 'package:didvan/views/widgets/didvan/switch.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/didvan/text_field.dart'; import 'package:didvan/views/widgets/didvan/text_field.dart';
import 'package:didvan/views/widgets/hoshan_app_bar.dart'; import 'package:didvan/views/widgets/hoshan_app_bar.dart';
import 'package:didvan/views/widgets/marquee_text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -59,12 +64,13 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
// String? youtubeLink; // String? youtubeLink;
List<String> countOfLink = ['']; List<String> countOfLink = [''];
List<XFile> files = []; List<FileCreateAssistantsModel> files = [];
ValueNotifier<XFile?> image = ValueNotifier(null); ValueNotifier<XFile?> image = ValueNotifier(null);
String selectedBotType = 'text'; String selectedBotType = 'text';
bool isPrivate = true; bool isPrivate = true;
BotAssistants? assistant; BotAssistants? assistant;
BotsModel? initialBot; BotsModel? initialBot;
Timer? _timer;
@override @override
void initState() { void initState() {
@ -92,20 +98,28 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
if (!isValid) { if (!isValid) {
return; return;
} }
final List<XFile> resultFiles = [];
for (var file in files) {
if (!file.fromNetwork) {
resultFiles.add(file.file!);
}
}
await state.createAssistants( await state.createAssistants(
id: widget.id, id: widget.id,
data: BotAssistantsReqModel( data: BotAssistantsReqModel(
type: selectedBotType, type: selectedBotType,
name: name, name: name,
description: desc,
botId: selectedBotId, botId: selectedBotId,
prompt: prompt, prompt: prompt,
webLinks: countOfLink.first.isEmpty ? null : countOfLink, webLinks: countOfLink.first.isEmpty ? null : countOfLink,
// youtubeLink: youtubeLink, // youtubeLink: youtubeLink,
isPrivate: isPrivate, isPrivate: isPrivate,
files: files, files: resultFiles,
image: image.value)); image: image.value));
context.read<BotAssistantsState>().getMyAssissmant(); context.read<BotAssistantsState>().getMyAssissmant();
context.read<CreateBotAssistantsState>().assistant = null;
Navigator.pop(context); Navigator.pop(context);
} }
@ -123,22 +137,28 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
}, },
child: Scaffold( child: Scaffold(
appBar: HoshanAppBar( appBar: HoshanAppBar(
onBack: () => Navigator.pop(context), onBack: () {
context.read<CreateBotAssistantsState>().assistant = null;
Navigator.pop(context);
},
withActions: false, withActions: false,
), ),
body: Consumer<CreateBotAssistantsState>(builder: (BuildContext context, body: Consumer<CreateBotAssistantsState>(builder: (BuildContext context,
CreateBotAssistantsState state, Widget? child) { CreateBotAssistantsState state, Widget? child) {
if (assistant == null && state.assistant != null) {
assistant = state.assistant; assistant = state.assistant;
if (assistant != null) {
name = assistant!.name ?? ''; name = assistant!.name ?? '';
prompt = assistant!.prompt ?? ''; prompt = assistant!.prompt ?? '';
desc = assistant!.description ?? ''; desc = assistant!.description ?? '';
// youtubeLink = assistant!.; // youtubeLink = assistant!.;
isPrivate = assistant!.private ?? true; isPrivate = assistant!.private ?? true;
if (assistant!.files != null && assistant!.files!.isNotEmpty) { if (files.isEmpty &&
assistant!.files != null &&
assistant!.files!.isNotEmpty) {
for (var file in assistant!.files!) { for (var file in assistant!.files!) {
final data = File.fromUri(Uri.parse(file)).readAsBytesSync(); files.add(FileCreateAssistantsModel(
files.add(XFile.fromData(data)); fromNetwork: true, file: null, url: file));
} }
} }
countOfLink = assistant!.websites ?? ['']; countOfLink = assistant!.websites ?? [''];
@ -262,6 +282,16 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
initialValue: name, initialValue: name,
onChanged: (value) { onChanged: (value) {
name = value; name = value;
if (value.isEmpty) {
return;
}
_timer?.cancel();
_timer =
Timer(const Duration(seconds: 1), () async {
await state.getAssistantsName(name: value);
_formNameKey.currentState!.validate();
});
}, },
validator: (value) { validator: (value) {
String? result; String? result;
@ -269,6 +299,9 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
result = 'نام نباید خالی باشد'; result = 'نام نباید خالی باشد';
} else if (value.length < 4) { } else if (value.length < 4) {
result = 'نام نباید کمتر از 4 حرف باشد'; result = 'نام نباید کمتر از 4 حرف باشد';
} else if (!state.successName) {
result =
'اسم دیگری انتخاب کنید این اسم موجود است';
} }
return result; return result;
}, },
@ -279,17 +312,26 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Icon( state.loadingName
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator())
: Icon(
DidvanIcons.info_circle_light, DidvanIcons.info_circle_light,
color: Theme.of(context).colorScheme.caption, color:
Theme.of(context).colorScheme.caption,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Expanded( Expanded(
child: DidvanText( child: DidvanText(
'نام منحصر به فرد شامل 4 تا 20 کاراکتر (حروف، اعداد، خط تیره، نقطه و زیرخط) ', state.loadingName
? '...درحال بررسی اسم'
: 'نام منحصر به فرد شامل 4 تا 20 کاراکتر (حروف، اعداد، خط تیره، نقطه و زیرخط) ',
textAlign: TextAlign.right, textAlign: TextAlign.right,
fontSize: 12, fontSize: 12,
color: Theme.of(context).colorScheme.caption, color: Theme.of(context).colorScheme.caption,
@ -416,6 +458,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
Column( Column(
children: [ children: [
title(text: 'پایگاه دانش', isRequired: false), title(text: 'پایگاه دانش', isRequired: false),
if (files.length != 3)
SizedBox( SizedBox(
height: 48, height: 48,
child: ElevatedButton( child: ElevatedButton(
@ -424,14 +467,20 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
.colorScheme .colorScheme
.disabledBackground, .disabledBackground,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: borderRadius: DesignConfig
DesignConfig.lowBorderRadius)), .lowBorderRadius)),
onPressed: () async { onPressed: () async {
final picks = final picks =
await MediaService.pickMultiFile(); await MediaService.pickMultiFile();
if (picks != null) { if (picks != null) {
files.addAll(picks.xFiles); for (var file in picks.xFiles) {
files.add(FileCreateAssistantsModel(
fromNetwork: false,
file: file,
url: null));
} }
}
state.update();
}, },
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment:
@ -456,6 +505,106 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
], ],
)), )),
), ),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
...List.generate(
files.length,
(index) {
return Stack(
children: [
Container(
width: MediaQuery.sizeOf(context)
.width /
5,
height: MediaQuery.sizeOf(context)
.width /
5,
margin:
const EdgeInsets.symmetric(
horizontal: 8,
vertical: 12),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.disabledBackground,
borderRadius: DesignConfig
.lowBorderRadius),
child: Column(
children: [
Expanded(
child: files[index]
.fromNetwork
? files[index]
.url!
.isImage()
? CachedNetworkImage(
imageUrl:
files[index]
.url!)
: const Icon(
CupertinoIcons
.doc)
: files[index]
.file!
.path
.isImage()
? Image.file(File(
files[index]
.file!
.path))
: const Icon(
CupertinoIcons
.doc),
),
MarqueeText(
text: files[index]
.fromNetwork
? files[index]
.url!
.split('/')
.last
: files[index]
.file!
.name,
textDirection:
TextDirection.rtl,
style: Theme.of(context)
.textTheme
.labelSmall!)
],
)),
Positioned(
top: 8,
left: 4,
child: InkWell(
onTap: () {
files.removeAt(index);
state.update();
},
child: Container(
padding:
const EdgeInsets.all(6),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.colorScheme
.error),
child: const Icon(
DidvanIcons.trash_solid,
color: Colors.white,
size: 18,
),
),
))
],
);
},
)
],
),
const SizedBox( const SizedBox(
height: 24, height: 24,
), ),
@ -648,6 +797,10 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
onConfirmed: () async { onConfirmed: () async {
await state.deleteAssistants( await state.deleteAssistants(
id: widget.id!); id: widget.id!);
context
.read<
CreateBotAssistantsState>()
.assistant = null;
Navigator.pop(context); Navigator.pop(context);
}, },
)); ));

View File

@ -11,6 +11,8 @@ class CreateBotAssistantsState extends CoreProvier {
List<BotsModel> imageBots = []; List<BotsModel> imageBots = [];
bool loadingImageBots = false; bool loadingImageBots = false;
bool loadingCreate = false; bool loadingCreate = false;
bool loadingName = false;
bool successName = false;
bool loading = false; bool loading = false;
BotAssistants? assistant; BotAssistants? assistant;
@ -100,4 +102,30 @@ class CreateBotAssistantsState extends CoreProvier {
loadingCreate = false; loadingCreate = false;
update(); update();
} }
Future getAssistantsName({required final String name}) async {
loadingName = true;
update();
final service =
RequestService(RequestHelper.nameOfAssistant(), body: {'name': name});
await service.post();
if (service.isSuccess) {
appState = AppState.idle;
loadingName = false;
if (service.result['available'] as bool) {
successName = true;
} else {
successName = false;
}
update();
return;
}
appState = AppState.failed;
loadingName = false;
successName = false;
update();
}
} }

View File

@ -1,5 +1,6 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/hoshan_app_bar.dart'; import 'package:didvan/views/widgets/hoshan_app_bar.dart';
@ -205,7 +206,12 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText('هنوز سوالی دارید؟'), const DidvanText('هنوز سوالی دارید؟'),
TextButton( TextButton(
onPressed: () {}, onPressed: () {
Navigator.of(context).pushNamed(
Routes.direct,
arguments: {'type': 'پشتیبانی اپلیکیشن'},
);
},
child: const DidvanText( child: const DidvanText(
' پیام به پشتیبانی', ' پیام به پشتیبانی',
color: Color(0xff007EA7), color: Color(0xff007EA7),