Skip to content

Commit

Permalink
Implemented toast + search filter
Browse files Browse the repository at this point in the history
  • Loading branch information
brelieu05 committed Feb 4, 2025
1 parent b269dea commit 2b28a0e
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 34 deletions.
2 changes: 2 additions & 0 deletions client/src/components/PDFButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useState, useEffect } from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
import { PDFDownloadLink } from '@react-pdf/renderer';
import { useBackendContext } from '../contexts/hooks/useBackendContext';
import { IconButton } from "@chakra-ui/react";
import { DownloadIcon } from "@chakra-ui/icons";


const styles = StyleSheet.create({
Expand Down
133 changes: 99 additions & 34 deletions client/src/components/invoices/InvoicesDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
import { Text, Box, Stack, Table, HStack, Tr, Thead, Th, TableContainer, Tbody, Td, Flex, Button, Input, IconButton, Image, InputGroup, InputRightElement} from "@chakra-ui/react";
import { Text, Box, Stack, Table, HStack, Tr, Thead, Th, TableContainer, Tbody, Td, Flex, Button, Input, IconButton, Image, InputGroup, InputRightElement, Heading} from "@chakra-ui/react";
import filterIcon from "../../assets/logo/filter.svg";
import { DownloadIcon, SearchIcon } from "@chakra-ui/icons";
import { useBackendContext } from "../../contexts/hooks/useBackendContext";
import Navbar from "../Navbar";
import { useEffect, useState } from "react";
function InvoicesDashboard(){
// const items = [
// { id: 1, name: "Dance Class", status: "Paid", payee: "John Adams", dateDue: "11/12/25" },
// { id: 2, name: "Coffee Maker", status: "Not Paid",payee: "Home Appliances", dateDue: "11/12/25"},
// { id: 3, name: "Desk Chair", status: "Past Due", payee: "Furniture", dateDue: "11/12/25"},
// { id: 4, name: "Smartphone", status: "Paid", payee: "Electronics", dateDue: "11/12/25" },
// { id: 5, name: "Headphones", status: "Paid", payee: "Accessories", dateDue: "11/12/25" },
// ]
import { useToast } from '@chakra-ui/react'
import { useEffect, useState, useRef } from "react";
import { useNavigate } from 'react-router-dom'
import PDFButtonInvoice from "./PDFButtonInvoice"


function InvoicesDashboard(){
const toast = useToast()
const navigate = useNavigate();
const { backend } = useBackendContext();
const [invoices, setInvoices] = useState([]);
const [query, setQuery] = useState('');
const hasShownToast = useRef(false);

const isPaidColor = (invoice) => {
if (invoice.isSent && invoice.paymentStatus === "full") {
return "#6CE65C";
}
if (!invoice.isSent && new Date() < new Date(invoice.endDate) && invoice.paymentStatus !== "full") {
return "transparent";
}
return "#FF4D4D";
};

const isPaid = (invoice) => {
if (invoice.isSent && invoice.paymentStatus === "full") {
return "Paid";
}
if (!invoice.isSent && new Date() < new Date(invoice.endDate) && invoice.paymentStatus !== "full") {
return "Not Paid";
}
return "Past Due";
};


useEffect(() => {
const fetchInvoicesData = async () => {
try {
Expand All @@ -27,32 +48,70 @@ function InvoicesDashboard(){
console.log(err);
}
}
// const fetchEventsData = asynnc () => {
// const
// }

fetchInvoicesData();
}, []);

useEffect(() => {
if (invoices.length === 0 || hasShownToast.current) return;

const callToast = () => {
toast({
title: "Unpaid Invoices",
description: notifCounter > 1
? `You have ${notifCounter} past due invoices`
: `${pastDueInvoices[0].name} - ${pastDueInvoices[0].endDate.split("T")[0]}`,
status: "error",
duration: 9000,
position: "bottom-right",
isClosable: true,
render: () => (
<Box p={3} bg="#FED7D7" borderTop="4px solid" borderTopColor="red.500" onClick={() => navigate("/notification")}>
<Heading size="sm">Unpaid Invoices</Heading>
{notifCounter > 1
? `You have ${notifCounter} past due invoices`
: `${pastDueInvoices[0].name} -
${new Date(pastDueInvoices[0].endDate).toLocaleDateString("en-US", {month: "2-digit", day: "2-digit", year: "2-digit",})}`
}
</Box>
),
});
}

const getUnpaidInvoices = () => {
const pastDueInvoices = invoices.filter(invoice => isPaid(invoice) === "Past Due");
const notifCounter = pastDueInvoices.length;

if (notifCounter > 0) {
hasShownToast.current = true; // Set ref to true to prevent multiple toasts

callToast();
}
};

getUnpaidInvoices();
}, [invoices]);


return(
<Flex minH="100vh">
<Navbar style={{display: 'flex', h: "100vh"}}/>
<Flex
w='100vw'
justifyContent='center'
flexDirection='column'
padding="48px"
>
<Flex w='100vw' m='120px 40px' flexDirection='column' padding="48px">
<Flex justifyContent='space-between' mb='40px'>
<Button backgroundColor='transparent' border="1px solid rgba(71, 72, 73, 0.20)" borderRadius='15px' h='48px'>
<Image src={filterIcon} alt="Filter" boxSize="24px" mt='4px'/>Filter
</Button>

<InputGroup w='400px' borderColor='transparent' >
<InputRightElement pointerEvents='none' bgColor="rgba(71, 72, 73, 0.20)" borderRadius='0px 15px 15px 0px'>
<SearchIcon color='#767778'/>
</InputRightElement>
<Input icon={SearchIcon} borderColor='gray.100' borderRadius='15px 15px 15px 15px' placeholder="Search..."/>
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
icon={SearchIcon} borderColor='gray.100'
borderRadius='15px 15px 15px 15px'
placeholder="Search..."
/>
</InputGroup>
</Flex>
<TableContainer paddingTop="8px" border='1px solid var(--gray-200, #E2E8F0)' borderRadius='12px' >
Expand All @@ -68,24 +127,30 @@ function InvoicesDashboard(){
</Tr>
</Thead>
<Tbody>
{invoices.map((invoices, index) => (
{invoices
.filter(invoice => invoice.eventName.toLowerCase().includes(query.toLowerCase()))
.map((invoice, index) => (
<Tr key={index}>
<Td textAlign='center'>{invoices.eventName}</Td>
<Td textAlign='center'>{invoice.eventName}</Td>
<Td
textAlign='center'
bg = {
// invoices.isSent === true ? "#6CE65C" : invoices.isSent === "Not Paid"
// ? "grey.100" : invoices.isSent === "Past Due"
// ? "#FF4D4D" : "transparent"
// }
invoices.isSent === true ? "#6CE65C" : "gray.100"
}
bg = {isPaidColor(invoice)}
>
{invoices.isSent ? "Paid" : "Not Paid"}
{isPaid(invoice)}
</Td>
<Td textAlign='center'>{invoices.name}</Td>
<Td textAlign='center'>{invoices.endDate.substring(0, invoices.endDate.indexOf("T"))}</Td>
<Td><Flex justifyContent = "center"><IconButton icon={<DownloadIcon boxSize='20px'/>} backgroundColor='transparent' >DownloadIcon</IconButton></Flex></Td>
<Td textAlign='center'>{invoice.name}</Td>
<Td textAlign="center">
{new Date(invoice.endDate).toLocaleDateString("en-US", {
month: "2-digit",
day: "2-digit",
year: "numeric",
})}
</Td>
<Td>
<Flex justifyContent = "center">
<PDFButtonInvoice invoice={invoice} />
</Flex>
</Td>
</Tr>
))}
</Tbody>
Expand Down
109 changes: 109 additions & 0 deletions client/src/components/invoices/PDFButtonInvoice.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react';
import { useState, useEffect } from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
import { PDFDownloadLink } from '@react-pdf/renderer';
import { useBackendContext } from '../../contexts/hooks/useBackendContext';
import { IconButton } from "@chakra-ui/react";
import { DownloadIcon } from "@chakra-ui/icons";


const styles = StyleSheet.create({
page: {
padding: 30,
backgroundColor: '#ffffff'
},
section: {
margin: 10,
padding: 20,
borderRadius: 5,
backgroundColor: '#f8f9fa',
borderBottom: '1px solid #eee'
},
header: {
fontSize: 14,
fontWeight: 'bold',
marginBottom: 10,
color: '#2c3e50'
},
row: {
flexDirection: 'row',
marginBottom: 8,
alignItems: 'center'
},
label: {
width: 100,
fontSize: 12,
color: '#666',
marginRight: 10
},
value: {
flex: 1,
fontSize: 12,
color: '#333'
},
dateTime: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 10
},
timeBlock: {
flex: 1
}
});

const MyDocument = ({ bookingData }) => {
return (
<Document>
<Page size="A4" style={styles.page}>
{bookingData && bookingData.map((element) => (
<View style={styles.section} key={element.id}>
<Text>Archived: {element.archived}</Text>
<Text>Date: {element.date}</Text>
<Text>Event ID: {element.eventId}</Text>
<Text>DB ID: {element.id}</Text>
<Text>Room ID: {element.roomId}</Text>
<Text>Start Time: {element.startTime}</Text>
<Text>End Time: {element.endTime}</Text>
</View>
))}
</Page>
</Document>
);
};

const PDFButtonInvoice = ({invoice}) => {
const { backend } = useBackendContext();
const [bookingData, setBookingData] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const fetchData = async () => {
try {
const response = await backend.get("/bookings");
setBookingData(Array.isArray(response.data) ? response.data : []);
} catch (err) {
console.error("Error fetching bookings:", err);
} finally {
setIsLoading(false);
}
}
fetchData();
}, [backend]);

if (isLoading) return <div>Loading...</div>;

return (
<PDFDownloadLink
document={<MyDocument bookingData={bookingData} />}
fileName="bookingdata.pdf"
>
<IconButton
icon={<DownloadIcon boxSize="20px" />}
backgroundColor="transparent"
aria-label="Download PDF"
/>
</PDFDownloadLink>
);
};

export default PDFButtonInvoice;

0 comments on commit 2b28a0e

Please sign in to comment.