25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

519 lines
21 KiB

  1. // ignore_for_file: public_member_api_docs, sort_constructors_first
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_gen/gen_l10n/app_localizations.dart';
  4. import 'package:go_router/go_router.dart';
  5. import 'package:provider/provider.dart';
  6. import 'package:qadirneyriz/global_state/global_state.dart';
  7. import 'package:qadirneyriz/screens/meeting_edit/diolog_add_location.dart';
  8. import 'package:qadirneyriz/screens/meeting_edit/diolog_add_subject.dart';
  9. import 'package:qadirneyriz/screens/meeting_edit/diolog_add_user.dart';
  10. import 'package:qadirneyriz/screens/meeting_edit/state.dart';
  11. import 'package:qadirneyriz/utils/enums/status.dart';
  12. import 'package:qadirneyriz/utils/tools/tools.dart';
  13. import 'package:qadirneyriz/widgets/ExpansionTileCustom.dart';
  14. import 'package:qadirneyriz/widgets/custom_appbar.dart';
  15. import 'package:qadirneyriz/widgets/custom_button.dart';
  16. import 'package:qadirneyriz/widgets/ink_warpper.dart';
  17. import 'package:qadirneyriz/widgets/loading_widget.dart';
  18. import 'package:qadirneyriz/widgets/picker.dart';
  19. class MeetingEditScreen extends StatefulWidget {
  20. final int id;
  21. const MeetingEditScreen({
  22. Key? key,
  23. required this.id,
  24. }) : super(key: key);
  25. @override
  26. State<MeetingEditScreen> createState() => _MeetingEditScreenState();
  27. }
  28. class _MeetingEditScreenState extends State<MeetingEditScreen> {
  29. final _formKey = GlobalKey<FormState>(); // Key for form validation
  30. // all states we have
  31. late MeetingEditState meetingEditState;
  32. late GlobalState globalState;
  33. @override
  34. void initState() {
  35. super.initState();
  36. //set states
  37. meetingEditState = Provider.of<MeetingEditState>(context, listen: false);
  38. globalState = Provider.of<GlobalState>(context, listen: false);
  39. Future.delayed(Duration.zero, () async {
  40. // get items
  41. await meetingEditState.getOneMeeting(id: widget.id);
  42. await globalState.getAllFiltersItems();
  43. // set variables
  44. meetingEditState.setAllVariablesAtStart(id: widget.id);
  45. });
  46. }
  47. @override
  48. Widget build(BuildContext context) {
  49. return Scaffold(
  50. body: Consumer2<MeetingEditState, GlobalState>(
  51. builder: (context, meetingEditState, globalState, child) {
  52. return CustomScrollView(
  53. slivers: <Widget>[
  54. CustomAppbar(title: AppLocalizations.of(context)!.editmeeting),
  55. SliverFillRemaining(
  56. child: content(context, meetingEditState, globalState)),
  57. ],
  58. );
  59. },
  60. ),
  61. );
  62. }
  63. Widget content(BuildContext context, MeetingEditState meetingEditState,
  64. GlobalState globalState) {
  65. if (meetingEditState.oneMeetingStatus[widget.id] == Status.ready &&
  66. globalState.allFiltersStatus == Status.ready) {
  67. final itemInOneMeeting = meetingEditState.oneMeetingModel![widget.id]!;
  68. return Padding(
  69. // This is now wrapped inside SliverToBoxAdapter
  70. padding: const EdgeInsets.all(16.0),
  71. child: Form(
  72. key: _formKey,
  73. child: SingleChildScrollView(
  74. child: Column(
  75. crossAxisAlignment: CrossAxisAlignment.start,
  76. children: [
  77. // subject ExpansionTile
  78. Padding(
  79. padding: const EdgeInsets.symmetric(vertical: 8.0),
  80. child: ExpansionTileCustom(
  81. isForm: true,
  82. subTitile: AppLocalizations.of(context)!.meetingsubject,
  83. title: meetingEditState.selectedSubject.id != null
  84. ? meetingEditState.selectedSubject.text ?? ''
  85. : meetingEditState.oneMeetingModel![widget.id]!.subject!
  86. .subject ??
  87. '',
  88. widgets: <Widget>[
  89. ItemInTile(
  90. text: 'عضو جدید',
  91. onTap: () async {
  92. await showDialog(
  93. context: context, // این باید کانتکست فعلی باشد
  94. builder: (BuildContext context) {
  95. return AddSubjectDiolog();
  96. },
  97. );
  98. },
  99. hasIcon: true,
  100. backColor: Colors.white,
  101. textColor: Colors.black.withOpacity(.5),
  102. ),
  103. Column(
  104. children: globalState.subjectsModel!.map((subject) {
  105. bool isSelected =
  106. meetingEditState.selectedSubject.id == subject.id;
  107. return ItemInTile(
  108. backColor:
  109. isSelected ? Color(0xff06CF64) : Colors.white,
  110. textColor: isSelected ? Colors.white : Colors.black,
  111. text: subject.subject ?? '',
  112. hasIcon: false,
  113. onTap: () {
  114. setState(() {
  115. meetingEditState.selectedSubject = ItemSelected(
  116. text: subject.subject ?? '',
  117. id: subject.id ??
  118. 0); // Update selected location
  119. });
  120. },
  121. );
  122. }).toList(),
  123. ),
  124. ],
  125. ),
  126. ),
  127. // Date Picker
  128. Padding(
  129. padding: const EdgeInsets.symmetric(vertical: 8.0),
  130. child: PickerCustom(
  131. showDate: meetingEditState.fromDate != null
  132. ? meetingEditState.fromDate ?? ''
  133. : itemInOneMeeting.dateJalali ?? '',
  134. onTap: () {
  135. showDialog(
  136. context: context,
  137. builder: (context) {
  138. return Dialog(
  139. child: Tools.shamsiDateCalendarWidget(
  140. context,
  141. (newDate) {
  142. String fromDateString =
  143. '${newDate.year}/${newDate.month}/${newDate.day}';
  144. meetingEditState.setFromDate(
  145. fromDateString); // Update the selected date
  146. },
  147. ),
  148. );
  149. },
  150. );
  151. },
  152. isForm: true,
  153. title: AppLocalizations.of(context)!.date,
  154. ),
  155. ),
  156. // From and To time Range Pickers
  157. Padding(
  158. padding: const EdgeInsets.symmetric(vertical: 15.0),
  159. child: Row(
  160. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  161. crossAxisAlignment: CrossAxisAlignment.end,
  162. children: [
  163. PickerCustom(
  164. showDate: meetingEditState.selectedStartTime != null
  165. ? Tools.formatTime(
  166. meetingEditState.selectedStartTime!.hour,
  167. meetingEditState.selectedStartTime!.minute)
  168. : itemInOneMeeting.azHour ?? '',
  169. onTap: () async {
  170. TimeOfDay? picked = await showTimePicker(
  171. context: context,
  172. initialTime: meetingEditState.selectedStartTime!,
  173. );
  174. if (picked != null &&
  175. picked != meetingEditState.selectedStartTime)
  176. setState(() {
  177. meetingEditState.selectedStartTime = picked;
  178. });
  179. },
  180. isForm: true,
  181. icon: Icons.access_time_outlined,
  182. title: AppLocalizations.of(context)!.clock,
  183. ),
  184. Text(AppLocalizations.of(context)!.to),
  185. PickerCustom(
  186. showDate: meetingEditState.selectedEndTime != null
  187. ? Tools.formatTime(
  188. meetingEditState.selectedEndTime!.hour,
  189. meetingEditState.selectedEndTime!.minute)
  190. : itemInOneMeeting.taHour ?? '',
  191. isForm: true,
  192. icon: Icons.access_time_outlined,
  193. onTap: () async {
  194. TimeOfDay? picked = await showTimePicker(
  195. context: context,
  196. initialTime: meetingEditState.selectedEndTime!,
  197. );
  198. if (picked != null &&
  199. picked != meetingEditState.selectedEndTime)
  200. setState(() {
  201. meetingEditState.selectedEndTime = picked;
  202. });
  203. },
  204. ),
  205. ],
  206. ),
  207. ),
  208. // Location ExpansionTile
  209. Padding(
  210. padding: const EdgeInsets.symmetric(vertical: 8.0),
  211. child: ExpansionTileCustom(
  212. isForm: true,
  213. subTitile: AppLocalizations.of(context)!.location,
  214. title: meetingEditState.selectedLocation.id != null
  215. ? meetingEditState.selectedLocation.text ?? ''
  216. : itemInOneMeeting.location!.address ?? '',
  217. widgets: <Widget>[
  218. ItemInTile(
  219. text: 'مکان جدید',
  220. onTap: () async {
  221. await showDialog(
  222. context: context, // این باید کانتکست فعلی باشد
  223. builder: (BuildContext context) {
  224. return AddLocationDiolog();
  225. },
  226. );
  227. },
  228. hasIcon: true,
  229. backColor: Colors.white,
  230. textColor: Colors.black.withOpacity(.5),
  231. ),
  232. Column(
  233. children: globalState.locationsModel!.map((location) {
  234. bool isSelected =
  235. meetingEditState.selectedLocation.id ==
  236. location.id;
  237. return ItemInTile(
  238. backColor:
  239. isSelected ? Color(0xff06CF64) : Colors.white,
  240. textColor: isSelected ? Colors.white : Colors.black,
  241. text: location.address ?? '',
  242. hasIcon: false,
  243. onTap: () {
  244. setState(() {
  245. meetingEditState.selectedLocation =
  246. ItemSelected(
  247. text: location.address,
  248. id: location
  249. .id); // Update selected location
  250. });
  251. },
  252. );
  253. }).toList(),
  254. ),
  255. ],
  256. ),
  257. ),
  258. // Another ExpansionTile for users
  259. Padding(
  260. padding: const EdgeInsets.symmetric(vertical: 8.0),
  261. child: ExpansionTileCustom(
  262. isForm: true,
  263. subTitile: AppLocalizations.of(context)!.users,
  264. title: AppLocalizations.of(context)!.selectusers,
  265. widgets: <Widget>[
  266. ItemInTile(
  267. text: 'کاربر جدید',
  268. onTap: () async {
  269. await showDialog(
  270. context: context, // این باید کانتکست فعلی باشد
  271. builder: (BuildContext context) {
  272. return AddUserDiolog();
  273. },
  274. );
  275. },
  276. hasIcon: true,
  277. backColor: Colors.white,
  278. textColor: Colors.black.withOpacity(.5),
  279. ),
  280. Column(
  281. children: globalState.usersModel != null
  282. ? globalState.usersModel!.map((user) {
  283. bool isSelected = meetingEditState
  284. .selectedUsersItems
  285. .contains(user.id);
  286. return Container(
  287. margin: EdgeInsets.symmetric(
  288. vertical: 5.0, horizontal: 10),
  289. decoration: BoxDecoration(
  290. color: isSelected
  291. ? Color(0xff06CF64)
  292. : Colors.white,
  293. borderRadius: BorderRadius.circular(10),
  294. boxShadow: [
  295. BoxShadow(
  296. color: Colors.black12,
  297. blurRadius: 8,
  298. offset: Offset(0, 4),
  299. ),
  300. ],
  301. ),
  302. child: InkWrapper(
  303. onTap: () {
  304. setState(() {
  305. if (isSelected) {
  306. meetingEditState.selectedUsersItems
  307. .remove(user.id);
  308. } else {
  309. meetingEditState.selectedUsersItems
  310. .add(user.id ?? 0);
  311. }
  312. });
  313. },
  314. child: Padding(
  315. padding: const EdgeInsets.all(10.0),
  316. child: Row(
  317. children: [
  318. Text(
  319. maxLines: 1,
  320. overflow: TextOverflow.ellipsis,
  321. user.name ?? '',
  322. style: TextStyle(
  323. fontSize: 12,
  324. color: isSelected
  325. ? Colors.white
  326. : Colors.black,
  327. ),
  328. ),
  329. ],
  330. ),
  331. ),
  332. ),
  333. );
  334. }).toList()
  335. : [],
  336. ),
  337. ],
  338. ),
  339. ),
  340. // Final ExpansionTile if required
  341. Padding(
  342. padding: const EdgeInsets.symmetric(vertical: 10.0),
  343. child: ExpansionTileCustom(
  344. isForm: true,
  345. subTitile: AppLocalizations.of(context)!.meetingmanager,
  346. title: meetingEditState.selectedManager.id != null
  347. ? meetingEditState.selectedManager.text ?? ''
  348. : itemInOneMeeting.manager!.name ?? '',
  349. widgets: <Widget>[
  350. Column(
  351. children:
  352. globalState.meetingsManagerModel!.map((manager) {
  353. bool isSelected =
  354. meetingEditState.selectedManager.id == manager.id;
  355. return ItemInTile(
  356. backColor:
  357. isSelected ? Color(0xff06CF64) : Colors.white,
  358. textColor: isSelected ? Colors.white : Colors.black,
  359. text: manager.name ?? '',
  360. hasIcon: false,
  361. onTap: () {
  362. setState(() {
  363. meetingEditState.selectedManager = ItemSelected(
  364. id: manager.id,
  365. text: manager
  366. .name); // Update selected manager
  367. });
  368. },
  369. );
  370. }).toList(),
  371. ),
  372. ],
  373. ),
  374. ),
  375. // Submit Button
  376. SizedBox(
  377. height: 60,
  378. ),
  379. submit(context)
  380. ],
  381. ),
  382. ),
  383. ),
  384. );
  385. } else if (meetingEditState.oneMeetingStatus[widget.id] == Status.loading ||
  386. globalState.allFiltersStatus == Status.loading) {
  387. return const LoadingWidget();
  388. } else {
  389. return Container();
  390. }
  391. }
  392. CustomButton submit(BuildContext context) {
  393. switch (meetingEditState.statusEitMeeting) {
  394. case Status.loading:
  395. return CustomButton(
  396. width: double.infinity,
  397. hieght: 50,
  398. fontSize: 16,
  399. onPressed: null,
  400. text: AppLocalizations.of(context)!.loading);
  401. default:
  402. return CustomButton(
  403. width: double.infinity,
  404. hieght: 50,
  405. fontSize: 16,
  406. onPressed: () async {
  407. final status = await meetingEditState.editMeeting(
  408. id: widget.id,
  409. locationId: meetingEditState.selectedLocation.id ?? -1,
  410. subjectId: meetingEditState.selectedSubject.id ?? -1,
  411. managerId: meetingEditState.selectedManager.id ?? -1,
  412. fromHour: Tools.formatTime(
  413. meetingEditState.selectedStartTime!.hour,
  414. meetingEditState.selectedStartTime!.minute),
  415. toHour: Tools.formatTime(
  416. meetingEditState.selectedEndTime!.hour,
  417. meetingEditState.selectedEndTime!.minute),
  418. dateMeeting: meetingEditState.fromDate ?? '',
  419. members: meetingEditState.selectedUsersItems);
  420. if (status == Status.ready) {
  421. context.pop();
  422. } else {
  423. Tools.showCustomSnackBar(
  424. text: meetingEditState.errorsEditMeeting == null
  425. ? meetingEditState.messageEditMeeting ??
  426. AppLocalizations.of(context)!.haserror
  427. : Tools.combineErrorMessages(
  428. meetingEditState.errorsEditMeeting ?? {}),
  429. isError: true,
  430. context,
  431. );
  432. }
  433. },
  434. text: AppLocalizations.of(context)!.submit);
  435. }
  436. }
  437. }
  438. class ItemInTile extends StatelessWidget {
  439. final void Function()? onTap;
  440. final String text;
  441. final bool hasIcon;
  442. final Color backColor;
  443. final Color textColor;
  444. const ItemInTile({
  445. Key? key,
  446. this.onTap,
  447. required this.text,
  448. required this.hasIcon,
  449. required this.backColor,
  450. required this.textColor,
  451. }) : super(key: key);
  452. @override
  453. Widget build(BuildContext context) {
  454. return Padding(
  455. padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
  456. child: InkWrapper(
  457. borderRadius: 10,
  458. onTap: onTap,
  459. child: Container(
  460. decoration: BoxDecoration(boxShadow: [
  461. BoxShadow(
  462. color: Colors.black12,
  463. blurRadius: 8,
  464. offset: Offset(0, 4),
  465. ),
  466. ], color: backColor, borderRadius: BorderRadius.circular(10)),
  467. child: Padding(
  468. padding: const EdgeInsets.all(10.0),
  469. child: Row(
  470. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  471. children: [
  472. Expanded(
  473. child: Text(
  474. maxLines: 1,
  475. overflow: TextOverflow.ellipsis,
  476. text,
  477. style: TextStyle(color: textColor, fontSize: 12),
  478. ),
  479. ),
  480. if (hasIcon)
  481. Icon(Icons.add_circle_outline,
  482. color: Colors.black.withOpacity(.3))
  483. ],
  484. ),
  485. ),
  486. ),
  487. ),
  488. );
  489. }
  490. }
  491. class ItemSelected {
  492. final String? text;
  493. final int? id;
  494. ItemSelected({
  495. this.text,
  496. this.id,
  497. });
  498. }