From 7c927d694da5111fc9e6ef8888701e07a8f6eefc Mon Sep 17 00:00:00 2001 From: Harsh Verma <87369868+hv2308@users.noreply.github.com> Date: Wed, 15 Jan 2025 04:04:02 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=BD=E2=80=8D=E2=99=80=EF=B8=8F=20Bug/a?= =?UTF-8?q?dmin=20login=20(#1160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added sort order key * fix lint issue * Add DND Feature --- backend/api/models/Training.py | 4 +- backend/api/views/training.py | 65 ++++++++ frontend/package.json | 3 + frontend/src/components/TrainingList.js | 8 +- .../src/components/pages/AdminTraining.js | 145 +++++++++++++++++- frontend/src/utils/api.js | 24 ++- 6 files changed, 238 insertions(+), 11 deletions(-) diff --git a/backend/api/models/Training.py b/backend/api/models/Training.py index e9d7c293..9a990de0 100644 --- a/backend/api/models/Training.py +++ b/backend/api/models/Training.py @@ -27,10 +27,12 @@ class Training(Document, Mixin): partner_id = StringField(required=False) mentor_id = ListField(StringField(), required=False) mentee_id = ListField(StringField(), required=False) + sort_order = IntField(required=False, default=0) def __repr__(self): return f"""""" + \n date_submitted: {self.date_submitted} + \n sort_order: {self.sort_order}>""" \ No newline at end of file diff --git a/backend/api/views/training.py b/backend/api/views/training.py index b9246ec2..85910b4b 100644 --- a/backend/api/views/training.py +++ b/backend/api/views/training.py @@ -169,6 +169,71 @@ def get_trainings(role): return create_response(data={"trainings": result}) +@training.route("/update_multiple", methods=["PATCH"]) +def update_multiple_trainings(): + data = request.json.get("trainings", []) + if not data: + return create_response(status=400, message="No trainings provided for update") + + updated_trainings = [] + failed_updates = [] + + for training in data: + training_id = training.get("id") + update_data = training.get("updated_data", {}) + + if not training_id: + failed_updates.append({"error": "Training ID is required"}) + continue + + try: + training_id = ObjectId(training_id) + except Exception as e: + failed_updates.append({"error": f"Invalid ID format: {str(e)}"}) + continue + + train = Training.objects(id=training_id).first() + if not train: + failed_updates.append({"error": f"Training with ID {training_id} not found"}) + continue + + train_data = train.to_mongo().to_dict() + + # Exclude `_id` and `sort_order` for comparison + train_data.pop("_id", None) + existing_sort_order = train_data.pop("sort_order", None) + updated_sort_order = update_data.get("sort_order") + + # Compare all fields except `sort_order` + other_fields_match = all( + train_data.get(key) == value + for key, value in update_data.items() + if key != "sort_order" + ) + + if not other_fields_match: + failed_updates.append({ + "error": f"Only sort_order can be updated. Mismatched fields for ID {training_id}" + }) + continue + + # Update sort_order if it's different + if updated_sort_order != existing_sort_order: + train.update(sort_order=updated_sort_order) + updated_trainings.append(train.reload()) + else: + failed_updates.append({ + "error": f"No changes in sort_order for ID {training_id}" + }) + + result = { + "updated_trainings": [json.loads(t.to_json()) for t in updated_trainings], + "failed_updates": failed_updates, + } + + return create_response(data=result) + + @training.route("/", methods=["DELETE"]) @admin_only diff --git a/frontend/package.json b/frontend/package.json index 91376dbc..ff3313ae 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,9 @@ "private": true, "dependencies": { "@ant-design/icons": "^4.8.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", "@emotion/css": "^11.11.2", "@jitsi/react-sdk": "^1.4.0", "@reduxjs/toolkit": "^1.9.3", diff --git a/frontend/src/components/TrainingList.js b/frontend/src/components/TrainingList.js index f0eae06a..dcba22b1 100644 --- a/frontend/src/components/TrainingList.js +++ b/frontend/src/components/TrainingList.js @@ -203,9 +203,13 @@ const TrainingList = (props) => { } else { hub_user_id = user._id.$oid; } - setTrainingData(trains.filter((x) => x.hub_id == hub_user_id)); + setTrainingData( + trains + .sort((a, b) => a.sort_order - b.sort_order) + .filter((x) => x.hub_id == hub_user_id) + ); } else { - setTrainingData(trains); + setTrainingData(trains.sort((a, b) => a.sort_order - b.sort_order)); } setLoading(false); setFlag(!flag); diff --git a/frontend/src/components/pages/AdminTraining.js b/frontend/src/components/pages/AdminTraining.js index c6c4d58b..518dfad4 100644 --- a/frontend/src/components/pages/AdminTraining.js +++ b/frontend/src/components/pages/AdminTraining.js @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useContext, useEffect, useMemo, useState } from "react"; import { deleteTrainbyId, downloadBlob, @@ -9,6 +9,7 @@ import { fetchAccounts, fetchPartners, newTrainCreate, + updateTrainings, } from "utils/api"; import { ACCOUNT_TYPE, I18N_LANGUAGES, TRAINING_TYPE } from "utils/consts"; import { HubsDropdown } from "../AdminDropdowns"; @@ -26,6 +27,7 @@ import { import { DeleteOutlined, EditOutlined, + HolderOutlined, PlusCircleOutlined, TeamOutlined, } from "@ant-design/icons"; @@ -35,6 +37,69 @@ import "components/css/Training.scss"; import AdminDownloadDropdown from "../AdminDownloadDropdown"; import TrainingTranslationModal from "../TrainingTranslationModal"; import UpdateTrainingForm from "../UpdateTrainingModal"; +import { + arrayMove, + SortableContext, + useSortable, + verticalListSortingStrategy, +} from "@dnd-kit/sortable"; +import { DndContext } from "@dnd-kit/core"; +import { CSS } from "@dnd-kit/utilities"; +import { restrictToVerticalAxis } from "@dnd-kit/modifiers"; + +const RowContext = React.createContext({}); +const DragHandle = () => { + const { setActivatorNodeRef, listeners } = useContext(RowContext); + return ( +