Skip to content

Commit

Permalink
Merge pull request #411 from COS301-SE-2024/fix/web/minor-fixes
Browse files Browse the repository at this point in the history
Fix/web/minor fixes
  • Loading branch information
Tinashe-Austin committed Sep 30, 2024
2 parents 523559b + 7b31779 commit f73fe11
Show file tree
Hide file tree
Showing 9 changed files with 537 additions and 266 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ import {
SortDescriptor,
Tooltip,
} from "@nextui-org/react";
import { PlusIcon } from "@assets/index";
import { SearchIcon } from "@assets/index";
import { ChevronDownIcon } from "@assets/index";
import { columns, users, statusOptions } from "../data/Data";
import { capitalize } from "../data/Utils";
import { OccupancyModal,TopNav } from "@components/index";
import { OccupancyModal, TopNav } from "@components/index";

const statusColorMap: Record<string, ChipProps["color"]> = {
ONSITE: "success",
Expand Down Expand Up @@ -311,12 +310,12 @@ export default function App() {
))}
</DropdownMenu>
</Dropdown>
<Button
{/* <Button
endContent={<PlusIcon />}
className=" bg-primary_alt text-text_col_alt"
>
Add New
</Button>
</Button> */}
</div>
</div>
<div className="flex justify-between items-center">
Expand Down
6 changes: 2 additions & 4 deletions frontend/occupi-web/src/components/modal/AvgArrDep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ const AvgArrDep: React.FC<AvgArrDepProps> = ({ email }) => {

const params = {
email: email,
// timeFrom: '2024-01-01T00:00:00.000Z',
// timeTo: '2024-09-11T00:00:00.000Z',
};

try {
Expand All @@ -44,7 +42,7 @@ const AvgArrDep: React.FC<AvgArrDepProps> = ({ email }) => {
}));
setChartData(formattedData);
} else {
setError("No data available");
setError("No data found for user. User should make bookings.");
}
} catch (err) {
setError("Failed to fetch user statistics");
Expand All @@ -63,7 +61,7 @@ const AvgArrDep: React.FC<AvgArrDepProps> = ({ email }) => {
};

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (error) return <div className="text-red-500">{error}</div>;

return (
<ResponsiveContainer width="100%" height={400}>
Expand Down
266 changes: 188 additions & 78 deletions frontend/occupi-web/src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,28 @@ import {
AccordionItem,
} from "@nextui-org/react";
import { EyeIcon } from "@assets/index";
import { ProfileComponent ,UserStatsComponent,UserHoursCharts,UserWorkRatioChart,UserPeakOfficeHoursChart,AvgArrDep} from "@components/index";

import {
ProfileComponent,
UserStatsComponent,
UserHoursCharts,
UserWorkRatioChart,
UserPeakOfficeHoursChart,
AvgArrDep,
} from "@components/index";
import { motion } from "framer-motion";
import * as userStatsService from 'userStatsService';
import * as userStatsService from "userStatsService";
import {
PDFDownloadLink,
Page,
Text,
View,
Document,
StyleSheet,
Image,
} from "@react-pdf/renderer";
import { occupiLogo } from "@assets/index"; // Assuming occupiLogo is an image asset
import NotificationService from "NotificationsService";

interface User {
id: string;
name: string;
Expand Down Expand Up @@ -64,6 +81,7 @@ interface ReportData {
export default function OccupancyModal({ user }: OccupancyModalProps) {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const [openItems, setOpenItems] = useState<string[]>([]);
const [reportData, setReportData] = useState<ReportData | null>(null);
const [isDownloading, setIsDownloading] = useState(false);

const handleToggle = (key: string) => {
Expand All @@ -79,83 +97,122 @@ export default function OccupancyModal({ user }: OccupancyModalProps) {
visible: { height: "auto", opacity: 1 },
};

const generateReport = async () => {
setIsDownloading(true);
try {
const params = {
email: user.email,
// Assuming the start and end dates for the report in ISO format
timeFrom: new Date('1970-01-01T00:00:00.000Z').toISOString(),
timeTo: new Date().toISOString(),
};
const fetchDataForReport = async () => {
const params = {
email: user.email,
timeFrom: new Date("1970-01-01T00:00:00.000Z").toISOString(),
timeTo: new Date().toISOString(),
};

const [userHours, userWorkRatio, userArrivalDepartureAverage, userPeakOfficeHours] = await Promise.all([
userStatsService.getUserHours(params),
userStatsService.getUserWorkRatio(params),
userStatsService.getUserArrivalDepartureAverage(params),
userStatsService.getUserPeakOfficeHours(params),
]);
const [
userHours,
userWorkRatio,
userArrivalDepartureAverage,
userPeakOfficeHours,
] = await Promise.all([
userStatsService.getUserHours(params),
userStatsService.getUserWorkRatio(params),
userStatsService.getUserArrivalDepartureAverage(params),
userStatsService.getUserPeakOfficeHours(params),
]);

const reportData: ReportData = {
userName: user.name,
userEmail: user.email,
dailyHours: userHours.data,
workRatio: {
...userWorkRatio.data[0],
days: userWorkRatio.data[0].days.map(day => ({
...day,
avgArrival: '', // Provide appropriate default or fetched value
avgDeparture: '' // Provide appropriate default or fetched value
}))
},
arrivalDeparture: {
...userArrivalDepartureAverage.data[0],
days: userArrivalDepartureAverage.data[0].days.map(day => ({
...day,
ratio: 0 // Provide an appropriate default or fetched value for ratio
}))
},
peakHours: userPeakOfficeHours.data[0],
};
const data: ReportData = {
userName: user.name,
userEmail: user.email,
dailyHours: userHours.data,
workRatio: {
...userWorkRatio.data[0],
days: userWorkRatio.data[0].days.map((day) => ({
...day,
avgArrival: "", // Provide appropriate default or fetched value
avgDeparture: "", // Provide appropriate default or fetched value
})),
},
arrivalDeparture: {
...userArrivalDepartureAverage.data[0],
days: userArrivalDepartureAverage.data[0].days.map((day) => ({
...day,
ratio: 0, // Provide an appropriate default or fetched value for ratio
})),
},
peakHours: userPeakOfficeHours.data[0],
};

const reportContent = `
User Statistics Report for ${reportData.userName} (${reportData.userEmail})
1. Work Ratio: ${reportData.workRatio.ratio.toFixed(2)}
setReportData(data);
};

2. Average Arrival and Departure Times:
Overall Average Arrival: ${reportData.arrivalDeparture.overallavgArrival}
Overall Average Departure: ${reportData.arrivalDeparture.overallavgDeparture}
const generateReportPDF = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.header}>
<Image style={styles.logo} src={occupiLogo} />
<Text style={styles.title}>User Statistics Report</Text>
</View>

3. Daily Hours Summary:
${reportData.dailyHours.map(day => ` ${day.date}: ${day.totalHours.toFixed(2)} hours`).join('\n')}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Overview</Text>
<Text style={styles.text}>Name: {reportData?.userName}</Text>
<Text style={styles.text}>Email: {reportData?.userEmail}</Text>
</View>

4. Peak Office Hours:
${reportData.peakHours.days.map((day) => ` ${day.weekday}: ${day.hours.join(', ')}`).join('\n')}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Work Ratio</Text>
<Text style={styles.text}>
Overall Work Ratio: {reportData?.workRatio.ratio.toFixed(2)}
</Text>
{reportData?.workRatio.days.map((day, index) => (
<Text key={index} style={styles.text}>
{day.weekday}: {day.ratio.toFixed(2)}
</Text>
))}
</View>

5. Work Ratio by Day:
${reportData.workRatio.days.map((day) => ` ${day.weekday}: ${day.ratio.toFixed(2)}`).join('\n')}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Arrival & Departure Times</Text>
<Text style={styles.text}>
Overall Average Arrival:{" "}
{reportData?.arrivalDeparture.overallavgArrival}
</Text>
<Text style={styles.text}>
Overall Average Departure:{" "}
{reportData?.arrivalDeparture.overallavgDeparture}
</Text>
{reportData?.arrivalDeparture.days.map((day, index) => (
<Text key={index} style={styles.text}>
{day.weekday}: Arrival - {day.avgArrival}, Departure -{" "}
{day.avgDeparture}
</Text>
))}
</View>

6. Arrival and Departure Times by Day:
${reportData.arrivalDeparture.days.map((day) => ` ${day.weekday}: Arrival - ${day.avgArrival}, Departure - ${day.avgDeparture}`).join('\n')}
`;
<View style={styles.section}>
<Text style={styles.sectionTitle}>Daily Hours</Text>
{reportData?.dailyHours.map((day, index) => (
<Text key={index} style={styles.text}>
{day.date}: {day.totalHours.toFixed(2)} hours
</Text>
))}
</View>

const blob = new Blob([reportContent], { type: 'text/plain' });
const url = window.URL.createObjectURL(blob);

const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `${user.name}_stats_report.txt`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
<View style={styles.section}>
<Text style={styles.sectionTitle}>Peak Office Hours</Text>
{reportData?.peakHours.days.map((day, index) => (
<Text key={index} style={styles.text}>
{day.weekday}: {day.hours.join(", ")}
</Text>
))}
</View>
</Page>
</Document>
);

// Notify the backend that the report has been downloaded
const handleGenerateReport = async () => {
setIsDownloading(true);
try {
await fetchDataForReport();
await NotificationService.downloadPDFReport(user.email);
} catch (error) {
console.error('Error generating report:', error);
// You might want to show an error message to the user here
console.error("Error generating report:", error);
} finally {
setIsDownloading(false);
}
Expand Down Expand Up @@ -186,10 +243,14 @@ ${reportData.arrivalDeparture.days.map((day) => ` ${day.weekday}: Arrival - ${
profileImage={`https://dev.occupi.tech/api/download-profile-image?email=${user.email}&quality=mid`}
email={user.email}
name={user.name}
officeStatus={user.status.toLowerCase() as "onsite" | "offsite" | "booked"}
officeStatus={
user.status.toLowerCase() as
| "onsite"
| "offsite"
| "booked"
}
/>
<UserStatsComponent email={user.email} />

</div>

<Accordion>
Expand Down Expand Up @@ -276,18 +337,67 @@ ${reportData.arrivalDeparture.days.map((day) => ` ${day.weekday}: Arrival - ${
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
<Button
className="text-text_col_alt bg-secondary_alt"
onPress={generateReport}
isLoading={isDownloading}
>
{isDownloading ? 'Generating Report...' : 'Download Report'}
</Button>
{reportData ? (
<Button className="bg-text_col_secondary_alt ">
<PDFDownloadLink
className="text-text_col_alt bg-secondary-alt "
document={generateReportPDF()}
fileName={`${user.name}_Stats_Report.pdf`}
>
{({ loading }) =>
loading ? "Generating PDF..." : "Download PDF Report"
}
</PDFDownloadLink>
</Button>
) : (
<Button
className="text-text_col_alt bg-secondary_alt"
onPress={handleGenerateReport}
isLoading={isDownloading}
>
{isDownloading ? "Generating Report..." : "Generate Report"}
</Button>
)}
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</>
);
}
}

// Define PDF styles
const styles = StyleSheet.create({
page: {
padding: 30,
backgroundColor: "#FFFFFF",
},
header: {
padding: 10,
flexDirection: "row",
justifyContent: "space-between",
borderBottom: "2 solid black",
marginBottom: 20,
},
logo: {
width: 50,
height: 50,
},
title: {
fontSize: 24,
textAlign: "right",
textTransform: "uppercase",
},
section: {
marginBottom: 20,
},
sectionTitle: {
fontSize: 18,
marginBottom: 10,
},
text: {
fontSize: 12,
marginBottom: 5,
},
});
Loading

0 comments on commit f73fe11

Please sign in to comment.