diff --git a/src/App.tsx b/src/App.tsx index 71e7708a9..64f87a93e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -57,6 +57,7 @@ import DashboardLC from "./modules/Dashboard/modules/LearningCircleV2/pages/dash import YourLC from "./modules/Dashboard/modules/LearningCircleV2/pages/YourLC/YourLC"; import MoreInfoLC from "./modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC"; import AttendeeReport from "./modules/Dashboard/modules/LearningCircleV2/pages/AttendeeReport/AttendeeReport"; +import LCReport from "./modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport"; const Profile = lazy( () => import("./modules/Dashboard/modules/Profile/pages/Profile") @@ -724,10 +725,10 @@ function App() { /> ) }, - { - path: "learning-circle", - element: - }, + // { + // path: "learning-circle", + // element: + // }, { path: "learningcircle", element: @@ -753,33 +754,37 @@ function App() { element: }, { - path: "learningcircle/create-meetup/:circle_id", - element: - }, - { - path: "learning-circle/meetup/:id", - element: - }, - { - path: "learning-circle/meetup/:id/attendee-report", - element: - }, - { - path: "learning-circle/details/:id", - element: - }, - { - path: "learning-circle/dashboard/:id", - element: - }, - { - path: "learning-circle/find-circle", - element: + path: "learningcircle/report/:meet_id", + element: }, { - path: "learning-circle/create-circle", - element: + path: "learningcircle/create-meetup/:circle_id", + element: }, + // { + // path: "learning-circle/meetup/:id", + // element: + // }, + // { + // path: "learning-circle/meetup/:id/attendee-report", + // element: + // }, + // { + // path: "learning-circle/details/:id", + // element: + // }, + // { + // path: "learning-circle/dashboard/:id", + // element: + // }, + // { + // path: "learning-circle/find-circle", + // element: + // }, + // { + // path: "learning-circle/create-circle", + // element: + // }, { path: "organization-transfer", element: ( diff --git a/src/modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport.module.css b/src/modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport.module.css new file mode 100644 index 000000000..21a8836cd --- /dev/null +++ b/src/modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport.module.css @@ -0,0 +1,224 @@ +.popup { + position: fixed; + backdrop-filter: blur(5px) contrast(100%); + -webkit-backdrop-filter: blur(5px) contrast(100%); + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 50; + display: flex; + transition: 0.3s ease; + /* transform: scale(0); */ + flex-direction: column; + align-items: center; + + .container { + background: #fff; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 20px; + border-radius: 10px; + width: 50%; + height: fit-content; + display: flex; + flex-direction: column; + align-items: start; + justify-content: start; + gap: 20px; + h1 { + color: #000; + font-size: 20px; + font-weight: 600; + } + p { + width: 100%; + background: #eef2ff; + text-align: left; + padding: 10px; + border-radius: 10px; + color: #000; + } + a { + width: 100%; + background: #eef2ff; + text-align: left; + padding: 10px; + border-radius: 10px; + display: flex; + gap: 10px; + align-items: center; + } + .buttons { + width: 100%; + button { + width: 100%; + margin-top: 20px; + } + } + } +} +.lcReport { + width: 95%; + + .backLink { + display: flex; + align-items: center; + background-color: white; + padding: 0.5rem 1rem; + margin-bottom: 10px; + width: fit-content; + color: #5d5d5d; + text-decoration: none; + cursor: pointer; + border-radius: 10px; + } + .container { + padding: 20px; + background: #fff; + border-radius: 10px; + display: flex; + gap: 10px; + flex-direction: column; + align-items: start; + text-align: start; + width: 100%; + h1 { + color: #000; + font-weight: 500; + font-size: 18px; + margin-bottom: 10px; + } + h2 { + margin-top: 10px; + color: #000; + font-size: 17px; + } + textarea { + height: 100px; + width: 70%; + padding: 10px; + background: #eef2ff; + padding: 10px; + border-radius: 10px; + color: #333; + } + .info { + color: #777; + font-size: 15px; + margin-bottom: 20px; + } + .attendees { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; + + .attendee { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + background: #eef2ff; + padding: 15px 20px; + border-radius: 10px; + .content { + display: flex; + gap: 20px; + align-items: center; + + .counter { + color: #000; + font-weight: 600; + } + .profile { + height: 30px; + width: 30px; + border-radius: 50%; + } + } + .noreport { + color: rgb(255, 113, 113); + font-size: 13px; + font-weight: 600; + } + .buttons { + display: flex; + gap: 20px; + span { + display: flex; + padding: 1px; + border-radius: 50%; + border: 1px solid #22222290; + cursor: pointer; + font-size: 30px; + width: 35px; + height: 35px; + align-items: center; + justify-content: center; + &.accepted, + &.rejected { + font-size: 15px !important; + border: none; + width: auto; + gap: 5px; + + &:hover { + background: none; + } + } + &.accepted { + color: rgb(0, 210, 0); + } + &.rejected { + font-size: 12px; + color: red; + } + &:hover { + background: #b9c9ff; + } + &.view { + font-size: 20px; + } + &.accept { + color: rgb(0, 210, 0); + } + &.reject { + color: red; + } + &.disabled { + cursor: not-allowed; + color: grey; + } + } + } + } + } + .bottom { + width: 100%; + display: flex; + justify-content: end; + .button { + margin-top: 20px; + } + } + } +} + +@media screen and (max-width: 768px) { + .lcReport { + width: 100%; + .container { + textarea { + width: 100%; + } + } + } + .popup { + .container { + width: 90%; + } + } +} diff --git a/src/modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport.tsx b/src/modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport.tsx new file mode 100644 index 000000000..316cba770 --- /dev/null +++ b/src/modules/Dashboard/modules/LearningCircleV2/pages/LCReport/LCReport.tsx @@ -0,0 +1,328 @@ +import { useEffect, useState } from "react"; +import styles from "./LCReport.module.css"; +import { + getLcReportInfo, + submitLcReport +} from "../../services/LearningCircleAPIs"; +import { useNavigate, useParams } from "react-router-dom"; +import placeholder from "../../../../assets/images/dpm.webp"; +import { BiCheck, BiLink, BiRefresh, BiX } from "react-icons/bi"; +import { BsEye } from "react-icons/bs"; +import { FiChevronLeft } from "react-icons/fi"; +import MuModal from "@/MuLearnComponents/MuModal/MuModal"; +import EditProfilePopUp from "../../../Profile/components/EditProfilePopUp/pages/EditProfilePopUp"; +import toast from "react-hot-toast"; +import { PowerfulButton } from "@/MuLearnComponents/MuButtons/MuButton"; +export default function LCReport() { + const [reportInfo, setReportInfo] = useState(null); + const [report, setReport] = useState(""); + const [acceptedList, setAcceptedList] = useState([]); + const [rejectedList, setRejectedList] = useState([]); + const [reportSubmitted, setReportSubmitted] = useState(false); + const [selectedAttendee, setSelectedAttendee] = useState<{ + user_id: string; + full_name: string; + muid: string; + is_lc_approved: boolean; + report: string | null; + report_link: string | null; + } | null>(null); + const params = useParams(); + const navigate = useNavigate(); + useEffect(() => { + getLcReportInfo(params.meet_id ?? "").then(res => { + setReportInfo(res); + if (res?.is_report_submitted) { + setReportSubmitted(true); + setReport(res.report ?? ""); + setAcceptedList( + res.attendees + .filter(attendee => attendee.is_lc_approved) + .map(attendee => attendee.user_id) + ); + setRejectedList( + res.attendees + .filter(attendee => !attendee.is_lc_approved) + .map(attendee => attendee.user_id) + ); + } + }); + }, []); + + const handleAccept = (attendee: any) => { + if (acceptedList.includes(attendee.user_id)) { + return; + } + if (rejectedList.includes(attendee.user_id)) { + setRejectedList( + rejectedList.filter(rej => rej != attendee.user_id) + ); + } + setAcceptedList([...acceptedList, attendee.user_id]); + }; + + const handleReject = (attendee: any) => { + if (rejectedList.includes(attendee.user_id)) { + return; + } + if (acceptedList.includes(attendee.user_id)) { + setAcceptedList( + acceptedList.filter(acc => acc != attendee.user_id) + ); + } + setRejectedList([...rejectedList, attendee.user_id]); + }; + + const handleView = (attendee: any) => { + if (attendee.report == null || attendee.report == "") { + toast.error(`${attendee.full_name} didn't submited his report.`); + return; + } + setSelectedAttendee(attendee); + }; + + const handleReset = (attendee: any) => { + if (reportSubmitted) { + return; + } + setAcceptedList(acceptedList.filter(acc => acc != attendee.user_id)); + setRejectedList(rejectedList.filter(rej => rej != attendee.user_id)); + }; + + const handleSubmit = () => { + if (reportSubmitted) { + return; + } + if ((reportInfo?.attendees ?? []).length <= 1) { + toast.error( + "You need minumum of 2 participants to submit a report." + ); + return; + } + if (report == null || report == "") { + toast.error("Report empty"); + return; + } + if ( + acceptedList.length + rejectedList.length != + (reportInfo?.attendees ?? []).length + ) { + toast.error("You must review all attendees."); + return; + } + var map: Record = {}; + acceptedList.forEach(user_id => { + map[user_id] = true; + }); + rejectedList.forEach(user_id => (map[user_id] = false)); + // console.log(map); + submitLcReport(params.meet_id ?? "", report, map).then(res => { + if (res) { + navigate(-1); + } + }); + }; + + return ( + <> + {selectedAttendee && ( +
+
+

+ {selectedAttendee.full_name.split(" ")[0]}'s Report +

+ Report +

{selectedAttendee.report}

+ Links + + + {selectedAttendee.report_link} + +
+ { + setSelectedAttendee(null); + }} + > + Close + +
+
+
+ )} +
+
{ + navigate(-1); + }} + > + + Learning Circles +
+
+

Report Submission

+ +

Attendees

+ + From the listed attendees of your learning circle, + review the report submitted by them and approve/reject + it to submit report. + +
+ {reportInfo + ? reportInfo.attendees.map((attendee, index) => { + return ( +
+
+ + {index + 1}. + + + + {attendee.full_name} + + {/* + {attendee.muid} + */} +
+ {attendee.report != null && + attendee.report != "" ? ( +
+ {acceptedList.includes( + attendee.user_id + ) ? ( + + Accepted + + ) : rejectedList.includes( + attendee.user_id + ) ? ( + + Rejected + + ) : null} + + {!reportSubmitted && + (acceptedList.includes( + attendee.user_id + ) || + rejectedList.includes( + attendee.user_id + )) && ( + { + handleReset( + attendee + ); + }} + > + + + )} + + {reportSubmitted || + (!acceptedList.includes( + attendee.user_id + ) && + !rejectedList.includes( + attendee.user_id + )) ? ( + { + handleView( + attendee + ); + }} + > + + + ) : null} + {!acceptedList.includes( + attendee.user_id + ) && + !rejectedList.includes( + attendee.user_id + ) ? ( + <> + { + handleReject( + attendee + ); + }} + > + + + + { + handleAccept( + attendee + ); + }} + > + + + + ) : null} +
+ ) : ( + + Attendee report not submitted + + )} +
+ ); + }) + : null} +
+
+ + {reportSubmitted + ? "Report Already Submitted" + : "Submit Report"} + +
+
+
+ + ); +} diff --git a/src/modules/Dashboard/modules/LearningCircleV2/pages/dashboard/DashboardLC.tsx b/src/modules/Dashboard/modules/LearningCircleV2/pages/dashboard/DashboardLC.tsx index 89d6cd23e..2453b932a 100644 --- a/src/modules/Dashboard/modules/LearningCircleV2/pages/dashboard/DashboardLC.tsx +++ b/src/modules/Dashboard/modules/LearningCircleV2/pages/dashboard/DashboardLC.tsx @@ -53,7 +53,7 @@ export default function DashboardLC() { {circleInfo.next_meetup.title}
- {/* { var url = `${window.location.protocol}//${window.location.host}/dashboard/learningcircle/meetup/${circleInfo.next_meetup.id}` + @@ -78,7 +78,7 @@ export default function DashboardLC() { > Copy Link - */} + { @@ -134,7 +134,7 @@ export default function DashboardLC() { style={{ padding: "5px 40px" }} onClick={() => { navigate( - "/dashboard/learningcircle/attendee-report/" + + "/dashboard/learningcircle/report/" + circleInfo.next_meetup.id ); }} diff --git a/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.module.css b/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.module.css index 232c5f474..6a30865c7 100644 --- a/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.module.css +++ b/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.module.css @@ -1,3 +1,20 @@ +.joinCodeInput { + width: 70%; + margin: 10px; + padding: 0.5rem; + border: 1px solid #d1d1d1; + border-radius: 0.5rem; + font-size: 1rem; + margin-right: 1rem; + color: #000; +} +.joinInfo { + color: #515151; + font-size: 1rem; + margin-top: 0.5rem; + display: block; + padding: 20px; +} .container { display: flex; flex-direction: column; diff --git a/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.tsx b/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.tsx index ae23235ce..b5f603cf8 100644 --- a/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.tsx +++ b/src/modules/Dashboard/modules/LearningCircleV2/pages/moreInfoLC/MoreInfoLC.tsx @@ -2,7 +2,7 @@ import styles from "./MoreInfoLC.module.css"; import { FiChevronLeft } from "react-icons/fi"; import { CiLocationOn, CiClock2 } from "react-icons/ci"; import { PowerfulButton } from "@/MuLearnComponents/MuButtons/MuButton"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { useEffect, useState } from "react"; import { getMeetupInfo, @@ -23,13 +23,21 @@ export default function MoreInfoLC() { const [isModalOpen, setIsModalOpen] = useState(false); const [meetCode, setMeetCode] = useState(""); const params = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); useEffect(() => { + var code = searchParams.get("code"); + if (code) { + setMeetCode(code); + } getMeetupInfo(params.id ?? "").then(res => { setMeetup(res as any); }); }, []); const handleMainButton = () => { + if (meetup?.attendee?.is_report_submitted) { + navigate(`/dashboard/learningcircle/report/${meetup.id}`); + } if (meetup?.is_started) { if (meetup.attendee && meetup.attendee.is_joined) { if (!meetup.attendee.is_report_submitted) { @@ -38,6 +46,17 @@ export default function MoreInfoLC() { ); } } else { + if (meetCode) { + handleJoin(); + setMeetup({ + ...meetup, + attendee: { + ...(meetup.attendee as any), + is_joined: 1 + } + }); + return; + } setIsModalOpen(true); } } else { diff --git a/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleAPIs.ts b/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleAPIs.ts index 7dff083d3..56926e193 100644 --- a/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleAPIs.ts +++ b/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleAPIs.ts @@ -6,6 +6,60 @@ import toast from "react-hot-toast"; const openstreetmapurl = "https://nominatim.openstreetmap.org/search?format=json&q="; +export const getLcReportInfo = async ( + meet_id: string +): Promise => { + try { + const response = await privateGateway.get( + learningCircleRoutes.getLcReportInfo + meet_id + ); + return response.data.response; + } catch (err) { + const error = err as AxiosError; + toast.error( + ((error?.response?.data as any)?.message?.general ?? [ + "Unexpected error occured." + ])[0] + ); + return null; + } +}; + +export const submitLcReport = async ( + meet_id: string, + report: string, + attendees: Record +): Promise => { + try { + const response = await privateGateway.post( + learningCircleRoutes.getLcReportInfo + meet_id, + { + report: report, + attendees: attendees + } + ); + if (response.status === 200) { + toast.success( + [response.data?.message?.general ?? "Report Submited.."][0] + ); + return true; + } + toast.error( + (response.data?.message?.general ?? ["Unable to submit report."])[0] + ); + return false; + } catch (err) { + const error = err as AxiosError; + console.log(error); + toast.error( + ((error.response as any)?.data?.message?.general ?? [ + "Unable to join learning circle." + ])[0] + ); + return false; + } +}; + export const searchCoordinates = async ( searchString: string ): Promise => { diff --git a/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleInterface.d.ts b/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleInterface.d.ts index b11d4f961..b6c93adc6 100644 --- a/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleInterface.d.ts +++ b/src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleInterface.d.ts @@ -82,3 +82,16 @@ interface CircleMeetingAttendee { is_report_submitted: boolean; is_lc_approved: boolean; } + +interface LCReportInfo { + is_report_submitted: boolean; + report: string | null; + attendees: { + user_id: string; + full_name: string; + muid: string; + is_lc_approved: boolean; + report: string | null; + report_link: string | null; + }[]; +} diff --git a/src/services/urls.ts b/src/services/urls.ts index 8d1d65eb6..9514919f3 100644 --- a/src/services/urls.ts +++ b/src/services/urls.ts @@ -6,6 +6,7 @@ import { import { createLearningCircle, getCreatedLearningCircles, + getLcReportInfo, getLearningCircleInfo, scheduleMeetup } from "src/modules/Dashboard/modules/LearningCircleV2/services/LearningCircleAPIs"; @@ -55,7 +56,8 @@ export const learningCircleRoutes = { submitAttendeeReport: "/api/v1/dashboard/learningcircle/meeting/attendee-report/", getMeetups: "/api/v1/dashboard/learningcircle/meeting/list/", - joinMeetup: "/api/v1/dashboard/learningcircle/meeting/join/" + joinMeetup: "/api/v1/dashboard/learningcircle/meeting/join/", + getLcReportInfo: "/api/v1/dashboard/learningcircle/meeting/report/" }; export const dashboardRoutes = { forgetPassword: "/api/v1/dashboard/user/forgot-password/",