From 17af2c8b86b2419c878b2d2639a490779facc8b9 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:07:20 +0530 Subject: [PATCH 01/41] Added auto sized text --- frontend/pubspec.lock | 8 ++++++++ frontend/pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index ae084b6..ee7bde3 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" awesome_notifications: dependency: "direct main" description: diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index ad1eaba..beb3561 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: image_cropper: ^5.0.1 url_launcher: ^6.2.4 flutter_dotenv: ^5.1.0 + auto_size_text: ^3.0.0 dev_dependencies: flutter_test: From b6f53faf622d7cbe399a58f7f6f2fdea62547b06 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:07:52 +0530 Subject: [PATCH 02/41] added message for lost and found --- backend/constants/messages.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/constants/messages.js b/backend/constants/messages.js index 81edfbc..ce504a3 100644 --- a/backend/constants/messages.js +++ b/backend/constants/messages.js @@ -30,4 +30,9 @@ export const invalidUserType = "Invalid user type"; export const tokenUpdateError = "Error updating token"; export const badRequest = "Bad request"; export const notFound = "Not found"; -export const deleted = "Deleted"; \ No newline at end of file +export const deleted = "Deleted"; + +// Lost and Found +export const itemNotFound = "Item not found"; +export const itemAdded = "Item added successfully"; +export const itemDeleted = "Item deleted successfully"; From 6e2f66a9ba90e2d931954a15bd261c3c847860d5 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:08:06 +0530 Subject: [PATCH 03/41] remove unused resource --- .../resources/rooms/occupantListResource.js | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 backend/resources/rooms/occupantListResource.js diff --git a/backend/resources/rooms/occupantListResource.js b/backend/resources/rooms/occupantListResource.js deleted file mode 100644 index 1fb4034..0000000 --- a/backend/resources/rooms/occupantListResource.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Router } from "express"; -import Room from "../../models/room.js"; -import Student from "../../models/student.js"; -const router = Router(); - -//GET method -router.get("/", async (req, res) => { - try { - const documentIds = req.body.documentIds; // Assuming the documentIds are sent in the request body - - const occupants = await Promise.all( - documentIds.map(async (documentId) => { - const room = await Room.findOne({ documentId }); - const occupant = await Student.findOne({ _id: room.occupantId }); - return { - occupantName: occupant.name, - roomId: room._id, - }; - }) - ); - - res.json(occupants); - } catch (error) { - console.error(error); - res.status(500).json({ message: "Internal Server Error" }); - } -}); From 4e1337f895e248e9dae08c288e8f84c21926fa43 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:12:34 +0530 Subject: [PATCH 04/41] added a list Id attribute to track who listed a item --- backend/models/lost_and_found.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/models/lost_and_found.js b/backend/models/lost_and_found.js index a191c73..83bc4e1 100644 --- a/backend/models/lost_and_found.js +++ b/backend/models/lost_and_found.js @@ -19,6 +19,10 @@ const lostAndFoundItemSchema = new mongoose.Schema({ type: String, required: true, }, + listerId: { + type: String, + required: true, + }, isLost: { type: Boolean, required: true, From 7c042d564fd39d51ed589c1e0f47519c4a1a84cb Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:27:47 +0530 Subject: [PATCH 05/41] Create uploads folder if not exist --- backend/middlewares/multerConfig.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/middlewares/multerConfig.js b/backend/middlewares/multerConfig.js index 384f487..af51179 100644 --- a/backend/middlewares/multerConfig.js +++ b/backend/middlewares/multerConfig.js @@ -1,9 +1,16 @@ import multer from "multer"; +import fs from "fs"; + +const uploadsFolder = "uploads/"; + +if (!fs.existsSync(uploadsFolder)) { + fs.mkdirSync(uploadsFolder); +} // Define storage configuration const storage = multer.diskStorage({ destination: function (req, file, cb) { - cb(null, "uploads/"); + cb(null, uploadsFolder); }, filename: function (req, file, cb) { cb(null, file.originalname); From 14e15d18db5bb5d24f5b319c49f9ec5b6b762579 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:30:54 +0530 Subject: [PATCH 06/41] added occupant name to room schema --- backend/models/room.js | 4 ++++ frontend/lib/models/room.dart | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/models/room.js b/backend/models/room.js index a2ca4c1..76865cb 100644 --- a/backend/models/room.js +++ b/backend/models/room.js @@ -13,6 +13,10 @@ const roomSchema = new mongoose.Schema({ type: String, default: null, }, + occupantName: { + type: String, + default: null, + }, }); const Room = mongoose.model("Room", roomSchema); diff --git a/frontend/lib/models/room.dart b/frontend/lib/models/room.dart index ea40e55..cf71ec3 100644 --- a/frontend/lib/models/room.dart +++ b/frontend/lib/models/room.dart @@ -1,10 +1,11 @@ class Room { - Room({this.occupantId, this.id, required this.name, this.vacant = true}); + Room({this.occupantId, this.id, required this.name, this.vacant = true, this.occupantName}); final String? id; final String name; final bool vacant; final String? occupantId; + final String? occupantName; factory Room.fromJson(Map json) { return Room( @@ -12,6 +13,7 @@ class Room { name: json['name'], vacant: json['vacant'], occupantId: json['occupantId'], + occupantName: json['occupantName'], ); } @@ -20,6 +22,7 @@ class Room { 'name': name, 'vacant': vacant, 'occupantId': occupantId, + 'occupantName': occupantName, }; } } From fc62bc3ff4ef3280f9015e5c8863adb36674b01f Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:32:15 +0530 Subject: [PATCH 07/41] Added delete method for lost and found alongwith logic to remove listing image --- .../lostAndFound/lostAndFoundResource.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 backend/resources/lostAndFound/lostAndFoundResource.js diff --git a/backend/resources/lostAndFound/lostAndFoundResource.js b/backend/resources/lostAndFound/lostAndFoundResource.js new file mode 100644 index 0000000..cd49c2b --- /dev/null +++ b/backend/resources/lostAndFound/lostAndFoundResource.js @@ -0,0 +1,32 @@ +import express from "express"; +import * as messages from "../../constants/messages.js"; +import LostAndFoundItem from "../../models/lost_and_found.js"; +import fs from "fs"; + +const lostAndFoundRouter = express.Router(); + +// DELETE item by id +lostAndFoundRouter.delete("/:id", async (req, res) => { + const itemId = req.params.id; + + try { + const item = await LostAndFoundItem.findByIdAndDelete(itemId); + + if (!item) { + return res.status(404).json({ message: messages.itemNotFound }); + } + + // Delete the image file + fs.unlink(item.imagePath, (err) => { + if (err) { + console.error(err); + } + }); + + res.json(item); + } catch (err) { + res.status(500).json({ message: messages.internalServerError }); + } +}); + +export default lostAndFoundRouter; From 286ca1d4638982b5f22f53a0e2fc274c6b9c14f8 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:33:05 +0530 Subject: [PATCH 08/41] Accomodated listerId inn get, post and put method --- .../lostAndFound/lostAndFoundListResource.js | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/backend/resources/lostAndFound/lostAndFoundListResource.js b/backend/resources/lostAndFound/lostAndFoundListResource.js index e0c2e6c..6b5f936 100644 --- a/backend/resources/lostAndFound/lostAndFoundListResource.js +++ b/backend/resources/lostAndFound/lostAndFoundListResource.js @@ -2,6 +2,7 @@ import { Router } from "express"; import LostAndFoundItem from "../../models/lost_and_found.js"; import fs from "fs/promises"; import uploader from "../../middlewares/multerConfig.js"; +import messages from "../../constants/messages.js"; const router = Router(); @@ -13,7 +14,7 @@ router.get("/", async (req, res) => { // Create an empty array to store items with images const itemsWithImages = []; -`` + // Iterate through each item for (const item of items) { // Check if imagePath is null @@ -32,6 +33,7 @@ router.get("/", async (req, res) => { imagePath: imagePathBase64, // Set imagePath to null if null in the database description: item.description, contactNumber: item.contactNumber, + listerId: item.listerId, isLost: item.isLost, }; @@ -39,36 +41,38 @@ router.get("/", async (req, res) => { itemsWithImages.push(itemWithImage); } - console.log("Retrieved items:", itemsWithImages.length); - // Send the response with the items res.json(itemsWithImages); } catch (error) { // Handle errors - console.error("Error:", error); - res.status(500).send("Error retrieving items"); + res.status(500).json({ message: messages.internalServerError }); } }); // POST method router.post("/", uploader.single("image"), async (req, res) => { - // Access the uploaded file using req.file - const file = req.file; + try { + // Access the uploaded file using req.file + const file = req.file; - // Construct the LostAndFoundItem object with data from the request - const newItem = new LostAndFoundItem({ - name: req.body.name, - lastSeenLocation: req.body.lastSeenLocation, - imagePath: file ? file.path : null, - description: req.body.description, - contactNumber: req.body.contactNumber, - isLost: req.body.isLost, - }); + // Construct the LostAndFoundItem object with data from the request + const newItem = new LostAndFoundItem({ + name: req.body.name, + lastSeenLocation: req.body.lastSeenLocation, + imagePath: file ? file.path : null, + description: req.body.description, + contactNumber: req.body.contactNumber, + listerId: req.body.listerId, + isLost: req.body.isLost, + }); - // Save the new item to the database - await newItem.save(); + // Save the new item to the database + await newItem.save(); - res.send("Added new item"); + res.json({ message: messages.itemAdded }); + } catch (error) { + res.status(500).json({ message: messages.internalServerError }); + } }); export default router; From a64333b83a660fa8475782899d6e7762c00f6205 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:34:43 +0530 Subject: [PATCH 09/41] occupant name in post and put method --- backend/resources/rooms/roomListResource.js | 3 ++- backend/resources/rooms/roomResource.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/resources/rooms/roomListResource.js b/backend/resources/rooms/roomListResource.js index ceb9cc6..6b07ab1 100644 --- a/backend/resources/rooms/roomListResource.js +++ b/backend/resources/rooms/roomListResource.js @@ -13,13 +13,14 @@ router.get("/", async (req, res) => { router.post("/", async (req, res) => { try { // Extract data from request body - const { name, vacant, occupantId } = req.body; + const { name, vacant, occupantId, occupantName } = req.body; // Create a new room instance const newRoom = new Room({ name, vacant: vacant || true, // Set default value if not provided occupantId: occupantId || null, // Set default value if not provided + occupantName: occupantName || null, // Set default value if not provided }); // Save the new room to the database diff --git a/backend/resources/rooms/roomResource.js b/backend/resources/rooms/roomResource.js index f78114f..edcab26 100644 --- a/backend/resources/rooms/roomResource.js +++ b/backend/resources/rooms/roomResource.js @@ -6,14 +6,21 @@ const router = Router(); router.put("/:id", async (req, res) => { try { const { id } = req.params; - const { occupantId } = req.body; + const { occupantName, occupantId, vacant } = req.body; const room = await Room.findById(id); if (!room) { return res.status(404).json({ error: "Room not found" }); } - room.vacant = false; - room.occupantId = occupantId || room.occupantId; + if (vacant) { + room.vacant = true; + room.occupantId = null; + room.occupantName = null; + } else { + room.vacant = false; + room.occupantId = occupantId; + room.occupantName = occupantName; + } await room.save(); From 3b9585f78a9ced5c071eec4b13745c3cfd6a5b8f Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:34:53 +0530 Subject: [PATCH 10/41] Fix imports and add lost-and-found resource --- backend/app.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/app.js b/backend/app.js index ee41b68..1f23981 100644 --- a/backend/app.js +++ b/backend/app.js @@ -16,7 +16,8 @@ import roomResource from "./resources/rooms/roomResource.js"; import lostAndFoundListResource from "./resources/lostAndFound/lostAndFoundListResource.js"; import studentListResource from "./resources/student/studentListResource.js"; import facultyListResource from "./resources/faculty/facultyListResource.js"; -import messageResource from './resources/chatroom/messageListResource.js' +import messageResource from "./resources/chatroom/messageListResource.js"; +import lostAndFoundResource from "./resources/lostAndFound/lostAndFoundResource.js"; const PORT = `${process.env.PORT || 3000}`; const app = express(); @@ -41,7 +42,8 @@ app.use("/", testResource); app.use("/rooms", roomListResource); app.use("/room", roomResource); app.use("/lost-and-found", lostAndFoundListResource); -app.use("/messages",messageResource); +app.use("/lost-and-found-item", lostAndFoundResource); +app.use("/messages", messageResource); app.get("/protected", tokenRequired, (req, res) => { res.json({ message: "Access granted" }); From 54e1d00319ec672c323e6ef4e188f8452cea4153 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:35:09 +0530 Subject: [PATCH 11/41] Updated dummy entries to add new attribute --- frontend/lib/constants/dummy_entries.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/lib/constants/dummy_entries.dart b/frontend/lib/constants/dummy_entries.dart index b1a941d..23a7a28 100644 --- a/frontend/lib/constants/dummy_entries.dart +++ b/frontend/lib/constants/dummy_entries.dart @@ -1015,6 +1015,7 @@ class DummyLostAndFound { description: 'Black Dell laptop with a sticker on the back', lastSeenLocation: 'Library', contactNumber: '+91 1234567890', + listerId: 'S001', isLost: false, ), LostAndFoundItem( @@ -1022,6 +1023,7 @@ class DummyLostAndFound { description: 'White iPhone 12 with a black case', lastSeenLocation: 'Cafeteria', contactNumber: '+91 9876543210', + listerId: 'S002', isLost: true, ), LostAndFoundItem( @@ -1029,6 +1031,7 @@ class DummyLostAndFound { description: 'Blue steel water bottle with a dent on the bottom', lastSeenLocation: 'Gymnasium', contactNumber: '+91 4567890123', + listerId: 'S003', isLost: false, ), LostAndFoundItem( @@ -1036,6 +1039,7 @@ class DummyLostAndFound { description: 'Red and black backpack with a broken zipper', lastSeenLocation: 'Auditorium', contactNumber: '+91 7890123456', + listerId: 'S004', isLost: true, ), LostAndFoundItem( @@ -1043,6 +1047,7 @@ class DummyLostAndFound { description: 'Silver wristwatch with a black leather strap', lastSeenLocation: 'Classroom 101', contactNumber: '+91 2345678901', + listerId: 'S005', isLost: false, ), LostAndFoundItem( @@ -1050,6 +1055,7 @@ class DummyLostAndFound { description: 'Green and white striped umbrella with a broken handle', lastSeenLocation: 'Student Lounge', contactNumber: '+91 8901234567', + listerId: 'S006', isLost: true, ), LostAndFoundItem( @@ -1057,6 +1063,7 @@ class DummyLostAndFound { description: 'Black aviator sunglasses with a scratch on the left lens', lastSeenLocation: 'Cafeteria', contactNumber: '+91 3456789012', + listerId: 'S007', isLost: false, ), LostAndFoundItem( @@ -1064,6 +1071,7 @@ class DummyLostAndFound { description: 'Brown leather wallet with a broken zipper', lastSeenLocation: 'Library', contactNumber: '+91 9012345678', + listerId: 'S008', isLost: true, ), LostAndFoundItem( @@ -1071,6 +1079,7 @@ class DummyLostAndFound { description: 'Black over-ear headphones with a missing ear cushion', lastSeenLocation: 'Auditorium', contactNumber: '+91 6789012345', + listerId: 'S009', isLost: false, ), LostAndFoundItem( @@ -1078,6 +1087,7 @@ class DummyLostAndFound { description: 'Blue denim jacket with a tear on the left sleeve', lastSeenLocation: 'Gymnasium', contactNumber: '+91 5678901234', + listerId: 'S010', isLost: true, ), ]; From b976caf041815aa932341606a82e012ffe7bd908 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:35:34 +0530 Subject: [PATCH 12/41] Add textAlign, maxLength, and maxLines properties to MaterialTextFormField --- .../lib/components/material_textformfield.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/lib/components/material_textformfield.dart b/frontend/lib/components/material_textformfield.dart index a1a878e..cce0a4e 100644 --- a/frontend/lib/components/material_textformfield.dart +++ b/frontend/lib/components/material_textformfield.dart @@ -12,7 +12,10 @@ class MaterialTextFormField extends StatelessWidget { this.hintColor, this.enabled, this.controllerLessValue, - this.onTap}); + this.onTap, + this.textAlign, + this.maxLength, + this.maxLines}); final TextEditingController? controller; final String? Function(String?)? validator; @@ -24,6 +27,9 @@ class MaterialTextFormField extends StatelessWidget { final bool? enabled; final String? controllerLessValue; final Function? onTap; + final TextAlign? textAlign; + final int? maxLength; + final int? maxLines; @override Widget build(BuildContext context) { @@ -35,11 +41,12 @@ class MaterialTextFormField extends StatelessWidget { onTap: () => onTap != null ? onTap!() : null, enabled: enabled ?? true, controller: controller ?? substituteController, - maxLines: 1, + textAlign: textAlign ?? TextAlign.start, + maxLines: maxLines ?? 1, + maxLength: maxLength, onChanged: (value) => onChanged != null ? onChanged!(value) : null, validator: (value) => validator != null ? validator!(value) : null, - onFieldSubmitted: (value) => - onSubmitted != null ? onSubmitted!(value) : null, + onFieldSubmitted: (value) => onSubmitted != null ? onSubmitted!(value) : null, cursorColor: Colors.teal.shade900, style: TextStyle( color: Colors.teal.shade900, From 0d714d472f99aee41d7123090b7dc02e8e00ff28 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:35:42 +0530 Subject: [PATCH 13/41] Add listerId field to LostAndFoundItem model --- frontend/lib/models/lost_and_found_item.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/lib/models/lost_and_found_item.dart b/frontend/lib/models/lost_and_found_item.dart index 38a9ef1..d8ad60b 100644 --- a/frontend/lib/models/lost_and_found_item.dart +++ b/frontend/lib/models/lost_and_found_item.dart @@ -7,6 +7,7 @@ class LostAndFoundItem { String? imagePath; String description; String contactNumber; + String listerId; bool isLost; LostAndFoundItem({ @@ -15,8 +16,9 @@ class LostAndFoundItem { required this.lastSeenLocation, this.imagePath, required this.description, - required this.isLost, required this.contactNumber, + required this.listerId, + required this.isLost, }); factory LostAndFoundItem.fromJson(Map json) { @@ -26,8 +28,9 @@ class LostAndFoundItem { lastSeenLocation: json['lastSeenLocation'], imagePath: json['imagePath'], description: json['description'], - isLost: json['isLost'], contactNumber: json['contactNumber'], + listerId: json['listerId'], + isLost: json['isLost'], ); } @@ -37,8 +40,9 @@ class LostAndFoundItem { 'lastSeenLocation': lastSeenLocation, 'imagePath': imagePath, 'description': description, - 'isLost': isLost, 'contact': contactNumber, + 'listerId': listerId, + 'isLost': isLost, }; } } From 79769e462b8cacf65f30a92e2bddd1e6403e085b Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:36:02 +0530 Subject: [PATCH 14/41] Add models and update provider in LostAndFoundProvider --- .../lib/provider/lost_and_found_provider.dart | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/frontend/lib/provider/lost_and_found_provider.dart b/frontend/lib/provider/lost_and_found_provider.dart index 3b3968b..565dcef 100644 --- a/frontend/lib/provider/lost_and_found_provider.dart +++ b/frontend/lib/provider/lost_and_found_provider.dart @@ -11,13 +11,16 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'dart:io'; import '../constants/constants.dart'; +import '../models/admin.dart'; +import '../models/faculty.dart'; +import '../models/student.dart'; +import 'auth_provider.dart'; final lostAndFoundProvider = StateNotifierProvider((ref) => LostAndFoundStateNotifier(ref)); class LostAndFoundState { final List lostAndFoundItemList; - final List lostAndFoundImageList; final TextEditingController itemNameController; final TextEditingController itemDescriptionController; final TextEditingController lastSeenLocationController; @@ -29,7 +32,6 @@ class LostAndFoundState { LostAndFoundState({ required this.lostAndFoundItemList, - required this.lostAndFoundImageList, required this.itemNameController, required this.itemDescriptionController, required this.lastSeenLocationController, @@ -54,7 +56,6 @@ class LostAndFoundState { }) { return LostAndFoundState( lostAndFoundItemList: lostAndFoundItemList ?? this.lostAndFoundItemList, - lostAndFoundImageList: lostAndFoundImageList ?? this.lostAndFoundImageList, itemNameController: itemNameController ?? this.itemNameController, itemDescriptionController: itemDescriptionController ?? this.itemDescriptionController, lastSeenLocationController: lastSeenLocationController ?? this.lastSeenLocationController, @@ -69,11 +70,11 @@ class LostAndFoundState { class LostAndFoundStateNotifier extends StateNotifier { LostAndFoundStateNotifier(Ref ref) - : _api = ref.read(lostAndFoundRepositoryProvider), + : _authState = ref.read(authProvider), + _api = ref.read(lostAndFoundRepositoryProvider), super( LostAndFoundState( - lostAndFoundItemList: DummyLostAndFound.lostAndFoundItems, - lostAndFoundImageList: [], + lostAndFoundItemList: [], itemNameController: TextEditingController(), itemDescriptionController: TextEditingController(), lastSeenLocationController: TextEditingController(), @@ -88,21 +89,39 @@ class LostAndFoundStateNotifier extends StateNotifier { } final LostAndFoundRepository _api; - + final AuthState _authState; final Logger _logger = Logger(); - void addItem() { + Future addItem() async { + + String userId; + + if (_authState.currentUserRole == 'student') { + userId = (_authState.currentUser as Student).id; + } else if (_authState.currentUserRole == 'faculty') { + userId = (_authState.currentUser as Faculty).id; + } else if (_authState.currentUserRole == 'admin') { + userId = (_authState.currentUser as Admin).id; + } else { + return; + } + final LostAndFoundItem item = LostAndFoundItem( name: state.itemNameController.text, lastSeenLocation: state.lastSeenLocationController.text, imagePath: state.selectedImage?.path, description: state.itemDescriptionController.text, isLost: state.listingStatus == LostAndFoundConstants.lostState, + listerId: userId, contactNumber: state.contactNumberController.text, ); state = state.copyWith(loadingState: LoadingState.progress); - _api.addLostAndFoundItem(item); - loadItems(); + if(await _api.addLostAndFoundItem(item)){ + loadItems(); + } + else{ + state = state.copyWith(loadingState: LoadingState.error); + } } launchCaller(String number) async { @@ -178,4 +197,14 @@ class LostAndFoundStateNotifier extends StateNotifier { Image imageFromBase64String(String base64String) { return Image.memory(base64Decode(base64String)); } + + Future deleteItem(String id) async { + state = state.copyWith(loadingState: LoadingState.progress); + if(await _api.deleteLostAndFoundItem(id)){ + loadItems(); + } + else{ + state = state.copyWith(loadingState: LoadingState.error); + } + } } From 3525383c9b3c36377790bda5af0379df6d79d911 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:36:44 +0530 Subject: [PATCH 15/41] Add user models and import auth provider --- frontend/lib/provider/room_provider.dart | 45 ++++++++++++++++++++---- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/frontend/lib/provider/room_provider.dart b/frontend/lib/provider/room_provider.dart index b4a40b2..f53df4f 100644 --- a/frontend/lib/provider/room_provider.dart +++ b/frontend/lib/provider/room_provider.dart @@ -7,8 +7,12 @@ import 'package:smart_insti_app/components/borderless_button.dart'; import 'package:smart_insti_app/constants/dummy_entries.dart'; import 'package:smart_insti_app/components/menu_tile.dart'; import 'package:smart_insti_app/models/room.dart'; +import 'package:smart_insti_app/provider/auth_provider.dart'; import 'dart:io'; import '../constants/constants.dart'; +import '../models/admin.dart'; +import '../models/faculty.dart'; +import '../models/student.dart'; import '../repositories/room_repository.dart'; final roomProvider = StateNotifierProvider((ref) => RoomProvider(ref)); @@ -46,7 +50,8 @@ class RoomState { class RoomProvider extends StateNotifier { RoomProvider(Ref ref) - : _api = ref.read(roomRepositoryProvider), + : _authState = ref.read(authProvider), + _api = ref.read(roomRepositoryProvider), super( RoomState( roomList: DummyRooms.rooms, @@ -61,6 +66,7 @@ class RoomProvider extends StateNotifier { final RoomRepository _api; final Logger _logger = Logger(); + final AuthState _authState; void pickSpreadsheet() async { FilePickerResult? result = await FilePicker.platform.pickFiles(); @@ -95,20 +101,45 @@ class RoomProvider extends StateNotifier { } Future loadRooms() async { - state = state.copyWith(loadingState: LoadingState.progress); final rooms = await _api.getRooms(); + // _api.getOccupants(); final newState = state.copyWith(roomList: rooms, loadingState: LoadingState.success); state = newState; } Future reserveRoom(Room room) async { - state = state.copyWith( - loadingState: LoadingState.progress, - ); + String userId; + String userName; + + if (_authState.currentUserRole == 'student') { + userId = (_authState.currentUser as Student).id; + userName = (_authState.currentUser as Student).name; + } else if (_authState.currentUserRole == 'faculty') { + userId = (_authState.currentUser as Faculty).id; + userName = (_authState.currentUser as Faculty).name; + } else if (_authState.currentUserRole == 'admin') { + userId = (_authState.currentUser as Admin).id; + userName = (_authState.currentUser as Admin).name; + } else { + return; + } - await _api.reserveRoom(room.id!, '12345'); + state = state.copyWith(loadingState: LoadingState.progress); + if (await _api.reserveRoom(room.id!, userId, userName)) { + loadRooms(); + } else { + state = state.copyWith(loadingState: LoadingState.error); + } + } + + Future vacateRoom(Room room) async { + state = state.copyWith(loadingState: LoadingState.progress); - await loadRooms(); + if (await _api.vacateRoom(room.id!)) { + loadRooms(); + } else { + state = state.copyWith(loadingState: LoadingState.error); + } } void buildRoomTiles(BuildContext context) async { From 7bc0c8477d25f05a9ed5211692fca8f27ac39285 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:36:57 +0530 Subject: [PATCH 16/41] Add logger and implement deleteLostAndFoundItem method --- .../lost_and_found_repository.dart | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/frontend/lib/repositories/lost_and_found_repository.dart b/frontend/lib/repositories/lost_and_found_repository.dart index ac04aab..59abad5 100644 --- a/frontend/lib/repositories/lost_and_found_repository.dart +++ b/frontend/lib/repositories/lost_and_found_repository.dart @@ -17,6 +17,8 @@ class LostAndFoundRepository { ), ); + final Logger _logger = Logger(); + Future> lostAndFoundItems() async { try { final response = await _client.get('/lost-and-found'); @@ -31,26 +33,42 @@ class LostAndFoundRepository { } } - Future addLostAndFoundItem(LostAndFoundItem item) async { + Future deleteLostAndFoundItem(String id) async { + try { + final response = await _client.delete('/lost-and-found-item/$id'); + Logger().i(response.data); + return true; + } catch (e) { + Logger().e(e); + return false; + } + } + + Future addLostAndFoundItem(LostAndFoundItem item) async { try { String? fileName = item.imagePath?.split('/').last; - FormData formData = FormData.fromMap({ - "name": item.name, - "lastSeenLocation": item.lastSeenLocation, - "description": item.description, - "contactNumber": item.contactNumber, - "isLost": item.isLost, - "image": item.imagePath != null - ? await MultipartFile.fromFile( - item.imagePath!, - filename: fileName, - ) - : null, - }); + FormData formData = FormData.fromMap( + { + "name": item.name, + "lastSeenLocation": item.lastSeenLocation, + "description": item.description, + "contactNumber": item.contactNumber, + "isLost": item.isLost, + "listerId": item.listerId, + "image": item.imagePath != null + ? await MultipartFile.fromFile( + item.imagePath!, + filename: fileName, + ) + : null, + }, + ); final response = await _client.post('/lost-and-found', data: formData); - Logger().w(response.data); + Logger().i(response.data); + return true; } catch (e) { Logger().e(e); + return false; } } } From 57da56d16862f602fe49069553eaf3f2312ac588 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:37:06 +0530 Subject: [PATCH 17/41] Add occupant name to reserveRoom method and add vacateRoom method --- frontend/lib/repositories/room_repository.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/lib/repositories/room_repository.dart b/frontend/lib/repositories/room_repository.dart index e14a1a7..d6c0334 100644 --- a/frontend/lib/repositories/room_repository.dart +++ b/frontend/lib/repositories/room_repository.dart @@ -27,12 +27,25 @@ class RoomRepository { } } - Future reserveRoom(String roomId, String occupantId) async { + Future reserveRoom(String roomId, String occupantId, String userName) async { try { - final response = await _client.put('/room/$roomId', data: {'occupantId': occupantId}); + final response = await _client.put('/room/$roomId', data: {'occupantName' : userName ,'occupantId': occupantId, 'vacant': false }); Logger().i(response.data); + return true; } catch (e) { Logger().e(e); + return false; + } + } + + Future vacateRoom(String roomId) async { + try { + final response = await _client.put('/room/$roomId', data: {'occupantId': null, 'vacant': true }); + Logger().i(response.data); + return true; + } catch (e) { + Logger().e(e); + return false; } } From c2b50b8df579a16810bf2959fbc83c48457b2ad2 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:37:40 +0530 Subject: [PATCH 18/41] Updated lost and found listing info card and added delete functionality --- frontend/lib/screens/user/lost_and_found.dart | 223 ++++++++++++++---- 1 file changed, 176 insertions(+), 47 deletions(-) diff --git a/frontend/lib/screens/user/lost_and_found.dart b/frontend/lib/screens/user/lost_and_found.dart index ce2bbda..cb0423a 100644 --- a/frontend/lib/screens/user/lost_and_found.dart +++ b/frontend/lib/screens/user/lost_and_found.dart @@ -1,13 +1,18 @@ import 'package:animated_toggle_switch/animated_toggle_switch.dart'; +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:logger/logger.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:smart_insti_app/components/borderless_button.dart'; import 'package:smart_insti_app/components/material_textformfield.dart'; import 'package:smart_insti_app/provider/lost_and_found_provider.dart'; import '../../components/image_tile.dart'; import '../../constants/constants.dart'; +import '../../models/admin.dart'; +import '../../models/faculty.dart'; +import '../../models/student.dart'; import '../../provider/auth_provider.dart'; class LostAndFound extends ConsumerWidget { @@ -189,7 +194,7 @@ class LostAndFound extends ConsumerWidget { label: const Text('Cancel'), ), const Spacer(), - ElevatedButton( + BorderlessButton( onPressed: () { if (_formKey.currentState!.validate()) { ref.read(lostAndFoundProvider.notifier).addItem(); @@ -197,7 +202,9 @@ class LostAndFound extends ConsumerWidget { context.pop(); } }, - child: const Text('Add'), + label: const Text('Add'), + backgroundColor: Colors.blueAccent.shade100.withOpacity(0.5), + splashColor: Colors.blue.shade700, ), ], ), @@ -256,51 +263,170 @@ class LostAndFound extends ConsumerWidget { context: context, builder: (_) => Dialog( child: Padding( - padding: const EdgeInsets.all(15.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Item name : ${item.name}", - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 20), - ), - const SizedBox(height: 20), - Text( - "Item description : \n${item.description}", - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16), - ), - const SizedBox(height: 20), - Text( - "Last seen at : ${item.lastSeenLocation}", - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - item.contactNumber, - style: const TextStyle(fontSize: 16), + padding: const EdgeInsets.only(left: 35, right: 35, top: 20, bottom: 20), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Item Name", style: TextStyle(fontSize: 20)), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.tealAccent.withOpacity(0.4), ), - const SizedBox(width: 10), - IconButton( - onPressed: () => ref - .read(lostAndFoundProvider.notifier) - .launchCaller(item.contactNumber), - icon: const Icon(Icons.call, color: Colors.green)), - ], - ), - const SizedBox(height: 20), - BorderlessButton( - onPressed: () => context.pop(), - backgroundColor: Colors.redAccent.shade100.withOpacity(0.5), - splashColor: Colors.red.shade700, - label: const Text('Close'), - ), - ], + width: double.infinity, + child: AutoSizeText( + item.name, + style: TextStyle(fontSize: 15, color: Colors.teal.shade900), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 20), + const Text("Item Description", style: TextStyle(fontSize: 20)), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.tealAccent.withOpacity(0.4), + ), + width: double.infinity, + child: AutoSizeText( + item.description, + style: TextStyle(fontSize: 15, color: Colors.teal.shade900), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 20), + const Text("Last seen at", style: TextStyle(fontSize: 20)), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.tealAccent.withOpacity(0.4), + ), + width: double.infinity, + child: AutoSizeText( + item.lastSeenLocation, + style: TextStyle(fontSize: 15, color: Colors.teal.shade900), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 20), + const Text("Contact", style: TextStyle(fontSize: 20)), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.tealAccent.withOpacity(0.4), + ), + width: 185, + child: AutoSizeText( + item.contactNumber, + style: TextStyle(fontSize: 15, color: Colors.teal.shade900), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 10), + Container( + width: 65, + height: 65, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.orangeAccent.withOpacity(0.5), + ), + child: GestureDetector( + onTap: () => ref + .read(lostAndFoundProvider.notifier) + .launchCaller(item.contactNumber), + child: const Icon( + Icons.call, + color: Colors.orange, + ), + ), + ), + ], + ), + const SizedBox(height: 30), + Consumer( + builder: (_, ref, __) { + String userId; + final authState = ref.watch(authProvider); + if (ref.read(authProvider).currentUserRole == 'student') { + userId = (authState.currentUser as Student).id; + } else if (authState.currentUserRole == 'faculty') { + userId = (authState.currentUser as Faculty).id; + } else if (authState.currentUserRole == 'admin') { + userId = (authState.currentUser as Admin).id; + } else { + return const SizedBox.shrink(); + } + + if (userId == item.listerId) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + SizedBox( + height: 55, + width: 100, + child: BorderlessButton( + onPressed: () => context.pop(), + backgroundColor: Colors.blueAccent.shade100.withOpacity(0.5), + splashColor: Colors.blue.shade700, + label: const Text('Close'), + ), + ), + Container( + width: 55, + height: 55, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.redAccent.withOpacity(0.5), + ), + child: GestureDetector( + onTap: () { + ref.read(lostAndFoundProvider.notifier).deleteItem(item.id!); + context.pop(); + }, + child: const Icon( + Icons.delete, + color: Colors.red, + ), + ), + ), + ], + ); + } else { + return Center( + child: SizedBox( + height: 55, + width: 100, + child: BorderlessButton( + onPressed: () => context.pop(), + backgroundColor: Colors.blueAccent.shade100.withOpacity(0.5), + splashColor: Colors.blue.shade700, + label: const Text('Close'), + ), + ), + ); + } + }, + ), + ], + ), ), ), ), @@ -309,7 +435,10 @@ class LostAndFound extends ConsumerWidget { ], ) : const Center( - child: Text("No Listings"), + child: Text( + 'No lost items :)', + style: TextStyle(fontSize: 30, color: Colors.black38), + ), )) : const Center(child: CircularProgressIndicator()), ), From 707df948c041a3f7632d1b339a18de5c9918d998 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:38:18 +0530 Subject: [PATCH 19/41] Added logic to vaccant room for the occupier only --- frontend/lib/screens/user/room_vacancy.dart | 128 ++++++++++++++------ 1 file changed, 90 insertions(+), 38 deletions(-) diff --git a/frontend/lib/screens/user/room_vacancy.dart b/frontend/lib/screens/user/room_vacancy.dart index 665804f..b352e66 100644 --- a/frontend/lib/screens/user/room_vacancy.dart +++ b/frontend/lib/screens/user/room_vacancy.dart @@ -6,7 +6,10 @@ import 'package:smart_insti_app/components/borderless_button.dart'; import 'package:smart_insti_app/components/menu_tile.dart'; import 'package:smart_insti_app/constants/constants.dart'; import 'package:smart_insti_app/provider/room_provider.dart'; +import '../../models/admin.dart'; +import '../../models/faculty.dart'; import '../../models/room.dart'; +import '../../models/student.dart'; import '../../provider/auth_provider.dart'; class RoomVacancy extends ConsumerWidget { @@ -34,44 +37,81 @@ class RoomVacancy extends ConsumerWidget { for (Room room in ref.read(roomProvider).roomList) MenuTile( title: room.name, - onTap: () => showDialog( - context: context, - builder: (_) => Dialog( - child: Padding( - padding: const EdgeInsets.all(15.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text("${room.name} : ${room.vacant ? 'Vacant' : 'Occupied'}", - style: const TextStyle(fontSize: 20)), - const SizedBox(height: 20), - room.vacant - ? Row( - children: [ - BorderlessButton( - onPressed: () => context.pop(), - label: const Text('Cancel'), - backgroundColor: Colors.red.shade100, - splashColor: Colors.redAccent, - ), - const Spacer(), - BorderlessButton( - onPressed: () { - ref.read(roomProvider.notifier).reserveRoom(room); - context.pop(); - }, - label: const Text('Reserve'), - backgroundColor: Colors.blue.shade100, - splashColor: Colors.blueAccent, - ), - ], - ) - : Container(), - ], + onTap: () { + showDialog( + context: context, + builder: (_) => Dialog( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text("${room.name} : ${room.vacant ? 'Vacant' : 'Occupied'}", + style: const TextStyle(fontSize: 20)), + room.vacant + ? const SizedBox.shrink() + : Text( + "by : ${room.occupantName}", + textAlign: TextAlign.center, + maxLines: 1, + style: const TextStyle(overflow: TextOverflow.ellipsis), + ), + const SizedBox(height: 20), + room.vacant + ? Row( + children: [ + BorderlessButton( + onPressed: () => context.pop(), + label: const Text('Cancel'), + backgroundColor: Colors.red.shade100, + splashColor: Colors.redAccent, + ), + const Spacer(), + BorderlessButton( + onPressed: () { + ref.read(roomProvider.notifier).reserveRoom(room); + context.pop(); + }, + label: const Text('Reserve'), + backgroundColor: Colors.blue.shade100, + splashColor: Colors.blueAccent, + ), + ], + ) + : Consumer( + builder: (_, ref, __) { + String userId; + final authState = ref.watch(authProvider); + if (authState.currentUserRole == 'student') { + userId = (authState.currentUser as Student).id; + } else if (authState.currentUserRole == 'faculty') { + userId = (authState.currentUser as Faculty).id; + } else if (authState.currentUserRole == 'admin') { + userId = (authState.currentUser as Admin).id; + } else { + return const SizedBox.shrink(); + } + if (userId == room.occupantId) { + return BorderlessButton( + onPressed: () { + ref.read(roomProvider.notifier).vacateRoom(room); + context.pop(); + }, + label: const Text('Vacate'), + backgroundColor: Colors.red.shade100, + splashColor: Colors.redAccent, + ); + } else { + return const SizedBox.shrink(); + } + }, + ), + ], + ), ), ), - ), - ), + ); + }, body: [ const SizedBox(height: 5), Text( @@ -80,7 +120,14 @@ class RoomVacancy extends ConsumerWidget { style: const TextStyle(fontSize: 14), ), const SizedBox(height: 10), - room.vacant ? Container() : const Text("by : Aadarsh"), + room.vacant + ? Container() + : Text( + "by : ${room.occupantName}", + textAlign: TextAlign.center, + maxLines: 1, + style: const TextStyle(overflow: TextOverflow.ellipsis), + ), ], icon: Icons.class_, primaryColor: room.vacant ? Colors.greenAccent.shade100 : Colors.redAccent.shade100, @@ -88,7 +135,12 @@ class RoomVacancy extends ConsumerWidget { ), ], ) - : const Center(child: Text('No rooms found')) + : const Center( + child: Text( + 'No Rooms to go to', + style: TextStyle(fontSize: 30, color: Colors.black38), + ), + ) : const Center(child: CircularProgressIndicator()), ), ); From b69898eb4a6e2ff6d6b90f06181d13febdf59c90 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 22:51:01 +0530 Subject: [PATCH 20/41] Post merge cleanup --- backend/app.js | 1 - backend/resources/lostAndFound/lostAndFoundListResource.js | 2 +- frontend/lib/components/material_textformfield.dart | 4 +--- frontend/lib/main.dart | 2 -- frontend/lib/screens/loading_page.dart | 3 --- frontend/lib/screens/user/timetables.dart | 2 -- 6 files changed, 2 insertions(+), 12 deletions(-) diff --git a/backend/app.js b/backend/app.js index 5231d3d..64cd569 100644 --- a/backend/app.js +++ b/backend/app.js @@ -20,7 +20,6 @@ import messageResource from "./resources/chatroom/messageListResource.js"; import lostAndFoundResource from "./resources/lostAndFound/lostAndFoundResource.js"; import timetableListResource from "./resources/timetable/timetableListResource.js"; import timetableResource from "./resources/timetable/timetableResource.js"; -import messageResource from './resources/chatroom/messageListResource.js' const PORT = `${process.env.PORT || 3000}`; const app = express(); diff --git a/backend/resources/lostAndFound/lostAndFoundListResource.js b/backend/resources/lostAndFound/lostAndFoundListResource.js index 6b5f936..a88285a 100644 --- a/backend/resources/lostAndFound/lostAndFoundListResource.js +++ b/backend/resources/lostAndFound/lostAndFoundListResource.js @@ -2,7 +2,7 @@ import { Router } from "express"; import LostAndFoundItem from "../../models/lost_and_found.js"; import fs from "fs/promises"; import uploader from "../../middlewares/multerConfig.js"; -import messages from "../../constants/messages.js"; +import * as messages from "../../constants/messages.js"; const router = Router(); diff --git a/frontend/lib/components/material_textformfield.dart b/frontend/lib/components/material_textformfield.dart index 426eb45..82e03fb 100644 --- a/frontend/lib/components/material_textformfield.dart +++ b/frontend/lib/components/material_textformfield.dart @@ -17,8 +17,7 @@ class MaterialTextFormField extends StatelessWidget { this.maxLength, this.maxLines, this.textAlign, - this.inputFormatters, - this.onTap}); + this.inputFormatters,}); final TextEditingController? controller; final String? Function(String?)? validator; @@ -32,7 +31,6 @@ class MaterialTextFormField extends StatelessWidget { final TextAlign? textAlign; final List? inputFormatters; final Function? onTap; - final TextAlign? textAlign; final int? maxLength; final int? maxLines; diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 265db95..11c1468 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:auto_orientation/auto_orientation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -8,7 +7,6 @@ import 'package:smart_insti_app/routes/routes.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: ".env"); - AutoOrientation.portraitUpMode(); runApp(const ProviderScope(child: SmartInstiApp())); } diff --git a/frontend/lib/screens/loading_page.dart b/frontend/lib/screens/loading_page.dart index e22d9a9..89566c0 100644 --- a/frontend/lib/screens/loading_page.dart +++ b/frontend/lib/screens/loading_page.dart @@ -1,12 +1,9 @@ import 'package:animated_text_kit/animated_text_kit.dart'; -import 'package:auto_orientation/auto_orientation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; -import 'package:logger/logger.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:smart_insti_app/provider/auth_provider.dart'; -import 'package:smart_insti_app/services/auth/auth_service.dart'; class LoadingPage extends ConsumerWidget { const LoadingPage({super.key}); diff --git a/frontend/lib/screens/user/timetables.dart b/frontend/lib/screens/user/timetables.dart index f3a291a..8e8d05a 100644 --- a/frontend/lib/screens/user/timetables.dart +++ b/frontend/lib/screens/user/timetables.dart @@ -1,10 +1,8 @@ import 'package:animated_toggle_switch/animated_toggle_switch.dart'; -import 'package:auto_orientation/auto_orientation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; -import 'package:logger/logger.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:smart_insti_app/components/borderless_button.dart'; import 'package:smart_insti_app/components/material_textformfield.dart'; From 63d7d874ca352187f7832cd69b81bfebc0bd2c88 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Fri, 16 Feb 2024 23:19:24 +0530 Subject: [PATCH 21/41] Messages for room resources --- backend/constants/messages.js | 5 +++++ backend/resources/rooms/roomListResource.js | 9 +++------ backend/resources/rooms/roomResource.js | 9 ++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/backend/constants/messages.js b/backend/constants/messages.js index ce504a3..beceb3f 100644 --- a/backend/constants/messages.js +++ b/backend/constants/messages.js @@ -36,3 +36,8 @@ export const deleted = "Deleted"; export const itemNotFound = "Item not found"; export const itemAdded = "Item added successfully"; export const itemDeleted = "Item deleted successfully"; + +// Rooom Vacancy +export const roomNotFound = "Room not found"; +export const roomUpdated = "Room updated successfully"; +export const roomCreated = "Room created successfully"; diff --git a/backend/resources/rooms/roomListResource.js b/backend/resources/rooms/roomListResource.js index 6b07ab1..a1cf459 100644 --- a/backend/resources/rooms/roomListResource.js +++ b/backend/resources/rooms/roomListResource.js @@ -1,5 +1,6 @@ import { Router } from "express"; import Room from "../../models/room.js"; +import * as messages from "../../constants/messages.js"; const router = Router(); // GET method @@ -25,16 +26,12 @@ router.post("/", async (req, res) => { // Save the new room to the database await newRoom.save(); - console.log("Room created successfully"); // Respond with success message - res - .status(201) - .json({ message: "Room created successfully", room: newRoom }); + res.status(201).json({ message: messages.roomCreated, room: newRoom }); } catch (error) { // Handle errors - console.error("Error creating room:", error); - res.status(500).json({ error: "Internal server error" }); + res.status(500).json({ message: messages.internalServerError }); } }); diff --git a/backend/resources/rooms/roomResource.js b/backend/resources/rooms/roomResource.js index edcab26..1cd1a4b 100644 --- a/backend/resources/rooms/roomResource.js +++ b/backend/resources/rooms/roomResource.js @@ -1,5 +1,6 @@ import { Router } from "express"; import Room from "../../models/room.js"; +import * as messages from "../../constants/messages.js"; const router = Router(); // PUT method @@ -9,7 +10,7 @@ router.put("/:id", async (req, res) => { const { occupantName, occupantId, vacant } = req.body; const room = await Room.findById(id); if (!room) { - return res.status(404).json({ error: "Room not found" }); + return res.status(404).json({ message: messages.roomNotFound }); } if (vacant) { @@ -24,11 +25,9 @@ router.put("/:id", async (req, res) => { await room.save(); - console.log("Room updated successfully"); - res.json({ message: "Room updated successfully", room }); + res.json({ message: messages.roomUpdated, room }); } catch (error) { - console.error("Error updating room:", error); - res.status(500).json({ error: "Internal server error" }); + res.status(500).json({ message: messages.internalServerError }); } }); From 5d5de50de7f2b55270783462f6fc6e5aab019703 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 14:59:12 +0530 Subject: [PATCH 22/41] Added add and get methods in menu repository --- .../repositories/mess_menu_repository.dart | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 frontend/lib/repositories/mess_menu_repository.dart diff --git a/frontend/lib/repositories/mess_menu_repository.dart b/frontend/lib/repositories/mess_menu_repository.dart new file mode 100644 index 0000000..8c3fd91 --- /dev/null +++ b/frontend/lib/repositories/mess_menu_repository.dart @@ -0,0 +1,41 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:logger/logger.dart'; +import 'package:smart_insti_app/models/mess_menu.dart'; + +final messMenuRepositoryProvider = Provider((ref) => MessMenuRepository()); + +class MessMenuRepository{ + + final _client = Dio( + BaseOptions( + baseUrl: dotenv.env['BACKEND_DOMAIN']!, + ), + ); + + final Logger _logger = Logger(); + + Future> getMessMenu() async { + try { + final response = await _client.get('/mess-menus'); + List messMenus = (response.data as List).map((e) => MessMenu.fromJson(e)).toList(); + return messMenus; + } catch (e) { + _logger.e(e); + return []; + } + } + + Future addMessMenu(MessMenu messMenu) async { + try { + final response = await _client.post('/mess-menus', data: messMenu.toJson()); + _logger.i(response.data); + return true; + } catch (e) { + _logger.e(e); + return false; + } + } + +} \ No newline at end of file From 5c8c9716012a72a6dde3ae2d159cb31f150f09a1 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 14:59:35 +0530 Subject: [PATCH 23/41] added update and delete in menu repo --- .../repositories/mess_menu_repository.dart | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/frontend/lib/repositories/mess_menu_repository.dart b/frontend/lib/repositories/mess_menu_repository.dart index 8c3fd91..dc4e67b 100644 --- a/frontend/lib/repositories/mess_menu_repository.dart +++ b/frontend/lib/repositories/mess_menu_repository.dart @@ -38,4 +38,25 @@ class MessMenuRepository{ } } + Future updateMessMenu(String menuId, MessMenu messMenu) async { + try { + final response = await _client.put('/mess-menu/$menuId', data: messMenu.toJson()); + _logger.i(response.data); + return true; + } catch (e) { + _logger.e(e); + return false; + } + } + + Future deleteMessMenu(String id) async { + try { + final response = await _client.delete('/mess-menu/$id'); + _logger.i(response.data); + return true; + } catch (e) { + _logger.e(e); + return false; + } + } } \ No newline at end of file From 9bd8c70ec952dc267ba1a196c7820065011f6e44 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 14:59:45 +0530 Subject: [PATCH 24/41] Add mess menu constants --- backend/constants/messages.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/constants/messages.js b/backend/constants/messages.js index beceb3f..3f2d397 100644 --- a/backend/constants/messages.js +++ b/backend/constants/messages.js @@ -41,3 +41,8 @@ export const itemDeleted = "Item deleted successfully"; export const roomNotFound = "Room not found"; export const roomUpdated = "Room updated successfully"; export const roomCreated = "Room created successfully"; + +// Mess Menu +export const messMenuNotFound = "Mess Menu not found"; +export const messMenuAdded = "Mess Menu added successfully"; +export const messMenuUpdated = "Mess Menu updated successfully"; From 153bf62c669b0fa820f2a55f1e58615b7f3e471a Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 14:59:56 +0530 Subject: [PATCH 25/41] Refactor mess_menu.js model --- backend/models/mess_menu.js | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/backend/models/mess_menu.js b/backend/models/mess_menu.js index ed536e5..3e389bb 100644 --- a/backend/models/mess_menu.js +++ b/backend/models/mess_menu.js @@ -1,24 +1,20 @@ -const mongoose = require('mongoose'); +import mongoose from "mongoose"; const messMenuSchema = new mongoose.Schema({ - id: { - type: String, - required: true + kitchenName: { + type: String, + required: true, + }, + messMenu: { + type: Map, + of: { + type: Map, + of: [String], }, - kitchenName: { - type: String, - required: true - }, - messMenu: { - type: Map, - of: { - type: Map, - of: [String] - }, - required: true - } + required: true, + }, }); -const MessMenu = mongoose.model('MessMenu', messMenuSchema); +const MessMenu = mongoose.model("MessMenu", messMenuSchema); -module.exports = MessMenu; +export default MessMenu; From dd8b78988d30e08e8dd24ca34ac84948647b7405 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:00:19 +0530 Subject: [PATCH 26/41] Assets update --- frontend/lib/generated/assets.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/lib/generated/assets.dart b/frontend/lib/generated/assets.dart index efa078b..cc85ea0 100644 --- a/frontend/lib/generated/assets.dart +++ b/frontend/lib/generated/assets.dart @@ -13,6 +13,7 @@ class Assets { static const String adminViewFaculty = 'lib/screens/admin/view_faculty.dart'; static const String adminViewMenu = 'lib/screens/admin/view_menu.dart'; static const String adminViewStudents = 'lib/screens/admin/view_students.dart'; + static const String assetsChatroomBg = 'lib/assets/chatroom_bg.jpeg'; static const String assetsOpenlake = 'lib/assets/openlake.png'; static const String assetsPlaceholder = 'lib/assets/placeholder.png'; static const String assetsProfilePlaceholder = 'lib/assets/profile_placeholder.png'; @@ -53,17 +54,23 @@ class Assets { static const String libMain = 'lib/main.dart'; static const String modelsAchievement = 'lib/models/achievement.dart'; static const String modelsAdmin = 'lib/models/admin.dart'; + static const String modelsBroadcastSchema = 'lib/models/broadcast_schema.dart'; static const String modelsCourse = 'lib/models/course.dart'; static const String modelsFaculty = 'lib/models/faculty.dart'; static const String modelsLostAndFoundItem = 'lib/models/lost_and_found_item.dart'; static const String modelsMessMenu = 'lib/models/mess_menu.dart'; + static const String modelsMessage = 'lib/models/message.dart'; static const String modelsRoom = 'lib/models/room.dart'; static const String modelsSkills = 'lib/models/skills.dart'; static const String modelsStudent = 'lib/models/student.dart'; static const String modelsTimtable = 'lib/models/timtable.dart'; static const String modelsUser = 'lib/models/user.dart'; + static const String providerAboutEditWidget = 'lib/provider/about_Edit_widget.dart'; + static const String providerAchievementsEditWidget = 'lib/provider/achievements_edit_widget.dart'; static const String providerAdminProvider = 'lib/provider/admin_provider.dart'; static const String providerAuthProvider = 'lib/provider/auth_provider.dart'; + static const String providerBroadcastProvider = 'lib/provider/broadcast_provider.dart'; + static const String providerChatRoomProvider = 'lib/provider/chat_room_provider.dart'; static const String providerCoursesProvider = 'lib/provider/courses_provider.dart'; static const String providerFacultyProvider = 'lib/provider/faculty_provider.dart'; static const String providerHomeProvider = 'lib/provider/home_provider.dart'; @@ -71,19 +78,26 @@ class Assets { static const String providerMenuProvider = 'lib/provider/menu_provider.dart'; static const String providerOtpProvider = 'lib/provider/otp_provider.dart'; static const String providerRoomProvider = 'lib/provider/room_provider.dart'; + static const String providerSkillsEditWidget = 'lib/provider/skills_edit_widget.dart'; static const String providerStudentProvider = 'lib/provider/student_provider.dart'; static const String providerTimetableProvider = 'lib/provider/timetable_provider.dart'; static const String repositoriesAchievementRepository = 'lib/repositories/achievement_repository.dart'; static const String repositoriesAdminRepository = 'lib/repositories/admin_repository.dart'; + static const String repositoriesBroadcastRepository = 'lib/repositories/broadcast_repository.dart'; + static const String repositoriesChatRoomRepository = 'lib/repositories/chat_room_repository.dart'; static const String repositoriesCourseRepository = 'lib/repositories/course_repository.dart'; static const String repositoriesFacultyRepository = 'lib/repositories/faculty_repository.dart'; static const String repositoriesLostAndFoundRepository = 'lib/repositories/lost_and_found_repository.dart'; + static const String repositoriesMessMenuRepository = 'lib/repositories/mess_menu_repository.dart'; static const String repositoriesRoomRepository = 'lib/repositories/room_repository.dart'; static const String repositoriesSkillRepository = 'lib/repositories/skill_repository.dart'; static const String repositoriesStudentRepository = 'lib/repositories/student_repository.dart'; static const String repositoriesTimetableRepository = 'lib/repositories/timetable_repository.dart'; static const String routesRoutes = 'lib/routes/routes.dart'; static const String screensLoadingPage = 'lib/screens/loading_page.dart'; + static const String userBroadcast = 'lib/screens/user/broadcast.dart'; + static const String userChatRoom = 'lib/screens/user/chat_room.dart'; + static const String userEditProfile = 'lib/screens/user/edit_profile.dart'; static const String userFacultyProfile = 'lib/screens/user/faculty_profile.dart'; static const String userLostAndFound = 'lib/screens/user/lost_and_found.dart'; static const String userRoomVacancy = 'lib/screens/user/room_vacancy.dart'; From 36bc38140c5c4acc7e3bd1ae278b38dff61f5582 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:03:30 +0530 Subject: [PATCH 27/41] Scroll to bottom on select menu and added buttons for update, refresh and delete --- frontend/lib/screens/admin/view_menu.dart | 322 +++++++++++++--------- 1 file changed, 197 insertions(+), 125 deletions(-) diff --git a/frontend/lib/screens/admin/view_menu.dart b/frontend/lib/screens/admin/view_menu.dart index f0cd665..7ec4fbc 100644 --- a/frontend/lib/screens/admin/view_menu.dart +++ b/frontend/lib/screens/admin/view_menu.dart @@ -10,12 +10,13 @@ import '../../constants/constants.dart'; import '../../provider/auth_provider.dart'; class ViewMessMenu extends ConsumerWidget { - const ViewMessMenu({super.key}); + ViewMessMenu({super.key}); + + final ScrollController _scrollController = ScrollController(); @override Widget build(BuildContext context, WidgetRef ref) { WidgetsBinding.instance.addPostFrameCallback((_) { - ref.watch(menuProvider.notifier).initMenuView(); if (ref.read(authProvider.notifier).tokenCheckProgress != LoadingState.progress) { ref.read(authProvider.notifier).verifyAuthTokenExistence(context, AuthConstants.adminAuthLabel.toLowerCase()); } @@ -28,141 +29,212 @@ class ViewMessMenu extends ConsumerWidget { title: const Text('Mess Menu'), ), body: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: ChoiceSelector( - onChanged: (value) => ref.read(menuProvider.notifier).setSelectViewMenu(value), - value: ref.read(menuProvider).messMenus.keys.isNotEmpty - ? ref.read(menuProvider).messMenus.keys.first - : null, - items: [ - for (String i in ref.read(menuProvider).messMenus.keys) - DropdownMenuItem( - value: i, - child: Text(i), - ), - ], - hint: 'Select Kitchen', - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 30), - child: SlideSwitcher( - onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), - containerHeight: 65, - containerWight: 380, - containerBorderRadius: 20, - slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], - containerColor: Colors.tealAccent.shade100, - children: MessMenuConstants.mealTypes, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + controller: _scrollController, + child: Consumer( + builder: (_, ref, __) { + if (ref.watch(menuProvider).loadingState == LoadingState.progress) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return Column( children: [ - Container( - height: 550, - width: 285, - margin: const EdgeInsets.only(right: 15), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.transparent, width: 0), - color: Colors.grey[200], + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: ChoiceSelector( + onChanged: (value) { + ref.read(menuProvider.notifier).setSelectViewMenu(value); + _scrollController.animateTo(_scrollController.position.maxScrollExtent + 30, + duration: const Duration(milliseconds: 500), curve: Curves.easeIn); + }, + value: ref.read(menuProvider).messMenus.keys.isNotEmpty + ? ref.read(menuProvider).messMenus.keys.first + : null, + items: [ + for (String i in ref.read(menuProvider).messMenus.keys) + DropdownMenuItem( + value: i, + child: Text(i), + ), + ], + hint: 'Select Kitchen', + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 30), + child: SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), + containerHeight: 65, + containerWight: 380, + containerBorderRadius: 20, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.mealTypes, ), - child: Column( + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Consumer( - builder: (_, ref, __) { - final menuState = ref.watch(menuProvider); - final weekDay = - ref.watch(menuProvider.notifier).getWeekDay(menuState.selectedWeekdayIndex); - final mealType = - ref.watch(menuProvider.notifier).getMealType(menuState.selectedMealTypeIndex); - return Column( - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - alignment: Alignment.topLeft, - child: Text( - "$weekDay's $mealType", - style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), - ), - ), - ], - ); - }, - ), - Consumer( - builder: (context, ref, child) { - final menuState = ref.watch(menuProvider); - final weekDay = - ref.watch(menuProvider.notifier).getWeekDay(menuState.selectedWeekdayIndex); - final mealType = - ref.watch(menuProvider.notifier).getMealType(menuState.selectedMealTypeIndex); - if (menuState.selectedViewMenu != null) { - MessMenu? selectedMenu = menuState.messMenus[menuState.selectedViewMenu]; - int length = selectedMenu?.messMenu?[weekDay]?[mealType]?.length ?? 0; - List controllers = List.generate(length, (index) => TextEditingController()); - return ListView.builder( - shrinkWrap: true, - itemCount: length, - itemBuilder: (context, index) { - controllers[index].text = selectedMenu?.messMenu?[weekDay]?[mealType]?[index] ?? ''; - return Container( - margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15), - border: Border.all(color: Colors.transparent, width: 0), - ), - child: MaterialTextFormField( - contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - controller: controllers[index], - hintText: 'Item ${index + 1}', - onChanged: (value) => - selectedMenu?.messMenu?[weekDay]?[mealType]?[index] = value, - ), + Container( + height: 550, + width: 285, + margin: const EdgeInsets.only(right: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.transparent, width: 0), + color: Colors.grey[200], + ), + child: Column( + children: [ + Consumer( + builder: (_, ref, __) { + final menuState = ref.watch(menuProvider); + final weekDay = + ref.watch(menuProvider.notifier).getWeekDay(menuState.selectedWeekdayIndex); + final mealType = + ref.watch(menuProvider.notifier).getMealType(menuState.selectedMealTypeIndex); + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + alignment: Alignment.topLeft, + child: Text( + "$weekDay's $mealType", + style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), + ), + ), + ], ); }, - ); - } else { - return const Expanded( - child: Center( - child: Text( - 'No menu added yet', - style: TextStyle(fontSize: 20, color: Colors.black38), - ), - ), - ); - } - }, + ), + Consumer( + builder: (context, ref, child) { + final menuState = ref.watch(menuProvider); + final weekDay = + ref.watch(menuProvider.notifier).getWeekDay(menuState.selectedWeekdayIndex); + final mealType = + ref.watch(menuProvider.notifier).getMealType(menuState.selectedMealTypeIndex); + if (menuState.selectedViewMenu != null) { + MessMenu? selectedMenu = menuState.messMenus[menuState.selectedViewMenu]; + int length = selectedMenu?.messMenu?[weekDay]?[mealType]?.length ?? 0; + List controllers = List.generate(length, (index) => TextEditingController()); + return ListView.builder( + shrinkWrap: true, + itemCount: length, + itemBuilder: (context, index) { + controllers[index].text = + selectedMenu?.messMenu?[weekDay]?[mealType]?[index] ?? ''; + return length != 0 + ? Container( + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + border: Border.all(color: Colors.transparent, width: 0), + ), + child: MaterialTextFormField( + contentPadding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + controller: controllers[index], + hintText: 'Item ${index + 1}', + onChanged: (value) => + selectedMenu?.messMenu?[weekDay]?[mealType]?[index] = value, + ), + ) + : const Expanded( + child: Center( + child: Text( + 'No items today', + style: TextStyle(fontSize: 20, color: Colors.black38), + ), + ), + ); + }, + ); + } else { + return const Expanded( + child: Center( + child: Text( + 'No menu selected', + style: TextStyle(fontSize: 20, color: Colors.black38), + ), + ), + ); + } + }, + ), + ], + ), + ), + Align( + alignment: Alignment.centerRight, + child: SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), + containerHeight: 550, + containerWight: 70, + containerBorderRadius: 20, + direction: Axis.vertical, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.weekdays, + ), ), ], ), ), - Align( - alignment: Alignment.centerRight, - child: SlideSwitcher( - onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), - containerHeight: 550, - containerWight: 70, - containerBorderRadius: 20, - direction: Axis.vertical, - slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], - containerColor: Colors.tealAccent.shade100, - children: MessMenuConstants.weekdays, + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + GestureDetector( + onTap: () => ref.read(menuProvider.notifier).deleteMenu(), + child: Container( + width: 55, + height: 55, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.redAccent.withOpacity(0.5), + ), + child: const Icon( + Icons.delete, + color: Colors.red, + ), + ), + ), + const SizedBox(width: 20), + GestureDetector( + onTap: () => ref.read(menuProvider.notifier).resetMenu(), + child: Container( + width: 55, + height: 55, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.greenAccent.withOpacity(0.5), + ), + child: const Icon( + Icons.refresh, + color: Colors.green, + ), + ), + ), + Spacer(), + ElevatedButton( + onPressed: () => ref.read(menuProvider.notifier).updateMenu(), + style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), + child: const Text("Update Menu"), + ), + ], ), ), + const SizedBox(height: 30), ], - ), - ), - const SizedBox(height: 30), - ], + ); + } + }, ), ), ), From e918acca3c3eddda6b23a7336ea0afb54debf295 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:03:46 +0530 Subject: [PATCH 28/41] Added json factories to mess menu --- frontend/lib/models/mess_menu.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/lib/models/mess_menu.dart b/frontend/lib/models/mess_menu.dart index bcb1a1c..906a284 100644 --- a/frontend/lib/models/mess_menu.dart +++ b/frontend/lib/models/mess_menu.dart @@ -1,7 +1,20 @@ class MessMenu { - MessMenu({this.id, this.kitchenName, this.messMenu}); + MessMenu({this.id, required this.kitchenName, this.messMenu}); String? id; - String? kitchenName; + String kitchenName; Map>>? messMenu; + + factory MessMenu.fromJson(Map json) => MessMenu( + id: json["_id"], + kitchenName: json["kitchenName"], + messMenu: Map.from(json["messMenu"]).map((k, v) => MapEntry>>( + k, Map.from(v).map((k, v) => MapEntry>(k, List.from(v.map((x) => x)))))), + ); + + Map toJson() => { + "kitchenName": kitchenName, + "messMenu": Map.from(messMenu!).map((k, v) => MapEntry( + k, Map.from(v).map((k, v) => MapEntry(k, List.from(v.map((x) => x)))))), + }; } From 4b1affe6d7c0000bef4730db658dee04485bc784 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:04:26 +0530 Subject: [PATCH 29/41] Scroll to bottom on submit kitchen name --- frontend/lib/screens/admin/add_menu.dart | 388 ++++++++++++----------- 1 file changed, 196 insertions(+), 192 deletions(-) diff --git a/frontend/lib/screens/admin/add_menu.dart b/frontend/lib/screens/admin/add_menu.dart index 240e58e..ef40f10 100644 --- a/frontend/lib/screens/admin/add_menu.dart +++ b/frontend/lib/screens/admin/add_menu.dart @@ -13,12 +13,13 @@ class AddMessMenu extends ConsumerWidget { AddMessMenu({super.key}); final _formKey = GlobalKey(); + final ScrollController _scrollController = ScrollController(); @override Widget build(BuildContext context, WidgetRef ref) { WidgetsBinding.instance.addPostFrameCallback((_) { - ref.watch(menuProvider.notifier).clear(); + ref.watch(menuProvider.notifier).clearControllers(); if (ref.read(authProvider.notifier).tokenCheckProgress != LoadingState.progress) { ref.read(authProvider.notifier).verifyAuthTokenExistence(context, AuthConstants.adminAuthLabel.toLowerCase()); } @@ -30,214 +31,217 @@ class AddMessMenu extends ConsumerWidget { appBar: AppBar( title: const Text('Add Mess Menu'), ), - body: Padding( - padding: const EdgeInsets.only(top: 30), - child: SingleChildScrollView( - child: Column( - children: [ - Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 30), - child: const Text( - "Spreadsheet Entry", - style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), - ), + body: SingleChildScrollView( + controller: _scrollController, + child: Column( + children: [ + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 30), + child: const Text( + "Spreadsheet Entry", + style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), ), - const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.only(left: 30), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text( - "Upload file here", - style: TextStyle(fontSize: 16), - ), - const SizedBox(width: 30), - ElevatedButton( - onPressed: () => ref.read(menuProvider.notifier).pickSpreadsheet(), - style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), - child: const Text("Upload Spreadsheet"), - ), - ], - ), + ), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.only(left: 30), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Text( + "Upload file here", + style: TextStyle(fontSize: 16), + ), + const SizedBox(width: 30), + ElevatedButton( + onPressed: () => ref.read(menuProvider.notifier).pickSpreadsheet(), + style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), + child: const Text("Upload Spreadsheet"), + ), + ], ), - const SizedBox(height: 30), - const TextDivider(text: "OR"), - const SizedBox(height: 30), - Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 30), - child: const Text( - "Single Entry", - style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), - ), + ), + const SizedBox(height: 30), + const TextDivider(text: "OR"), + const SizedBox(height: 30), + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 30), + child: const Text( + "Single Entry", + style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), ), - const SizedBox(height: 30), - Container( - margin: const EdgeInsets.symmetric(horizontal: 15), - child: Form( - key: _formKey, - child: MaterialTextFormField( - controller: ref.read(menuProvider).kitchenNameController, - validator: (value) => Validators.nameValidator(value), - hintText: "Enter kitchen name", - hintColor: Colors.teal.shade900.withOpacity(0.5), + ), + const SizedBox(height: 30), + Container( + margin: const EdgeInsets.symmetric(horizontal: 15), + child: Form( + key: _formKey, + child: MaterialTextFormField( + controller: ref.read(menuProvider).kitchenNameController, + validator: (value) => Validators.nameValidator(value), + hintText: "Enter kitchen name", + onSubmitted: (value) => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 500), + curve: Curves.ease, ), + hintColor: Colors.teal.shade900.withOpacity(0.5), ), ), - const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: SlideSwitcher( - onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), - containerHeight: 65, - containerWight: 380, - containerBorderRadius: 20, - slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], - containerColor: Colors.tealAccent.shade100, - children: MessMenuConstants.mealTypes, - ), + ), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), + containerHeight: 65, + containerWight: 380, + containerBorderRadius: 20, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.mealTypes, ), - const SizedBox(height: 30), - Consumer( - builder: (_, ref, ___) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SlideSwitcher( - onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), - containerHeight: 550, - containerWight: 70, - containerBorderRadius: 20, - direction: Axis.vertical, - slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], - containerColor: Colors.tealAccent.shade100, - children: MessMenuConstants.weekdays), - Container( - height: 550, - width: 285, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.transparent, width: 0), - color: Colors.grey[200], - ), - child: Column( - children: [ - Consumer( - builder: (_, ref, __) { - final weekDayIndex = ref.watch(menuProvider).selectedWeekdayIndex; - final mealTypeIndex = ref.watch(menuProvider).selectedMealTypeIndex; + ), + const SizedBox(height: 30), + Consumer( + builder: (_, ref, ___) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), + containerHeight: 550, + containerWight: 70, + containerBorderRadius: 20, + direction: Axis.vertical, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.weekdays), + Container( + height: 550, + width: 285, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.transparent, width: 0), + color: Colors.grey[200], + ), + child: Column( + children: [ + Consumer( + builder: (_, ref, __) { + final weekDayIndex = ref.watch(menuProvider).selectedWeekdayIndex; + final mealTypeIndex = ref.watch(menuProvider).selectedMealTypeIndex; - final weekDay = ref.watch(menuProvider.notifier).getWeekDay(weekDayIndex); - final mealType = ref.watch(menuProvider.notifier).getMealType(mealTypeIndex); - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - alignment: Alignment.topLeft, - child: Text( - "$weekDay's $mealType", - style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), - ), - ); - }, + final weekDay = ref.watch(menuProvider.notifier).getWeekDay(weekDayIndex); + final mealType = ref.watch(menuProvider.notifier).getMealType(mealTypeIndex); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + alignment: Alignment.topLeft, + child: Text( + "$weekDay's $mealType", + style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), + ), + ); + }, + ), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MaterialTextFormField( + controller: ref.read(menuProvider).itemNameController, + onSubmitted: (value) => ref.read(menuProvider.notifier).addMenuItem(), + validator: (value) => Validators.nameValidator(value), + hintText: 'Enter Menu Item', + hintColor: Colors.teal.shade900.withOpacity(0.5), ), - const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: MaterialTextFormField( - controller: ref.read(menuProvider).itemNameController, - onSubmitted: (value) => ref.read(menuProvider.notifier).addMenuItem(), - validator: (value) => Validators.nameValidator(value), - hintText: 'Enter Menu Item', - hintColor: Colors.teal.shade900.withOpacity(0.5), - ), + ), + const SizedBox(height: 30), + Container( + width: 250, + height: 360, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.transparent, width: 0), + color: Colors.white, ), - const SizedBox(height: 30), - Container( - width: 250, - height: 360, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.transparent, width: 0), - color: Colors.white, - ), - child: Column( - children: [ - Container( - padding: const EdgeInsets.only(left: 20, top: 15), - alignment: Alignment.topLeft, - child: const Text( - "Menu Item", - style: TextStyle(fontSize: 18, fontFamily: "GoogleSanaFlex"), - ), + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 20, top: 15), + alignment: Alignment.topLeft, + child: const Text( + "Menu Item", + style: TextStyle(fontSize: 18, fontFamily: "GoogleSanaFlex"), ), - const Divider(height: 20), - Expanded( - child: Consumer( - builder: (_, ref, ___) { - final menuState = ref.watch(menuProvider); - final weekDay = ref - .watch(menuProvider.notifier) - .getWeekDay(menuState.selectedWeekdayIndex); - final mealType = ref - .watch(menuProvider.notifier) - .getMealType(menuState.selectedMealTypeIndex); - final currentMenu = ref.watch(menuProvider).currentMenu; - return ListView.builder( - itemCount: currentMenu.messMenu![weekDay]![mealType]!.length, - itemBuilder: (context, index) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - alignment: Alignment.centerLeft, - child: RoundedChip( - label: currentMenu.messMenu![weekDay]![mealType]![index], - color: Colors.tealAccent.shade100, - onDeleted: () => - ref.watch(menuProvider.notifier).removeMenuItem(index), - ), - ); - }, - ); - }, - ), + ), + const Divider(height: 20), + Expanded( + child: Consumer( + builder: (_, ref, ___) { + final menuState = ref.watch(menuProvider); + final weekDay = ref + .watch(menuProvider.notifier) + .getWeekDay(menuState.selectedWeekdayIndex); + final mealType = ref + .watch(menuProvider.notifier) + .getMealType(menuState.selectedMealTypeIndex); + final currentMenu = ref.watch(menuProvider).currentMenu; + return ListView.builder( + itemCount: currentMenu.messMenu![weekDay]![mealType]!.length, + itemBuilder: (context, index) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + alignment: Alignment.centerLeft, + child: RoundedChip( + label: currentMenu.messMenu![weekDay]![mealType]![index], + color: Colors.tealAccent.shade100, + onDeleted: () => + ref.watch(menuProvider.notifier).removeMenuItem(index), + ), + ); + }, + ); + }, ), - ], - ), + ), + ], ), - ], - ), + ), + ], ), - ], - ), - ); - }, - ), - const SizedBox(height: 30), - Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - child: Align( - alignment: Alignment.centerRight, - child: ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - ref.read(menuProvider.notifier).addMenu(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Menu added successfully"), - ), - ); - } - }, - style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), - child: const Text("Add Menu"), + ), + ], ), + ); + }, + ), + const SizedBox(height: 30), + Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + child: Align( + alignment: Alignment.centerRight, + child: ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + ref.read(menuProvider.notifier).addMenu(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Menu added successfully"), + ), + ); + } + }, + style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), + child: const Text("Add Menu"), ), ), - const SizedBox(height: 30), - ], - ), + ), + const SizedBox(height: 30), + ], ), ), ), From 96d36a6ac99b5d6627e3027fb2de0bdb442bb0d8 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:11:36 +0530 Subject: [PATCH 30/41] Add mess menu loading and CRUD methods --- frontend/lib/provider/menu_provider.dart | 138 ++++++++++++++++++----- 1 file changed, 107 insertions(+), 31 deletions(-) diff --git a/frontend/lib/provider/menu_provider.dart b/frontend/lib/provider/menu_provider.dart index 1779f32..51be69d 100644 --- a/frontend/lib/provider/menu_provider.dart +++ b/frontend/lib/provider/menu_provider.dart @@ -5,10 +5,11 @@ import 'package:logger/logger.dart'; import 'package:smart_insti_app/components/rounded_chip.dart'; import 'package:smart_insti_app/constants/dummy_entries.dart'; import 'package:smart_insti_app/models/mess_menu.dart'; +import 'package:smart_insti_app/repositories/mess_menu_repository.dart'; import '../constants/constants.dart'; import 'dart:io'; -final menuProvider = StateNotifierProvider((ref) => MenuStateNotifier()); +final menuProvider = StateNotifierProvider((ref) => MenuStateNotifier(ref)); class MenuState { final TextEditingController kitchenNameController; @@ -19,6 +20,7 @@ class MenuState { final MessMenu currentMenu; final int selectedWeekdayIndex; final int selectedMealTypeIndex; + final LoadingState loadingState; MenuState({ required this.kitchenNameController, @@ -29,6 +31,7 @@ class MenuState { required this.currentMenu, required this.selectedWeekdayIndex, required this.selectedMealTypeIndex, + required this.loadingState, }); MenuState copyWith({ @@ -40,6 +43,7 @@ class MenuState { MessMenu? currentMenu, int? selectedWeekdayIndex, int? selectedMealTypeIndex, + LoadingState? loadingState, }) { return MenuState( kitchenNameController: kitchenNameController ?? this.kitchenNameController, @@ -50,19 +54,22 @@ class MenuState { currentMenu: currentMenu ?? this.currentMenu, selectedWeekdayIndex: selectedWeekdayIndex ?? this.selectedWeekdayIndex, selectedMealTypeIndex: selectedMealTypeIndex ?? this.selectedMealTypeIndex, + loadingState: loadingState ?? this.loadingState, ); } } class MenuStateNotifier extends StateNotifier { - MenuStateNotifier() - : super(MenuState( + MenuStateNotifier(Ref ref) + : _messMenuRepository = ref.read(messMenuRepositoryProvider), + super(MenuState( kitchenNameController: TextEditingController(), itemNameController: TextEditingController(), selectedViewMenu: DummyMenus.messMenus.keys.isNotEmpty ? DummyMenus.messMenus.keys.first : null, items: [], messMenus: DummyMenus.messMenus, currentMenu: MessMenu( + kitchenName: "", messMenu: >>{ 'Sunday': >{ 'Breakfast': [], @@ -110,9 +117,13 @@ class MenuStateNotifier extends StateNotifier { ), selectedWeekdayIndex: 0, selectedMealTypeIndex: 0, - )); + loadingState: LoadingState.idle, + )) { + loadMenus(); + } final Logger _logger = Logger(); + final MessMenuRepository _messMenuRepository; String getWeekDay(int index) => MessMenuConstants.weekdaysShortToLong[MessMenuConstants.weekdays[index].data]!; @@ -138,18 +149,10 @@ class MenuStateNotifier extends StateNotifier { } } - void initMenuView() { - final selectedViewMenu = DummyMenus.messMenus.keys.isNotEmpty ? DummyMenus.messMenus.keys.first : null; - - state = state.copyWith( - selectedMealTypeIndex: 0, - selectedWeekdayIndex: 0, - selectedViewMenu: selectedViewMenu, - ); - } - void setSelectViewMenu(String? value) { - state = state.copyWith(selectedViewMenu: value); + if (value != null) { + state = state.copyWith(selectedViewMenu: value, currentMenu: state.messMenus[value!]!); + } } void selectWeekday(int index) { @@ -160,10 +163,12 @@ class MenuStateNotifier extends StateNotifier { state = state.copyWith(selectedMealTypeIndex: index); } - void clear() { + void clearControllers() { + state.kitchenNameController.clear(); state = state.copyWith( items: [], currentMenu: MessMenu( + kitchenName: state.kitchenNameController.text, messMenu: >>{ 'Sunday': >{ 'Breakfast': [], @@ -212,31 +217,102 @@ class MenuStateNotifier extends StateNotifier { ); } - void addMenu() { - String kitchenName = state.kitchenNameController.text; - if (kitchenName.isEmpty) { - _logger.e("Kitchen name is empty"); - return; - } - _logger.i("Adding $kitchenName to messMenus"); + void loadMenus() async { + state = state.copyWith(loadingState: LoadingState.progress); + final messMenus = await _messMenuRepository.getMessMenu(); - Map menuList = state.messMenus; - menuList[kitchenName] = state.currentMenu; - - state.kitchenNameController.clear(); + Map menuDictionary = {}; + for (var menu in messMenus) { + menuDictionary[menu.kitchenName] = menu; + } + final selectedViewMenu = menuDictionary.keys.isNotEmpty ? menuDictionary.keys.first : null; state = state.copyWith( - currentMenu: MessMenu(messMenu: {...MessMenuConstants.emptyMenu}), - messMenus: menuList, - items: [], + selectedMealTypeIndex: 0, + selectedWeekdayIndex: 0, + selectedViewMenu: selectedViewMenu, + currentMenu: selectedViewMenu != null + ? menuDictionary[selectedViewMenu] + : MessMenu(kitchenName: '', messMenu: >>{ + 'Sunday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + 'Monday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + 'Tuesday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + 'Wednesday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + 'Thursday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + 'Friday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + 'Saturday': >{ + 'Breakfast': [], + 'Lunch': [], + 'Snacks': [], + 'Dinner': [], + }, + }), ); + state = state.copyWith(messMenus: menuDictionary, loadingState: LoadingState.success); + } + + void resetMenu() { + if (state.selectedViewMenu != null) { + state = state.copyWith(currentMenu: state.messMenus[state.selectedViewMenu!]!); + } + } + + Future addMenu() async { + String kitchenName = state.kitchenNameController.text; + if (await _messMenuRepository + .addMessMenu(MessMenu(kitchenName: kitchenName, messMenu: state.currentMenu.messMenu!))) { + loadMenus(); + } + } + + void updateMenu() async { + String kitchenName = state.currentMenu.kitchenName; + if (await _messMenuRepository.updateMessMenu(state.messMenus[state.selectedViewMenu!]!.id!, + MessMenu(kitchenName: kitchenName, messMenu: state.currentMenu.messMenu!))) { + loadMenus(); + } + } + + void deleteMenu() async { + if (await _messMenuRepository.deleteMessMenu(state.messMenus[state.selectedViewMenu!]!.id!)) { + loadMenus(); + } } void addMenuItem() { String item = state.itemNameController.text; String weekday = getWeekDay(state.selectedWeekdayIndex); String mealType = getMealType(state.selectedMealTypeIndex); - _logger.i("Adding $item to $weekday $getMealType"); MessMenu menu = state.currentMenu; menu.messMenu![weekday]![mealType]!.add(item); From 0bfb5a9513e1fe40446c3f76c93c31ede10d1012 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:12:16 +0530 Subject: [PATCH 31/41] Add messMenuListResource.js with GET and POST methods --- .../messMenu/messMenuListResource.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 backend/resources/messMenu/messMenuListResource.js diff --git a/backend/resources/messMenu/messMenuListResource.js b/backend/resources/messMenu/messMenuListResource.js new file mode 100644 index 0000000..08473dc --- /dev/null +++ b/backend/resources/messMenu/messMenuListResource.js @@ -0,0 +1,29 @@ +import express from "express"; +import * as messages from "../../constants/messages.js"; +import MessMenu from "../../models/mess_menu.js"; + +const messMenuListRouter = express.Router(); + +// GET method +messMenuListRouter.get("/", async (req, res) => { + try { + const messMenuData = await MessMenu.find(); + res.json(messMenuData); + } catch (error) { + res.status(500).json({ message: messages.internalServerError }); + } +}); + +// POST method +messMenuListRouter.post("/", async (req, res) => { + try { + const { kitchenName, messMenu } = req.body; + const newMessMenu = new MessMenu({ kitchenName, messMenu }); + await newMessMenu.save(); + res.json({ message: messages.messMenuAdded, newMessMenu }); + } catch (error) { + res.status(500).json({ message: messages.internalServerError }); + } +}); + +export default messMenuListRouter; From 22d9bfedb9db1363fbf777e9a184b9b3e8cf1585 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:12:23 +0530 Subject: [PATCH 32/41] Add messMenuResource.js for PUT and DELETE methods --- .../resources/messMenu/messMenuResource.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 backend/resources/messMenu/messMenuResource.js diff --git a/backend/resources/messMenu/messMenuResource.js b/backend/resources/messMenu/messMenuResource.js new file mode 100644 index 0000000..aab3e71 --- /dev/null +++ b/backend/resources/messMenu/messMenuResource.js @@ -0,0 +1,39 @@ +import express from "express"; +import * as messages from "../../constants/messages.js"; +import MessMenu from "../../models/mess_menu.js"; + +const messMenuRouter = express.Router(); + +// PUT method +messMenuRouter.put("/:id", async (req, res) => { + try { + const { id } = req.params; + const { messMenu } = req.body; + const currentMenu = await MessMenu.findById(id); + + if (!currentMenu) { + return res.status(404).json({ message: messages.messMenuNotFound }); + } + + currentMenu.messMenu = messMenu; + await currentMenu.save(); + + res.json({ message: messages.messMenuUpdated }); + } catch (error) { + res.status(500).json({ message: messages.internalServerError }); + } +}); + +//DELETE method +messMenuRouter.delete("/:id", async (req, res) => { + try { + const { id } = req.params; + await MessMenu.findByIdAndDelete(id); + + res.json({ message: messages.deleted }); + } catch (error) { + res.status(500).json({ message: messages.internalServerError }); + } +}); + +export default messMenuRouter; From 374c3c0088b0d915732b87a1ad2a87be75394322 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 15:12:43 +0530 Subject: [PATCH 33/41] Add mess menu resources --- backend/app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/app.js b/backend/app.js index 64cd569..f09d824 100644 --- a/backend/app.js +++ b/backend/app.js @@ -20,6 +20,8 @@ import messageResource from "./resources/chatroom/messageListResource.js"; import lostAndFoundResource from "./resources/lostAndFound/lostAndFoundResource.js"; import timetableListResource from "./resources/timetable/timetableListResource.js"; import timetableResource from "./resources/timetable/timetableResource.js"; +import messMenuListResource from "./resources/messMenu/messMenuListResource.js"; +import messMenuResource from "./resources/messMenu/messMenuResource.js"; const PORT = `${process.env.PORT || 3000}`; const app = express(); @@ -39,6 +41,8 @@ app.use("/faculty", facultyResource); app.use("/faculties", facultyListResource); app.use("/timetable", timetableResource); app.use("/timetables", timetableListResource); +app.use("/mess-menu", messMenuResource); +app.use("/mess-menus", messMenuListResource); app.use("/admin-auth", adminAuthResource); app.use("/general-auth", generalAuthResource); From cb9adae267edbca28ec8c0fad8626986546470dc Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sat, 17 Feb 2024 21:42:28 +0530 Subject: [PATCH 34/41] Created a mess menu page for the user side --- frontend/lib/screens/user/user_mess_menu.dart | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 frontend/lib/screens/user/user_mess_menu.dart diff --git a/frontend/lib/screens/user/user_mess_menu.dart b/frontend/lib/screens/user/user_mess_menu.dart new file mode 100644 index 0000000..1ebf6c0 --- /dev/null +++ b/frontend/lib/screens/user/user_mess_menu.dart @@ -0,0 +1,190 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:responsive_framework/responsive_framework.dart'; +import 'package:slide_switcher/slide_switcher.dart'; +import 'package:smart_insti_app/components/choice_selector.dart'; +import 'package:smart_insti_app/models/mess_menu.dart'; +import 'package:smart_insti_app/provider/menu_provider.dart'; +import '../../constants/constants.dart'; +import '../../provider/auth_provider.dart'; + +class UserMessMenu extends ConsumerWidget { + const UserMessMenu({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (ref.read(authProvider.notifier).tokenCheckProgress != LoadingState.progress && context.mounted) { + ref.read(authProvider.notifier).verifyAuthTokenExistence(context, AuthConstants.generalAuthLabel.toLowerCase()); + } + }); + + return ResponsiveScaledBox( + width: 411, + child: Scaffold( + appBar: AppBar( + title: const Text('Mess Menu'), + ), + body: SingleChildScrollView( + child: Consumer( + builder: (_, ref, __) { + if (ref.watch(menuProvider).loadingState == LoadingState.progress) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return Column( + children: [ + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: ChoiceSelector( + onChanged: (value) { + ref.read(menuProvider.notifier).setSelectViewMenu(value); + }, + value: ref.read(menuProvider).selectedViewMenu, + items: [ + for (String i in ref.read(menuProvider).messMenus.keys) + DropdownMenuItem( + value: i, + child: Text(i), + ), + ], + hint: 'Select Kitchen', + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 30), + child: SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), + containerHeight: 65, + containerWight: 380, + containerBorderRadius: 20, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.mealTypes, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + height: 550, + width: 285, + margin: const EdgeInsets.only(right: 15), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.transparent, width: 0), + color: Colors.grey[200], + ), + child: Column( + children: [ + Consumer( + builder: (_, ref, __) { + final menuState = ref.watch(menuProvider); + final weekDay = + ref.watch(menuProvider.notifier).getWeekDay(menuState.selectedWeekdayIndex); + final mealType = + ref.watch(menuProvider.notifier).getMealType(menuState.selectedMealTypeIndex); + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + alignment: Alignment.topLeft, + child: Text( + "$weekDay's $mealType", + style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), + ), + ), + ], + ); + }, + ), + Consumer( + builder: (context, ref, child) { + final menuState = ref.watch(menuProvider); + final weekDay = + ref.watch(menuProvider.notifier).getWeekDay(menuState.selectedWeekdayIndex); + final mealType = + ref.watch(menuProvider.notifier).getMealType(menuState.selectedMealTypeIndex); + if (menuState.selectedViewMenu != null) { + MessMenu? selectedMenu = menuState.messMenus[menuState.selectedViewMenu]; + int length = selectedMenu?.messMenu?[weekDay]?[mealType]?.length ?? 0; + List controllers = List.generate(length, (index) => TextEditingController()); + return ListView.builder( + shrinkWrap: true, + itemCount: length, + itemBuilder: (context, index) { + controllers[index].text = + selectedMenu?.messMenu?[weekDay]?[mealType]?[index] ?? ''; + return length != 0 + ? Container( + margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.tealAccent.withOpacity(0.4), + ), + width: double.infinity, + child: AutoSizeText( + controllers[index].text, + style: TextStyle(fontSize: 15, color: Colors.teal.shade900), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ) + : const Expanded( + child: Center( + child: Text( + 'No items today', + style: TextStyle(fontSize: 20, color: Colors.black38), + ), + ), + ); + }, + ); + } else { + return const Expanded( + child: Center( + child: Text( + 'No menu selected', + style: TextStyle(fontSize: 20, color: Colors.black38), + ), + ), + ); + } + }, + ), + ], + ), + ), + Align( + alignment: Alignment.centerRight, + child: SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), + containerHeight: 550, + containerWight: 70, + containerBorderRadius: 20, + direction: Axis.vertical, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.weekdays, + ), + ), + ], + ), + ), + const SizedBox(height: 30), + ], + ); + } + }, + ), + ), + ), + ); + } +} From 64c152c29e1df4feb6f05cb54c88ea99d5be9851 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 00:04:21 +0530 Subject: [PATCH 35/41] Added buttons to for update, delete and refresh --- frontend/lib/screens/admin/view_menu.dart | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/frontend/lib/screens/admin/view_menu.dart b/frontend/lib/screens/admin/view_menu.dart index 7ec4fbc..5de9662 100644 --- a/frontend/lib/screens/admin/view_menu.dart +++ b/frontend/lib/screens/admin/view_menu.dart @@ -10,9 +10,7 @@ import '../../constants/constants.dart'; import '../../provider/auth_provider.dart'; class ViewMessMenu extends ConsumerWidget { - ViewMessMenu({super.key}); - - final ScrollController _scrollController = ScrollController(); + const ViewMessMenu({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,7 +27,6 @@ class ViewMessMenu extends ConsumerWidget { title: const Text('Mess Menu'), ), body: SingleChildScrollView( - controller: _scrollController, child: Consumer( builder: (_, ref, __) { if (ref.watch(menuProvider).loadingState == LoadingState.progress) { @@ -45,12 +42,8 @@ class ViewMessMenu extends ConsumerWidget { child: ChoiceSelector( onChanged: (value) { ref.read(menuProvider.notifier).setSelectViewMenu(value); - _scrollController.animateTo(_scrollController.position.maxScrollExtent + 30, - duration: const Duration(milliseconds: 500), curve: Curves.easeIn); }, - value: ref.read(menuProvider).messMenus.keys.isNotEmpty - ? ref.read(menuProvider).messMenus.keys.first - : null, + value: ref.read(menuProvider).selectedViewMenu, items: [ for (String i in ref.read(menuProvider).messMenus.keys) DropdownMenuItem( @@ -136,6 +129,8 @@ class ViewMessMenu extends ConsumerWidget { border: Border.all(color: Colors.transparent, width: 0), ), child: MaterialTextFormField( + enabled: ref.watch(authProvider).currentUserRole == + AuthConstants.adminAuthLabel.toLowerCase(), contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), controller: controllers[index], @@ -190,6 +185,7 @@ class ViewMessMenu extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 20), child: Row( children: [ + const SizedBox(height: 30), GestureDetector( onTap: () => ref.read(menuProvider.notifier).deleteMenu(), child: Container( @@ -221,7 +217,7 @@ class ViewMessMenu extends ConsumerWidget { ), ), ), - Spacer(), + const Spacer(), ElevatedButton( onPressed: () => ref.read(menuProvider.notifier).updateMenu(), style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), From c84b6e890ea5cee9e882d89bf98a497fdd015126 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 00:05:45 +0530 Subject: [PATCH 36/41] Add UserMessMenu screen to routes --- frontend/lib/routes/routes.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/lib/routes/routes.dart b/frontend/lib/routes/routes.dart index 33a0caa..05a25af 100644 --- a/frontend/lib/routes/routes.dart +++ b/frontend/lib/routes/routes.dart @@ -8,6 +8,7 @@ import 'package:smart_insti_app/screens/admin/view_students.dart'; import 'package:smart_insti_app/screens/auth/login_general.dart'; import 'package:smart_insti_app/screens/loading_page.dart'; import 'package:smart_insti_app/screens/user/lost_and_found.dart'; +import 'package:smart_insti_app/screens/user/user_mess_menu.dart'; import 'package:smart_insti_app/screens/user/timetable_editor.dart'; import '../screens/admin/add_faculty.dart'; import '../screens/admin/add_menu.dart'; @@ -125,6 +126,7 @@ final GoRouter routes = GoRouter( path: 'chat_room', pageBuilder: (context, state) => MaterialPage(child: ChatRoom()), ), + GoRoute(path: 'mess_menu', pageBuilder: (context, state) => MaterialPage(child: UserMessMenu()) ), ], ), ], From f7147ed4fe8c3b65114c8a046e00a58504e69bcc Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 00:05:54 +0530 Subject: [PATCH 37/41] Update mess menu file paths --- frontend/lib/generated/assets.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/lib/generated/assets.dart b/frontend/lib/generated/assets.dart index cc85ea0..56dee35 100644 --- a/frontend/lib/generated/assets.dart +++ b/frontend/lib/generated/assets.dart @@ -58,7 +58,7 @@ class Assets { static const String modelsCourse = 'lib/models/course.dart'; static const String modelsFaculty = 'lib/models/faculty.dart'; static const String modelsLostAndFoundItem = 'lib/models/lost_and_found_item.dart'; - static const String modelsMessMenu = 'lib/models/mess_menu.dart'; + static const String modelsMessMenu = 'lib/models/user_mess_menu.dart'; static const String modelsMessage = 'lib/models/message.dart'; static const String modelsRoom = 'lib/models/room.dart'; static const String modelsSkills = 'lib/models/skills.dart'; @@ -100,6 +100,7 @@ class Assets { static const String userEditProfile = 'lib/screens/user/edit_profile.dart'; static const String userFacultyProfile = 'lib/screens/user/faculty_profile.dart'; static const String userLostAndFound = 'lib/screens/user/lost_and_found.dart'; + static const String userMessMenu = 'lib/screens/user/user_mess_menu.dart'; static const String userRoomVacancy = 'lib/screens/user/room_vacancy.dart'; static const String userStudentProfile = 'lib/screens/user/student_profile.dart'; static const String userTimetableEditor = 'lib/screens/user/timetable_editor.dart'; From a90dba1bd4d570abaf9f8c1c9575fb5b8d9528b4 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 00:16:01 +0530 Subject: [PATCH 38/41] Mess menu tile in home --- frontend/lib/provider/home_provider.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/lib/provider/home_provider.dart b/frontend/lib/provider/home_provider.dart index cce4962..b9bd2ab 100644 --- a/frontend/lib/provider/home_provider.dart +++ b/frontend/lib/provider/home_provider.dart @@ -99,6 +99,13 @@ class UserProvider extends StateNotifier { secondaryColor: Colors.redAccent.shade200, icon: Icons.search, ), + MenuTile( + title: "Mess\nMenu", + onTap: () => context.push('/user_home/mess_menu'), + primaryColor: Colors.purpleAccent.shade100, + secondaryColor: Colors.purpleAccent.shade200, + icon: Icons.restaurant, + ), ]; String query = state.searchController.text; state = state.copyWith( From 5cc6c85b0c1223ca76af93354c8f8cf5ce0f29c6 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 00:21:18 +0530 Subject: [PATCH 39/41] Remove unused imports and clear kitchen name controller --- frontend/lib/provider/menu_provider.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/lib/provider/menu_provider.dart b/frontend/lib/provider/menu_provider.dart index 51be69d..7c71983 100644 --- a/frontend/lib/provider/menu_provider.dart +++ b/frontend/lib/provider/menu_provider.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logger/logger.dart'; import 'package:smart_insti_app/components/rounded_chip.dart'; -import 'package:smart_insti_app/constants/dummy_entries.dart'; import 'package:smart_insti_app/models/mess_menu.dart'; import 'package:smart_insti_app/repositories/mess_menu_repository.dart'; import '../constants/constants.dart'; @@ -65,9 +64,9 @@ class MenuStateNotifier extends StateNotifier { super(MenuState( kitchenNameController: TextEditingController(), itemNameController: TextEditingController(), - selectedViewMenu: DummyMenus.messMenus.keys.isNotEmpty ? DummyMenus.messMenus.keys.first : null, + selectedViewMenu: null, items: [], - messMenus: DummyMenus.messMenus, + messMenus: {}, currentMenu: MessMenu( kitchenName: "", messMenu: >>{ @@ -225,7 +224,8 @@ class MenuStateNotifier extends StateNotifier { for (var menu in messMenus) { menuDictionary[menu.kitchenName] = menu; } - final selectedViewMenu = menuDictionary.keys.isNotEmpty ? menuDictionary.keys.first : null; + const selectedViewMenu = null; + state.kitchenNameController.clear(); state = state.copyWith( selectedMealTypeIndex: 0, From bce1c9cc06f867b5e452f23e1a9ab88d974a6745 Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 00:36:21 +0530 Subject: [PATCH 40/41] Refactor code to improve performance and readability --- frontend/lib/screens/admin/add_menu.dart | 381 ++++++++++++----------- 1 file changed, 195 insertions(+), 186 deletions(-) diff --git a/frontend/lib/screens/admin/add_menu.dart b/frontend/lib/screens/admin/add_menu.dart index ef40f10..5333dd7 100644 --- a/frontend/lib/screens/admin/add_menu.dart +++ b/frontend/lib/screens/admin/add_menu.dart @@ -17,7 +17,6 @@ class AddMessMenu extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - WidgetsBinding.instance.addPostFrameCallback((_) { ref.watch(menuProvider.notifier).clearControllers(); if (ref.read(authProvider.notifier).tokenCheckProgress != LoadingState.progress) { @@ -33,179 +32,189 @@ class AddMessMenu extends ConsumerWidget { ), body: SingleChildScrollView( controller: _scrollController, - child: Column( - children: [ - Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 30), - child: const Text( - "Spreadsheet Entry", - style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), - ), - ), - const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.only(left: 30), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, + child: Consumer( + builder: (_, ref, __) { + if (ref.watch(menuProvider).loadingState == LoadingState.progress) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return Column( children: [ - const Text( - "Upload file here", - style: TextStyle(fontSize: 16), + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 30), + child: const Text( + "Spreadsheet Entry", + style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), + ), ), - const SizedBox(width: 30), - ElevatedButton( - onPressed: () => ref.read(menuProvider.notifier).pickSpreadsheet(), - style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), - child: const Text("Upload Spreadsheet"), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.only(left: 30), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Text( + "Upload file here", + style: TextStyle(fontSize: 16), + ), + const SizedBox(width: 30), + ElevatedButton( + onPressed: () => ref.read(menuProvider.notifier).pickSpreadsheet(), + style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), + child: const Text("Upload Spreadsheet"), + ), + ], + ), ), - ], - ), - ), - const SizedBox(height: 30), - const TextDivider(text: "OR"), - const SizedBox(height: 30), - Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 30), - child: const Text( - "Single Entry", - style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), - ), - ), - const SizedBox(height: 30), - Container( - margin: const EdgeInsets.symmetric(horizontal: 15), - child: Form( - key: _formKey, - child: MaterialTextFormField( - controller: ref.read(menuProvider).kitchenNameController, - validator: (value) => Validators.nameValidator(value), - hintText: "Enter kitchen name", - onSubmitted: (value) => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 500), - curve: Curves.ease, + const SizedBox(height: 30), + const TextDivider(text: "OR"), + const SizedBox(height: 30), + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 30), + child: const Text( + "Single Entry", + style: TextStyle(fontSize: 32, fontFamily: "RobotoFlex"), + ), ), - hintColor: Colors.teal.shade900.withOpacity(0.5), - ), - ), - ), - const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: SlideSwitcher( - onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), - containerHeight: 65, - containerWight: 380, - containerBorderRadius: 20, - slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], - containerColor: Colors.tealAccent.shade100, - children: MessMenuConstants.mealTypes, - ), - ), - const SizedBox(height: 30), - Consumer( - builder: (_, ref, ___) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SlideSwitcher( - onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), - containerHeight: 550, - containerWight: 70, - containerBorderRadius: 20, - direction: Axis.vertical, - slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], - containerColor: Colors.tealAccent.shade100, - children: MessMenuConstants.weekdays), - Container( - height: 550, - width: 285, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.transparent, width: 0), - color: Colors.grey[200], + const SizedBox(height: 30), + Container( + margin: const EdgeInsets.symmetric(horizontal: 15), + child: Form( + key: _formKey, + child: MaterialTextFormField( + controller: ref.read(menuProvider).kitchenNameController, + validator: (value) => Validators.nameValidator(value), + hintText: "Enter kitchen name", + onSubmitted: (value) => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 500), + curve: Curves.ease, ), - child: Column( + hintColor: Colors.teal.shade900.withOpacity(0.5), + ), + ), + ), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectMealType(index), + containerHeight: 65, + containerWight: 380, + containerBorderRadius: 20, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.mealTypes, + ), + ), + const SizedBox(height: 30), + Consumer( + builder: (_, ref, ___) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Consumer( - builder: (_, ref, __) { - final weekDayIndex = ref.watch(menuProvider).selectedWeekdayIndex; - final mealTypeIndex = ref.watch(menuProvider).selectedMealTypeIndex; - - final weekDay = ref.watch(menuProvider.notifier).getWeekDay(weekDayIndex); - final mealType = ref.watch(menuProvider.notifier).getMealType(mealTypeIndex); - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - alignment: Alignment.topLeft, - child: Text( - "$weekDay's $mealType", - style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), - ), - ); - }, - ), - const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: MaterialTextFormField( - controller: ref.read(menuProvider).itemNameController, - onSubmitted: (value) => ref.read(menuProvider.notifier).addMenuItem(), - validator: (value) => Validators.nameValidator(value), - hintText: 'Enter Menu Item', - hintColor: Colors.teal.shade900.withOpacity(0.5), - ), - ), - const SizedBox(height: 30), + SlideSwitcher( + onSelect: (index) => ref.read(menuProvider.notifier).selectWeekday(index), + containerHeight: 550, + containerWight: 70, + containerBorderRadius: 20, + direction: Axis.vertical, + slidersColors: [Colors.tealAccent.shade700.withOpacity(0.7)], + containerColor: Colors.tealAccent.shade100, + children: MessMenuConstants.weekdays), Container( - width: 250, - height: 360, + height: 550, + width: 285, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.transparent, width: 0), - color: Colors.white, + color: Colors.grey[200], ), child: Column( children: [ - Container( - padding: const EdgeInsets.only(left: 20, top: 15), - alignment: Alignment.topLeft, - child: const Text( - "Menu Item", - style: TextStyle(fontSize: 18, fontFamily: "GoogleSanaFlex"), + Consumer( + builder: (_, ref, __) { + final weekDayIndex = ref.watch(menuProvider).selectedWeekdayIndex; + final mealTypeIndex = ref.watch(menuProvider).selectedMealTypeIndex; + + final weekDay = ref.watch(menuProvider.notifier).getWeekDay(weekDayIndex); + final mealType = ref.watch(menuProvider.notifier).getMealType(mealTypeIndex); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + alignment: Alignment.topLeft, + child: Text( + "$weekDay's $mealType", + style: const TextStyle(fontSize: 23, fontFamily: "GoogleSanaFlex"), + ), + ); + }, + ), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: MaterialTextFormField( + controller: ref.read(menuProvider).itemNameController, + onSubmitted: (value) => ref.read(menuProvider.notifier).addMenuItem(), + validator: (value) => Validators.nameValidator(value), + hintText: 'Enter Menu Item', + hintColor: Colors.teal.shade900.withOpacity(0.5), ), ), - const Divider(height: 20), - Expanded( - child: Consumer( - builder: (_, ref, ___) { - final menuState = ref.watch(menuProvider); - final weekDay = ref - .watch(menuProvider.notifier) - .getWeekDay(menuState.selectedWeekdayIndex); - final mealType = ref - .watch(menuProvider.notifier) - .getMealType(menuState.selectedMealTypeIndex); - final currentMenu = ref.watch(menuProvider).currentMenu; - return ListView.builder( - itemCount: currentMenu.messMenu![weekDay]![mealType]!.length, - itemBuilder: (context, index) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - alignment: Alignment.centerLeft, - child: RoundedChip( - label: currentMenu.messMenu![weekDay]![mealType]![index], - color: Colors.tealAccent.shade100, - onDeleted: () => - ref.watch(menuProvider.notifier).removeMenuItem(index), - ), - ); - }, - ); - }, + const SizedBox(height: 30), + Container( + width: 250, + height: 360, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.transparent, width: 0), + color: Colors.white, + ), + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 20, top: 15), + alignment: Alignment.topLeft, + child: const Text( + "Menu Item", + style: TextStyle(fontSize: 18, fontFamily: "GoogleSanaFlex"), + ), + ), + const Divider(height: 20), + Expanded( + child: Consumer( + builder: (_, ref, ___) { + final menuState = ref.watch(menuProvider); + final weekDay = ref + .watch(menuProvider.notifier) + .getWeekDay(menuState.selectedWeekdayIndex); + final mealType = ref + .watch(menuProvider.notifier) + .getMealType(menuState.selectedMealTypeIndex); + final currentMenu = ref.watch(menuProvider).currentMenu; + return ListView.builder( + itemCount: currentMenu.messMenu![weekDay]![mealType]!.length, + itemBuilder: (context, index) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + alignment: Alignment.centerLeft, + child: RoundedChip( + label: currentMenu.messMenu![weekDay]![mealType]![index], + color: Colors.tealAccent.shade100, + onDeleted: () => + ref.watch(menuProvider.notifier).removeMenuItem(index), + ), + ); + }, + ); + }, + ), + ), + ], ), ), ], @@ -213,35 +222,35 @@ class AddMessMenu extends ConsumerWidget { ), ], ), + ); + }, + ), + const SizedBox(height: 30), + Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + child: Align( + alignment: Alignment.centerRight, + child: ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + ref.read(menuProvider.notifier).addMenu(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Menu added successfully"), + ), + ); + } + }, + style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), + child: const Text("Add Menu"), ), - ], + ), ), - ); - }, - ), - const SizedBox(height: 30), - Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - child: Align( - alignment: Alignment.centerRight, - child: ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - ref.read(menuProvider.notifier).addMenu(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Menu added successfully"), - ), - ); - } - }, - style: ButtonStyle(minimumSize: MaterialStateProperty.all(const Size(200, 60))), - child: const Text("Add Menu"), - ), - ), - ), - const SizedBox(height: 30), - ], + const SizedBox(height: 30), + ], + ); + } + }, ), ), ), From ecd13552fa0dcf79c21ab3f340a3b6ce4782e8de Mon Sep 17 00:00:00 2001 From: nailsonseat Date: Sun, 18 Feb 2024 01:10:07 +0530 Subject: [PATCH 41/41] Fixed allowing empty menu item --- frontend/lib/provider/menu_provider.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/lib/provider/menu_provider.dart b/frontend/lib/provider/menu_provider.dart index 7c71983..c39a68a 100644 --- a/frontend/lib/provider/menu_provider.dart +++ b/frontend/lib/provider/menu_provider.dart @@ -311,6 +311,9 @@ class MenuStateNotifier extends StateNotifier { void addMenuItem() { String item = state.itemNameController.text; + if (item == '') { + return; + } String weekday = getWeekDay(state.selectedWeekdayIndex); String mealType = getMealType(state.selectedMealTypeIndex);