Skip to content

Commit

Permalink
inidividual event report feature (#125)
Browse files Browse the repository at this point in the history
* Added event report submitted or not, and graphql query/mutation

* added frontend for the individual event report

* event report updated and refactored

* updated individual report - fixed issues

* download report modified and download event added

* Updated package lock file

* File format

* Updated Event Report and Event PDF download templates and formatted the files

* formatting updated and iiith color logo added

* line breaks adjusted

* download option for event report in doc format added

* final draft of templates for event report

* Move event components into corresponding folder

* paragraph spacing changed

* Fix event bills status not coming on the page

* added bill status into full event

* bill status error handling changed

* Separate out eventBills from FULL_EVENT and handle error accordingly

* back button added and cc image size adjusted in doc

* btn variant changed

* warnings cleared

* Updated react-pdf to latest version

* added eventstatus  in finance section

---------

Co-authored-by: Bhav Beri <[email protected]>
  • Loading branch information
Dileepadari and bhavberi authored Jan 3, 2025
1 parent 257b556 commit b8c1583
Show file tree
Hide file tree
Showing 24 changed files with 5,123 additions and 380 deletions.
2,533 changes: 2,207 additions & 326 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"browser-image-resizer": "^2.4.1",
"cookies-next": "^4.3.0",
"dayjs": "^1.11.13",
"docx": "^9.1.0",
"file-saver": "^2.0.5",
"framer-motion": "^11.11.9",
"graphql": "^16.9.0",
"graphql-tag": "^2.12.6",
Expand All @@ -50,6 +52,8 @@
"react-hook-form": "^7.53.1",
"react-is": "^18.3.1",
"react-nice-avatar": "^1.5.0",
"react-pdf": "^9.2.1",
"react-pdf-html": "^2.1.2",
"react-slick": "^0.30.2",
"sharp": "^0.33.5",
"sitemap": "^8.0.0",
Expand Down
Binary file added public/assets/iiit-logo-color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/acl/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const routes = {
"/manage/events/new": ["cc", "club"], // has to be higher to not conflict with :id
"/manage/events/:id": ["cc", "club", "slc", "slo"],
"/manage/events/:id/edit": ["cc", "club", "slo"],
"/manage/events/:id/report": ["cc", "club", "slo"],
"/manage/events/:id/report/new": ["cc", "club"],
"/manage/data-events": ["cc", "club", "slc", "slo"],
"/manage/finances": ["cc", "club", "slo"],
"/manage/finances/:id": ["slo"],
Expand Down
37 changes: 37 additions & 0 deletions src/actions/events/report/create/server_action.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use server";

import { getClient } from "gql/client";
import { ADD_EVENT_REPORT } from "gql/mutations/events";

export async function createEventReportAction(details) {
const response = { ok: false, data: null, error: null };

try {
const client = getClient();
const result = await client.mutation(ADD_EVENT_REPORT, { details });

if (result.error) {
response.error = {
title: result.error.name,
messages: result.error.graphQLErrors?.map((ge) => ge.message) || [
result.error.message,
],
};
} else if (result.data) {
response.ok = true;
response.data = result.data.addEventReport;
} else {
response.error = {
title: "Unexpected Error",
messages: ["No data returned from GraphQL server."],
};
}
} catch (error) {
response.error = {
title: "Request Error",
messages: [error.message],
};
}

return response;
}
53 changes: 48 additions & 5 deletions src/app/manage/events/[id]/page.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { getClient } from "gql/client";
import { GET_FULL_EVENT } from "gql/queries/events";
import { GET_FULL_EVENT, GET_EVENT_BILLS_STATUS } from "gql/queries/events";
import { GET_ACTIVE_CLUBS } from "gql/queries/clubs";
import { GET_USER } from "gql/queries/auth";
import { getFullUser } from "actions/users/get/full/server_action";

import {
Box,
Expand All @@ -16,8 +18,10 @@ import ActionPalette from "components/ActionPalette";

import EventDetails from "components/events/EventDetails";
import EventBudget from "components/events/EventBudget";
import EventBillStatus from "components/events/EventBillStatus";
import EventBillStatus from "components/events/bills/EventBillStatus";
import EventReportStatus from "components/events/report/EventReportStatus";
import EventApprovalStatus from "components/events/EventApprovalStatus";
import { DownloadEvent } from "components/events/report/EventpdfDownloads";
import {
EditEvent,
CopyEvent,
Expand Down Expand Up @@ -59,12 +63,40 @@ export default async function ManageEventID({ params }) {
eventid: id,
});

let eventBillsData = null;

if (
event &&
event?.status?.state === "approved" &&
new Date(event?.datetimeperiod[1]) < new Date() &&
event?.budget?.length
) {
const { error, data = {} } = await getClient().query(
GET_EVENT_BILLS_STATUS,
{
eventid: id,
}
);
if (error && error.message.includes("Event not found"))
return redirect("/404");
eventBillsData = data;
}

const {
data: { activeClubs },
} = await getClient().query(GET_ACTIVE_CLUBS);

const { data: { userMeta, userProfile } = {} } = await getClient().query(
GET_USER,
{ userInput: null },
{ userInput: null }
);
const user = { ...userMeta, ...userProfile };

const pocProfile = await getFullUser(event?.poc);
if (!pocProfile) {
return redirect("/404");
}

return (
user?.role === "club" &&
user?.uid !== event.clubid &&
Expand All @@ -80,6 +112,14 @@ export default async function ManageEventID({ params }) {
{ status: event?.status, location: event?.location },
]}
right={getActions(event, { ...userMeta, ...userProfile })}
downloadbtn={
<DownloadEvent
event={event}
clubs={activeClubs}
pocProfile={pocProfile}
eventBills={eventBillsData?.eventBills || {}}
/>
}
/>
<EventDetails showCode event={event} />
<Divider sx={{ borderStyle: "dashed", my: 2 }} />
Expand Down Expand Up @@ -169,8 +209,11 @@ export default async function ManageEventID({ params }) {
{/* show Approval status */}
{EventApprovalStatus(event?.status, event?.studentBodyEvent)}

{/* show financial information */}
{["cc", "club", "slo"].includes(user?.role) && EventBillStatus(event)}
{/* show post event information */}
{["cc", "club", "slo"].includes(user?.role) &&
EventBillStatus(event, eventBillsData?.eventBills || null)}
{["cc", "club", "slo"].includes(user?.role) &&
EventReportStatus(event, user)}
</Box>
)
);
Expand Down
70 changes: 70 additions & 0 deletions src/app/manage/events/[id]/report/new/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { getClient } from "gql/client";
import { GET_FULL_EVENT } from "gql/queries/events";
import { redirect } from "next/navigation";
import { GET_USER } from "gql/queries/auth";

import { Container, Typography } from "@mui/material";

import EventReportForm from "components/events/report/EventReportForm";

export const metadata = {
title: "Create Event Report",
};

function transformEvent(event) {
return {
...event,
datetimeperiod: [
new Date(event?.datetimeperiod[0]),
new Date(event?.datetimeperiod[1]),
],
budget:
event?.budget?.map((budget, key) => ({
...budget,
id: budget?.id || key,
})) || [],
population: parseInt(event?.population || 0),
additional: event?.additional || "",
equipment: event?.equipment || "",
poc: event?.poc,
collabclubs: event?.collabclubs || [],
};
}

export default async function NewEventReport({ params }) {
const { id } = params;
const { data: { userMeta, userProfile } = {} } = await getClient().query(
GET_USER,
{ userInput: null }
);
const user = { ...userMeta, ...userProfile };

try {
const { data: { event } = {} } = await getClient().query(GET_FULL_EVENT, {
eventid: id,
});
if (
!event ||
event?.eventReportSubmitted ||
user?.uid !== event.clubid ||
event.status.state !== "approved"
) {
return redirect("/404");
}
return (
<Container>
<Typography variant="h3" gutterBottom mb={3}>
Create Event Report
</Typography>

<EventReportForm
id={id}
defaultValues={transformEvent(event)}
action="create"
/>
</Container>
);
} catch (error) {
return redirect("/404");
}
}
58 changes: 58 additions & 0 deletions src/app/manage/events/[id]/report/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { getClient } from "gql/client";
import { GET_EVENT_REPORT, GET_FULL_EVENT } from "gql/queries/events";
import { GET_ACTIVE_CLUBS } from "gql/queries/clubs";
import { getFullUser } from "actions/users/get/full/server_action";
import { EventReportDetails } from "components/events/report/EventReportDetails";
import { GET_USER } from "gql/queries/auth";
import { redirect } from "next/navigation";

export default async function EventReport({ params }) {
const { id } = params;

try {
const { data: { event } = {} } = await getClient().query(GET_FULL_EVENT, {
eventid: id,
});

const { data: { userMeta, userProfile } = {} } = await getClient().query(
GET_USER,
{ userInput: null },
);
const user = { ...userMeta, ...userProfile };
user?.role === "club" &&
user?.uid !== event.clubid &&
!event?.collabclubs.includes(user?.uid) &&
redirect("/404");

if (!event || !event?.eventReportSubmitted) {
return redirect("/404");
}

const { data: { eventReport } = {} } = await getClient().query(
GET_EVENT_REPORT,
{
eventid: id,
}
);

const {
data: { activeClubs },
} = await getClient().query(GET_ACTIVE_CLUBS);

const submittedUserProfile = await getFullUser(eventReport?.submittedBy);
if (!submittedUserProfile || !eventReport) {
return redirect("/404");
}

return (
<EventReportDetails
event={event}
eventReport={eventReport}
submittedUser={submittedUserProfile}
clubs={activeClubs}
/>
);
} catch (error) {
return redirect("/404");
}
}
76 changes: 45 additions & 31 deletions src/app/manage/finances/[id]/page.jsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,74 @@
import { getClient } from "gql/client";
import { GET_EVENT_BILLS_STATUS } from "gql/queries/events";
import { redirect, notFound } from "next/navigation";
import Link from "next/link";

import { Button, Container, Stack, Typography } from "@mui/material";

import BillsStatusForm from "components/events/EditBillsStatus";
import BillsStatusForm from "components/events/bills/EditBillsStatus";

export const metadata = {
title: "Edit Bill Status",
};

export default async function EditHoliday({ params }) {
export default async function EditFinance({ params }) {
const { id } = params;

try {
const { data, error } = await getClient().query(GET_EVENT_BILLS_STATUS, {
eventid: id,
});

if ((error || !data) && !error?.message.includes("Bills status not found"))
notFound();

const defaultValues = {
state: null,
sloComment: null,
};

const eventBills = data?.eventBills || defaultValues;
const { data, error } = await getClient().query(GET_EVENT_BILLS_STATUS, {
eventid: id,
});

if (error && !error.message.includes("no bills status")) {
return (
<Container>
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
mb={3}
>
<Typography variant="h3" gutterBottom>
Edit Bill Status Details
</Typography>
<Typography variant="h4" align="center" mt={5} px={2}>
Error: {error.message.slice(10)}
</Typography>
<Stack direction="column" alignItems="center" mt={2}>
<Button
variant="contained"
color="primary"
component={Link}
href={`/manage/events/${id}`}
href={`/manage/finances`}
>
<Typography variant="button" color="opposite">
View Event
Go Back
</Typography>
</Button>
</Stack>
<BillsStatusForm id={id} defaultValues={eventBills} />
</Container>
);
} catch (error) {
return redirect("/404");
}

const defaultValues = {
state: null,
sloComment: null,
};

const eventBills = data?.eventBills || defaultValues;

return (
<Container>
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
mb={3}
>
<Typography variant="h3" gutterBottom>
Edit Bill Status Details
</Typography>
<Button
variant="contained"
color="primary"
component={Link}
href={`/manage/events/${id}`}
>
<Typography variant="button" color="opposite">
View Event
</Typography>
</Button>
</Stack>
<BillsStatusForm id={id} defaultValues={eventBills} />
</Container>
);
}
Loading

0 comments on commit b8c1583

Please sign in to comment.