diff --git a/package-lock.json b/package-lock.json index 2605f42..d8d904c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,17 @@ "name": "brainspace", "version": "0.1.0", "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@reduxjs/toolkit": "^2.5.0", "@tsparticles/engine": "^3.7.1", "axios": "^1.7.9", "brainspace": "file:", "cookies-next": "^5.0.2", "dayjs": "^1.11.13", + "dragula": "^3.7.3", "file-saver": "^2.0.5", "formik": "^2.4.6", "jspdf": "^2.5.2", @@ -47,6 +52,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@types/dragula": "^3.7.5", "@types/file-saver": "^2.0.7", "@types/node": "^20", "@types/react": "^19", @@ -84,6 +90,59 @@ "node": ">=6.9.0" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -1635,6 +1694,12 @@ "@tsparticles/engine": "3.7.1" } }, + "node_modules/@types/dragula": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@types/dragula/-/dragula-3.7.5.tgz", + "integrity": "sha512-jojr2JVJB8DawAKXApGnollMvVOMyiMKpchH8gLeoExx35Eq0BQ4WgAiAHoBoEn7h/9eDrIl0yz//cM6ALIJbg==", + "dev": true + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2253,6 +2318,11 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/atoa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", + "integrity": "sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==" + }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -2717,6 +2787,15 @@ "dev": true, "license": "MIT" }, + "node_modules/contra": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", + "integrity": "sha512-N9ArHAqwR/lhPq4OdIAwH4e1btn6EIZMAz4TazjnzCiVECcWUPTma+dRAM38ERImEJBh8NiCCpjoQruSZ+agYg==", + "dependencies": { + "atoa": "1.0.0", + "ticky": "1.0.1" + } + }, "node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -2773,6 +2852,14 @@ "node": ">= 8" } }, + "node_modules/crossvent": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.5.tgz", + "integrity": "sha512-MY4xhBYEnVi+pmTpHCOCsCLYczc0PVtGdPBz6NXNXxikLaUZo4HdAeUb1UqAo3t3yXAloSelTmfxJ+/oUqkW5w==", + "dependencies": { + "custom-event": "^1.0.0" + } + }, "node_modules/css-line-break": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", @@ -2802,6 +2889,11 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==" + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -3008,6 +3100,15 @@ "license": "(MPL-2.0 OR Apache-2.0)", "optional": true }, + "node_modules/dragula": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.3.tgz", + "integrity": "sha512-/rRg4zRhcpf81TyDhaHLtXt6sEywdfpv1cRUMeFFy7DuypH2U0WUL0GTdyAQvXegviT4PJK4KuMmOaIDpICseQ==", + "dependencies": { + "contra": "1.9.4", + "crossvent": "1.5.5" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -7156,6 +7257,11 @@ "node": ">=0.8" } }, + "node_modules/ticky": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", + "integrity": "sha512-RX35iq/D+lrsqhcPWIazM9ELkjOe30MSeoBHQHSsRwd1YuhJO5ui1K1/R0r7N3mFvbLBs33idw+eR6j+w6i/DA==" + }, "node_modules/tiny-case": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", diff --git a/package.json b/package.json index 0207951..8b9c9d7 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,17 @@ "lint": "next lint" }, "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@reduxjs/toolkit": "^2.5.0", "@tsparticles/engine": "^3.7.1", "axios": "^1.7.9", "brainspace": "file:", "cookies-next": "^5.0.2", "dayjs": "^1.11.13", + "dragula": "^3.7.3", "file-saver": "^2.0.5", "formik": "^2.4.6", "jspdf": "^2.5.2", @@ -48,6 +53,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@types/dragula": "^3.7.5", "@types/file-saver": "^2.0.7", "@types/node": "^20", "@types/react": "^19", diff --git a/src.zip b/src.zip new file mode 100644 index 0000000..746c56d Binary files /dev/null and b/src.zip differ diff --git a/src/Components/DragAndDrop.tsx b/src/Components/DragAndDrop.tsx new file mode 100644 index 0000000..16c56b5 --- /dev/null +++ b/src/Components/DragAndDrop.tsx @@ -0,0 +1,165 @@ +"use client"; + +import React, { useEffect, useRef, useState } from "react"; +import dragula from "dragula"; +import "dragula/dist/dragula.css"; + +interface Task { + id: string; + title: string; + description: string; +} + +interface Section { + id: string; + name: string; + tasks: Task[]; +} + +interface DragAndDropProps { + initialSections: Section[]; +} + +const DragAndDrop: React.FC = ({ initialSections }) => { + const [sections, setSections] = useState(initialSections); + const containerRefs = useRef([]); + + useEffect(() => { + + const mirrorContainer = document.createElement("div"); + document.body.appendChild(mirrorContainer); + + const drake = dragula(containerRefs.current.filter(Boolean), { + mirrorContainer, + }); + + drake.on("drop", (el: HTMLElement, target: HTMLElement, source: HTMLElement) => { + const sourceId = source.dataset.sectionId; + const targetId = target.dataset.sectionId; + const taskId = el.dataset.taskId; + + if (sourceId && targetId && taskId) { + setSections((prevSections) => { + const updatedSections = [...prevSections]; + + const sourceSection = updatedSections.find((s) => s.id === sourceId); + const taskToMove = sourceSection?.tasks.find((t) => t.id === taskId); + if (sourceSection && taskToMove) { + sourceSection.tasks = sourceSection.tasks.filter((t) => t.id !== taskId); + } + + const targetSection = updatedSections.find((s) => s.id === targetId); + if (targetSection && taskToMove) { + targetSection.tasks.push(taskToMove); + } + + return updatedSections; + }); + } + }); + + return () => { + drake.destroy(); + document.body.removeChild(mirrorContainer); + }; + }, []); + + const addTask = (sectionId: string) => { + const title = prompt("Enter task title:"); + const description = prompt("Enter task description:"); + if (title && description) { + setSections((prevSections) => + prevSections.map((section) => + section.id === sectionId + ? { + ...section, + tasks: [ + ...section.tasks, + { id: `${Date.now()}`, title, description }, + ], + } + : section + ) + ); + } + }; + + const deleteTask = (sectionId: string, taskId: string) => { + setSections((prevSections) => + prevSections.map((section) => + section.id === sectionId + ? { + ...section, + tasks: section.tasks.filter((task) => task.id !== taskId), + } + : section + ) + ); + }; + + return ( +
+ {sections.map((section, index) => ( +
{ + if (el) containerRefs.current[index] = el; + }} + data-section-id={section.id} + style={{ + border: "1px solid #ccc", + padding: "10px", + width: "250px", + minHeight: "300px", + backgroundColor: "#f9f9f9", + borderRadius: "8px", + }} + > +
+

{section.name}

+ +
+ {section.tasks.map((task) => ( +
+ {task.title} +

+ {task.description} +

+
+))} + +
+ ))} +
+ ); +}; + +export default DragAndDrop; diff --git a/src/app/[locale]/brainspace/components/Modules.tsx b/src/app/[locale]/brainspace/components/Modules.tsx index 4b9fcab..55f0e0b 100644 --- a/src/app/[locale]/brainspace/components/Modules.tsx +++ b/src/app/[locale]/brainspace/components/Modules.tsx @@ -14,7 +14,9 @@ import Loader from "@/common/loader/loader"; import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Pagination } from "swiper/modules"; -const Modules: React.FC<{ additionalModules?: Module[] }> = ({ additionalModules = [] }) => { +const Modules: React.FC<{ additionalModules?: Module[] }> = ({ + additionalModules = [], +}) => { const [loading, setLoading] = useState(true); const [modulesData, setModulesData] = useState([]); const { accessToken } = useAuth(); @@ -23,12 +25,15 @@ const Modules: React.FC<{ additionalModules?: Module[] }> = ({ additionalModules // Function to fetch modules const fetchModules = async () => { try { - const response = await fetch("https://posnew.smcare.net/v0_0_1-modules/get-modules", { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); + const response = await fetch( + "https://posnew.smcare.net/v0_0_1-modules/get-modules", + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + } + ); if (response.ok) { const data = await response.json(); @@ -73,16 +78,14 @@ const Modules: React.FC<{ additionalModules?: Module[] }> = ({ additionalModules if (width >= 1100) { setChunkSize(10); // Large screens (lg) } else if (width >= 800) { - setChunkSize(9); // Medium screens (md) - } - else if (width >= 700) { + setChunkSize(9); // Medium screens (md) + } else if (width >= 700) { setChunkSize(6); // Medium screens (md) } else if (width >= 400) { setChunkSize(4); // Medium screens (md) - } else if ((width >= 300 && width <= 500) && height >= 800) { + } else if (width >= 300 && width <= 500 && height >= 800) { setChunkSize(4); // Small screens - } - else { + } else { setChunkSize(2); // Small screens (sm) } // if (height >= 1100) { @@ -92,7 +95,7 @@ const Modules: React.FC<{ additionalModules?: Module[] }> = ({ additionalModules // } else if (height <= 800 && width >= 500) { // setChunkSize(2); // Small screens // } else if (height >= 800) { - // setChunkSize(9); // Medium screens (md) + // setChunkSize(9); // Medium screens (md) // } else if (height >= 700) { // setChunkSize(6); // Medium screens (md) // } else if (height >= 400) { @@ -136,7 +139,9 @@ const Modules: React.FC<{ additionalModules?: Module[] }> = ({ additionalModules
{chunk.map((module: Module) => ( @@ -148,7 +153,9 @@ const Modules: React.FC<{ additionalModules?: Module[] }> = ({ additionalModules width={160} height={160} /> -

{module.name}

+

+ {module.name} +

))} diff --git a/src/app/[locale]/crm/components/AddCallModal.tsx b/src/app/[locale]/crm/components/AddCallModal.tsx new file mode 100644 index 0000000..3d382e1 --- /dev/null +++ b/src/app/[locale]/crm/components/AddCallModal.tsx @@ -0,0 +1,109 @@ +'use client'; + +import React, { useState } from 'react'; +import { Dialog } from 'primereact/dialog'; +import { Dropdown } from 'primereact/dropdown'; +import { InputText } from 'primereact/inputtext'; +import { Calendar } from 'primereact/calendar'; +import { Button } from 'primereact/button'; +import { InputTextarea } from 'primereact/inputtextarea'; + +const AddCallModal: React.FC<{ isOpen: boolean; onClose: () => void }> = ({ isOpen, onClose }) => { + const [salesRep, setSalesRep] = useState(null); + const [callDate, setCallDate] = useState(null); + const [duration, setDuration] = useState(''); + const [outcome] = useState('01/02/2025'); // Default outcome + const [note, setNote] = useState(''); + + const salesRepOptions = [ + { label: 'Employee 1', value: 'employee1' }, + { label: 'Employee 2', value: 'employee2' }, + { label: 'Employee 3', value: 'employee3' }, + ]; + + return ( + + {/* Header */} +
+

Add Call

+
+ + {/* Content */} +
+
+ {/* Sales Rep ID */} +
+ + setSalesRep(e.value)} + options={salesRepOptions} + placeholder="Employee name" + className="w-full p-inputtext border-gray-300 rounded-md" + /> +
+ + {/* Call Date */} +
+ + setCallDate(e.value as Date)} + placeholder="Enter call date" + className="w-full p-inputtext border-gray-300 rounded-md" + /> +
+ + {/* Call Duration */} +
+ + setDuration(e.target.value)} + placeholder="Enter call duration" + className="w-full border-gray-300 rounded-md" + /> +
+ + {/* Outcome (Pre-filled) */} +
+ + +
+ + {/* Note */} +
+ + setNote(e.target.value)} + placeholder="Add note" + rows={4} + className="w-full border-gray-300 rounded-md" + /> +
+
+
+ + {/* Footer - Create Button */} +
+
+
+ ); +}; + +export default AddCallModal; diff --git a/src/app/[locale]/crm/components/CallLogModal.tsx b/src/app/[locale]/crm/components/CallLogModal.tsx new file mode 100644 index 0000000..6ca08d1 --- /dev/null +++ b/src/app/[locale]/crm/components/CallLogModal.tsx @@ -0,0 +1,132 @@ +'use client'; + +import React from 'react'; + +interface CallLogModalProps { + isOpen: boolean; + onClose: () => void; +} + +const salesReps = ['John Doe', 'Jane Smith', 'Alice Johnson', 'Bob Brown']; + +const CallLogModal: React.FC = ({ isOpen, onClose }) => { + if (!isOpen) return null; + + return ( +
+
+
+

+ Log Call +

+ +
+ +
+ + + + +