Fixed ui design on admin pages

This commit is contained in:
ZacTohZY
2024-08-12 04:21:54 +08:00
parent 36ca8c08de
commit 22e778e907
9 changed files with 387 additions and 292 deletions

View File

@@ -0,0 +1,31 @@
import { Select, SelectItem } from "@nextui-org/react";
import { ChangeEvent } from "react";
interface SimpleSelectProps {
label: string;
placeholder: string;
options: Array<{ key: string; label: string }>;
onChange: (value: string) => void;
}
const NextUIFormikSelect2 = ({ label, placeholder, options, onChange }: SimpleSelectProps) => {
const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
onChange(e.target.value);
};
return (
<Select
aria-label={label}
placeholder={placeholder}
onChange={handleChange}
>
{options.map((option) => (
<SelectItem key={option.key} value={option.key}>
{option.label}
</SelectItem>
))}
</Select>
);
};
export default NextUIFormikSelect2;

View File

@@ -660,7 +660,7 @@ export const VoucherIcon = () => {
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
strokeWidth="1.5"
stroke="currentColor"
className="size-6"
>
@@ -710,3 +710,23 @@ export const BookOpenIcon = () => {
</svg>
);
};
export const ImageIcon = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
/>
</svg>
);
};

View File

@@ -7,6 +7,7 @@ import NextUIFormikInput from "../components/NextUIFormikInput";
import { useNavigate } from "react-router-dom";
import instance from "../security/http";
import dayjs from "dayjs";
import { ArrowUTurnLeftIcon } from "../icons";
// Validation schema
const validationSchema = yup.object().shape({
@@ -73,68 +74,73 @@ export default function CreateSchedulePage() {
};
return (
<section className="flex flex-col items-center justify-center gap-20 py-8 md:py-10">
<div className="w-full flex items-start">
<Button
variant="light"
onPress={() => navigate(-1)}
>
Back
</Button>
<div className="flex-grow text-center">
<p className="text-3xl font-bold">Add New Schedule</p>
<div className="w-full h-full pb-12 pt-10">
<div className="w-[400px] mx-auto p-6 bg-red-50 dark:bg-primary-950 border border-primary-100 rounded-2xl">
<div>
<Button
isIconOnly
variant="light"
onPress={() => navigate(-1)}
>
<ArrowUTurnLeftIcon />
</Button>
<div className="pt-2">
<p className="text-3xl font-bold">Add New Schedule</p>
</div>
</div>
</div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ isValid, dirty }) => (
<Form className="flex flex-col gap-4">
<div className="flex gap-8">
<NextUIFormikDatePicker
label="Date"
name="date"
className="max-w-[280px]"
/>
<NextUIFormikInput
type='time'
label="Time"
name="time"
placeholder=""
/>
</div>
<div className="flex gap-8">
<NextUIFormikInput
type="text"
label="Location"
name="location"
placeholder="Enter the location"
/>
<NextUIFormikInput
type="text"
label="Postal Code"
name="postalCode"
placeholder="123456"
/>
</div>
<NextUIFormikRadioGroup
label="Status"
name="status"
className="flex gap-2"
>
<Radio value="Up coming">Up coming</Radio>
<Radio value="On going">On going</Radio>
<Radio value="Ended">Ended</Radio>
</NextUIFormikRadioGroup>
<Button type="submit" color="primary" className="w-[100px]">Create</Button>
{/* Example of using isValid and dirty */}
<p>Form is {isValid ? 'valid' : 'invalid'}</p>
<p>Form has been {dirty ? 'touched' : 'not touched'}</p>
</Form>
)}
</Formik>
</section >
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ isValid, dirty }) => (
<Form className="flex flex-col gap-4 pt-5 items-center justify-center">
<div className="flex flex-col gap-5 w-[360px]">
<NextUIFormikDatePicker
label="Date"
name="date"
className="max-w-[360px]"
/>
<NextUIFormikInput
type='time'
label="Time"
name="time"
placeholder=""
/>
<NextUIFormikInput
type="text"
label="Location"
name="location"
placeholder="Enter the location"
/>
<NextUIFormikInput
type="text"
label="Postal Code"
name="postalCode"
placeholder="123456"
/>
</div>
<NextUIFormikRadioGroup
label="Status"
name="status"
className="flex gap-2"
>
<Radio value="Up coming">Up coming</Radio>
<Radio value="On going">On going</Radio>
<Radio value="Ended">Ended</Radio>
</NextUIFormikRadioGroup>
<Button
type="submit"
color="primary"
className="w-[100px]"
isDisabled={!isValid || !dirty}
>
Create
</Button>
</Form>
)}
</Formik>
</div >
</div>
)
}

View File

@@ -6,6 +6,7 @@ import InsertPostImage from "../components/InsertPostImage";
import NextUIFormikInput from "../components/NextUIFormikInput";
import { NextUIFormikDatePicker } from "../components/NextUIFormikDatePicker";
import instance from "../security/http";
import { ArrowUTurnLeftIcon } from "../icons";
// Validation schema
const validationSchema = Yup.object({
@@ -75,11 +76,11 @@ export default function CreateVoucherPage() {
};
return (
<div className="w-full h-full pb-12">
<div className="w-[680px] mx-auto p-6 bg-gray-100 dark:bg-gray-950 border border-primary-100 rounded-2xl">
<div className="w-full h-full pb-12 pt-10">
<div className="w-[350px] mx-auto p-6 bg-red-50 dark:bg-primary-950 border border-primary-100 rounded-2xl ">
<div className="py-2">
<Button variant="light" onPress={() => navigate(-1)}>
Back
<Button variant="light" isIconOnly onPress={() => navigate(-1)}>
<ArrowUTurnLeftIcon />
</Button>
</div>
<div className="flex-grow overflow-y-auto">
@@ -90,7 +91,7 @@ export default function CreateVoucherPage() {
>
{({ isValid, dirty, setFieldValue }) => (
<Form className="flex flex-col gap-4">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-5 w-[280px]">
<InsertPostImage
onImageSelected={(file) => {
setFieldValue("brandLogo", file);
@@ -130,7 +131,7 @@ export default function CreateVoucherPage() {
labelPlacement="inside"
/>
</div>
<Button type="submit" color="primary" className="w-[100px]"
<Button type="submit" color="primary" className="w-[90px]"
isDisabled={!isValid || !dirty}>
Create
</Button>

View File

@@ -8,6 +8,7 @@ import NextUIFormikInput from "../components/NextUIFormikInput";
import { useNavigate, useParams } from "react-router-dom";
import instance from "../security/http";
import dayjs from "dayjs";
import { ArrowUTurnLeftIcon } from "../icons";
// Validation schema
@@ -92,73 +93,76 @@ export default function EditSchedulePage() {
};
return (
<section className="flex flex-col items-center justify-center gap-20 py-8 md:py-10">
<div className="w-full flex items-start">
<Button
variant="light"
onPress={() => navigate(-1)}
>
Back
</Button>
<div className="flex-grow text-center">
<p className="text-3xl font-bold">Add New Schedule</p>
<div className="w-full h-full pb-12 pt-10">
<div className="w-[400px] mx-auto p-6 bg-red-50 dark:bg-primary-950 border border-primary-100 rounded-2xl">
<div>
<Button
isIconOnly
variant="light"
onPress={() => navigate(-1)}
>
<ArrowUTurnLeftIcon />
</Button>
<p className="text-3xl font-bold pt-2">Update Schedule</p>
</div>
</div>
<Formik
enableReinitialize
initialValues={schedule}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ isValid, dirty }) => (
<Form className="flex flex-col gap-4">
<div className="flex gap-8">
<NextUIFormikDatePicker
label="Date"
name="date"
className="max-w-[284px]"
/>
<NextUIFormikInput
type='time'
label="Time"
name="time"
placeholder=""
/>
</div>
<div className="flex gap-8">
<NextUIFormikInput
type="text"
label="Location"
name="location"
placeholder="Enter the location"
/>
<NextUIFormikInput
type="text"
label="Postal Code"
name="postalCode"
placeholder="123456"
/>
</div>
<div>
<NextUIFormikRadioGroup
label="Status"
name="status"
className="flex gap-2"
<Formik
enableReinitialize
initialValues={schedule}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({ isValid, dirty }) => (
<Form className="flex flex-col gap-4 pt-5 items-center justify-center">
<div className="flex flex-col gap-5 w-[360px]">
<NextUIFormikDatePicker
label="Date"
name="date"
className="max-w-[360px]"
/>
<NextUIFormikInput
type='time'
label="Time"
name="time"
placeholder=""
/>
<NextUIFormikInput
type="text"
label="Location"
name="location"
placeholder="Enter the location"
/>
<NextUIFormikInput
type="text"
label="Postal Code"
name="postalCode"
placeholder="123456"
/>
</div>
<div>
<NextUIFormikRadioGroup
label="Status"
name="status"
className="flex gap-2"
>
<Radio value="Up coming">Up coming</Radio>
<Radio value="On going">On going</Radio>
<Radio value="Ended">Ended</Radio>
</NextUIFormikRadioGroup>
</div>
<Button
type="submit"
color="primary"
className="w-[100px]"
isDisabled={!isValid || !dirty}
>
<Radio value="Up coming">Up coming</Radio>
<Radio value="On going">On going</Radio>
<Radio value="Ended">Ended</Radio>
</NextUIFormikRadioGroup>
</div>
<div className="flex gap-5">
<Button type="submit" color="secondary" className="max-w-[100px]">Update</Button>
</div>
{/* Example of using isValid and dirty */}
<p>Form is {isValid ? 'valid' : 'invalid'}</p>
<p>Form has been {dirty ? 'touched' : 'not touched'}</p>
</Form>
)}
</Formik>
</section>
Update
</Button>
</Form>
)}
</Formik>
</div>
</div>
)
}

View File

@@ -6,14 +6,22 @@ import NextUIFormikInput from "../components/NextUIFormikInput";
import { NextUIFormikDatePicker } from "../components/NextUIFormikDatePicker";
import instance from "../security/http";
import { useEffect, useState } from "react";
import { ArrowUTurnLeftIcon } from "../icons";
// Validation schema
const validationSchema = Yup.object({
brand: Yup.string().trim().required("Brand name is required"),
description: Yup.string().trim().required("Description is required"),
expirationDate: Yup.date().required("Expiry date is required"),
quantityAvailable: Yup.number().typeError("Must be a number").positive("Must be a positive value").required("Quantity is required"),
code: Yup.string().trim().required("Code is required"),
brand: Yup.string().trim()
.required("Brand name is required"),
description: Yup.string().trim()
.required("Description is required"),
expirationDate: Yup.date()
.required("Expiry date is required"),
quantityAvailable: Yup.number()
.typeError("Must be a number")
.positive("Must be a positive value")
.required("Quantity is required"),
code: Yup.string().trim()
.required("Code is required"),
});
export default function EditVoucherPage() {
@@ -74,11 +82,11 @@ export default function EditVoucherPage() {
};
return (
<div className="w-full h-full pb-12">
<div className="w-[680px] mx-auto p-6 bg-gray-100 dark:bg-gray-950 border border-primary-100 rounded-2xl">
<div className="w-full h-full pb-12 pt-20">
<div className="w-[350px] mx-auto p-6 bg-red-50 dark:bg-primary-950 border border-primary-100 rounded-2xl">
<div className="py-2">
<Button variant="light" onPress={() => navigate(-1)}>
Back
<Button variant="light" isIconOnly onPress={() => navigate(-1)}>
<ArrowUTurnLeftIcon />
</Button>
</div>
<div className="flex-grow overflow-y-auto">
@@ -90,7 +98,7 @@ export default function EditVoucherPage() {
>
{({ isValid }) => (
<Form className="flex flex-col gap-4">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4 w-[280px]">
<NextUIFormikInput
label="Brand name"
name="brand"

View File

@@ -97,9 +97,9 @@ export default function ManageSchedulePage() {
const sortedScheduleList = sortScheduleList(scheduleList, sortDescriptor);
return (
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
<div className="inline-block text-center justify-center flex flex-row gap-10">
<p className="text-3xl font-bold">Admin Karang Guni Schedule</p>
<div className="flex flex-col gap-8 p-8">
<div className="inline-block text-center justify-between flex flex-row gap-10">
<p className="text-4xl font-bold">Karang Guni Schedule</p>
<Button
isIconOnly
color="primary"
@@ -108,7 +108,7 @@ export default function ManageSchedulePage() {
<PlusIcon />
</Button>
</div>
<div className="w-full overflow-auto max-w-screen-lg">
<div>
<Table aria-label="Schedule Table">
<TableHeader>
<TableColumn>
@@ -170,7 +170,7 @@ export default function ManageSchedulePage() {
)}
</ModalContent>
</Modal>
</section>
</div>
)
}

View File

@@ -88,106 +88,106 @@ export default function ManageVoucherPage() {
};
return (
<div className="w-full h-full">
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
<div className="flex flex-row gap-10">
<p className="text-2xl font-bold">Manage Vouchers</p>
<div>
<Button
isIconOnly
onPress={() => navigate("create-voucher")}
>
<PlusIcon />
</Button>
</div>
<div className="flex flex-col gap-8 p-8">
<div className="flex flex-row justify-between gap-10">
<p className="text-4xl font-bold">Manage Vouchers</p>
<div className="flex justify-end">
<Button
isIconOnly
onPress={() => navigate("create-voucher")}
color="primary"
variant="solid"
>
<PlusIcon />
</Button>
</div>
<div className="flex flex-col gap-8">
<Table aria-label="Voucher Table">
<TableHeader>
<TableColumn>Brand Logo</TableColumn>
<TableColumn>Brand</TableColumn>
<TableColumn>Description</TableColumn>
<TableColumn>Expiration Date</TableColumn>
<TableColumn>Quantity Available</TableColumn>
<TableColumn>Code</TableColumn>
<TableColumn>Actions</TableColumn>
</TableHeader>
<TableBody>
{voucherList.map((voucher) => (
<TableRow key={voucher.id}>
<TableCell>
{brandLogoUrls[voucher.id] && (
<img
src={brandLogoUrls[voucher.id]}
alt={voucher.brand}
style={{ width: "50px", height: "50px" }}
/>
)}
</TableCell>
<TableCell>{voucher.brand}</TableCell>
<TableCell>{voucher.description}</TableCell>
<TableCell>
{voucher.expirationDate.toLocaleDateString()}
</TableCell>
<TableCell>{voucher.quantityAvailable}</TableCell>
<TableCell>{voucher.code}</TableCell>
<TableCell>
<Button
isIconOnly
variant="light"
color="success"
onPress={() => handleEdit(voucher.id)}>
<PencilSquareIcon />
</Button>
<Button
isIconOnly
variant="light"
color="danger"
onPress={() => handleDelete(voucher.id)}>
<TrashDeleteIcon />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
{/* Confirmation Modal for Deleting */}
<Modal
isOpen={showConfirmDelete}
onOpenChange={setShowConfirmDelete}
isDismissable={false}
isKeyboardDismissDisabled={true}
>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
Delete Voucher
</ModalHeader>
<ModalBody>
<p>Are you sure you want to delete this voucher?</p>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
No
</div>
<div className="flex flex-col gap-8">
<Table aria-label="Voucher Table">
<TableHeader>
<TableColumn>Brand Logo</TableColumn>
<TableColumn>Brand</TableColumn>
<TableColumn>Description</TableColumn>
<TableColumn>Expiration Date</TableColumn>
<TableColumn>Quantity Available</TableColumn>
<TableColumn>Code</TableColumn>
<TableColumn>Actions</TableColumn>
</TableHeader>
<TableBody>
{voucherList.map((voucher) => (
<TableRow key={voucher.id}>
<TableCell>
{brandLogoUrls[voucher.id] && (
<img
src={brandLogoUrls[voucher.id]}
alt={voucher.brand}
style={{ width: "40px", height: "40px" }}
/>
)}
</TableCell>
<TableCell>{voucher.brand}</TableCell>
<TableCell>{voucher.description}</TableCell>
<TableCell>
{voucher.expirationDate.toLocaleDateString()}
</TableCell>
<TableCell>{voucher.quantityAvailable}</TableCell>
<TableCell>{voucher.code}</TableCell>
<TableCell>
<Button
isIconOnly
variant="light"
color="success"
onPress={() => handleEdit(voucher.id)}>
<PencilSquareIcon />
</Button>
<Button
isIconOnly
variant="light"
color="danger"
onPress={() => {
deleteVoucher();
onClose();
}}
>
Yes
onPress={() => handleDelete(voucher.id)}>
<TrashDeleteIcon />
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</section>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
{/* Confirmation Modal for Deleting */}
<Modal
isOpen={showConfirmDelete}
onOpenChange={setShowConfirmDelete}
isDismissable={false}
isKeyboardDismissDisabled={true}
>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
Delete Voucher
</ModalHeader>
<ModalBody>
<p>Are you sure you want to delete this voucher?</p>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
No
</Button>
<Button
color="danger"
onPress={() => {
deleteVoucher();
onClose();
}}
>
Yes
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</div>
);
}

View File

@@ -2,7 +2,8 @@ import { useEffect, useState } from 'react';
import config from '../config';
import instance from '../security/http';
import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, SortDescriptor, Button, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@nextui-org/react';
import { EmailIcon, TrashDeleteIcon } from '../icons';
import { EmailIcon, ImageIcon, TrashDeleteIcon } from '../icons';
import NextUIFormikSelect2 from '../components/NextUIFormikSelect2';
interface User {
id: string;
@@ -125,31 +126,15 @@ export default function Ranking() {
// Sort form data based on descriptor
const sortFormData = (list: FormData[], descriptor: SortDescriptor) => {
const { column, direction } = descriptor;
const { column } = descriptor;
if (column === "avgBill") {
return [...list].sort((a, b) =>
direction === "ascending" ? a.avgBill - b.avgBill : b.avgBill - a.avgBill
);
return [...list].sort((a, b) => a.avgBill - b.avgBill);
}
return list;
};
const handleSort = () => {
const { direction } = sortDescriptor;
const newDirection = direction === "ascending" ? "descending" : "ascending";
setSortDescriptor({ column: "avgBill", direction: newDirection });
};
const renderSortIndicator = () => {
if (sortDescriptor.column === "avgBill") {
return sortDescriptor.direction === "ascending" ? <span>&uarr;</span> : <span>&darr;</span>;
}
return null;
};
const sortedFormData = sortFormData(filteredFormData, sortDescriptor);
const combinedData: FormDataWithUser[] = sortedFormData.map((data) => {
@@ -162,6 +147,14 @@ export default function Ranking() {
};
});
const [isImageModalOpen, setIsImageModalOpen] = useState(false);
const [modalImageUrl, setModalImageUrl] = useState<string | null>(null);
const handleImageClick = (imageUrl: string) => {
setModalImageUrl(imageUrl);
setIsImageModalOpen(true);
};
const handleEmailClick = (email: string, name: string) => {
setSelectedUser({ email, name });
setIsEmailModalOpen(true);
@@ -241,58 +234,76 @@ export default function Ranking() {
}
};
const handleTownCouncilChange = (value: string) => {
setSelectedTownCouncil(value);
};
return (
<section className="flex flex-col items-center justify-center py-5">
<div className="flex justify-between w-full">
<p className="text-xl font-bold">Form Data</p>
{top3Users.length > 0 && (
<Button color="primary" onPress={handleGiveVouchers}>
Give Vouchers
</Button>
)}
<div className="flex flex-col gap-8 p-8">
<div className="flex justify-between items-center gap-5">
<div className="flex w-[500px]">
<p className="text-4xl font-bold">Home Bill Contest Form Data</p>
</div>
<div className="flex flex-row gap-4 ">
<div className="w-[200px]">
{townCouncils.length > 0 && (
<NextUIFormikSelect2
label="Town council"
placeholder="Choose towncouncil"
options={townCouncils.map((townCouncil) => ({
key: townCouncil,
label: townCouncil,
}))}
onChange={handleTownCouncilChange}
/>
)}
</div>
<div className="w-[130px]">
{top3Users.length > 0 && (
<Button color="primary" onPress={handleGiveVouchers} className="w-full">
Give Vouchers
</Button>
)}
</div>
</div>
</div>
<div className="gap-8 p-8">
{townCouncils.length > 0 && (
<select
value={selectedTownCouncil}
onChange={(e) => setSelectedTownCouncil(e.target.value)}
>
<option value="">All locations</option>
{townCouncils.map((townCouncil) => (
<option key={townCouncil} value={townCouncil}>
{townCouncil}
</option>
))}
</select>
)}
<div>
<Table aria-label="Form Data Table">
<TableHeader>
<TableColumn>User Name</TableColumn>
<TableColumn>User Email</TableColumn>
<TableColumn>Electrical Bill</TableColumn>
<TableColumn>Water Bill</TableColumn>
<TableColumn>Total Bill</TableColumn>
<TableColumn>Dependents</TableColumn>
<TableColumn onClick={handleSort}>
Average Bill {renderSortIndicator()}
<TableColumn>
Average Bill
</TableColumn>
<TableColumn>Bill Picture</TableColumn>
<TableColumn>Actions</TableColumn>
</TableHeader>
<TableBody>
{combinedData.map((data) => (
<TableRow key={data.id}>
<TableCell>{data.userName}</TableCell>
<TableCell>{data.userEmail}</TableCell>
<TableCell>${data.electricalBill.toFixed(2)}</TableCell>
<TableCell>${data.waterBill.toFixed(2)}</TableCell>
<TableCell>${data.totalBill.toFixed(2)}</TableCell>
<TableCell>{data.noOfDependents}</TableCell>
<TableCell>${data.avgBill.toFixed(2)}</TableCell>
<TableCell>
{data.billPicture && <img src={`${config.serverAddress}/hbcform/billPicture/${data.id}`} alt="bill picture" className="w-full" />}
{data.billPicture ? (
<Button isIconOnly variant="light" onPress={() => handleImageClick(`${config.serverAddress}/hbcform/billPicture/${data.id}`)}>
<ImageIcon />
</Button>
) : (
<Button isIconOnly variant="light">
<ImageIcon />
</Button>
)}
</TableCell>
<TableCell className="flex flex-row">
<Button isIconOnly variant="light" className="text-blue-500" onClick={() => handleEmailClick(data.userEmail, data.userName)}><EmailIcon /></Button>
<Button isIconOnly variant="light" color="danger" onClick={() => handleDeleteClick(data.id)}><TrashDeleteIcon /></Button>
@@ -344,6 +355,20 @@ export default function Ranking() {
)}
</ModalContent>
</Modal>
</section>
{/* Open Image Modal */}
<Modal
isOpen={isImageModalOpen}
onOpenChange={setIsImageModalOpen}
isDismissable={true}
>
<ModalContent>
<ModalBody>
{modalImageUrl && (
<img src={modalImageUrl} alt="Bill Picture" style={{ width: '100%', height: 'auto' }} />
)}
</ModalBody>
</ModalContent>
</Modal>
</div >
);
}