You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

551 rivejä
26 KiB

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