From c5a5a5f5742ca887f6dd57d6b910a554243c4746 Mon Sep 17 00:00:00 2001 From: Sachintha Lakmin <68807141+sachintha-lk@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:23:44 +0530 Subject: [PATCH 1/6] Add sponsor form modal. Create new sponsor basics working --- src/app/admin/dashboard/sponsors/page.tsx | 5 +- .../ui/modals/sponsor-add-update-modal.tsx | 167 ++++++++++++++---- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/src/app/admin/dashboard/sponsors/page.tsx b/src/app/admin/dashboard/sponsors/page.tsx index c1346c5..c7a6290 100644 --- a/src/app/admin/dashboard/sponsors/page.tsx +++ b/src/app/admin/dashboard/sponsors/page.tsx @@ -59,8 +59,9 @@ export default async function ManageSponsors() { - - +
+ +
diff --git a/src/components/ui/modals/sponsor-add-update-modal.tsx b/src/components/ui/modals/sponsor-add-update-modal.tsx index 2d06316..3e0f4e7 100644 --- a/src/components/ui/modals/sponsor-add-update-modal.tsx +++ b/src/components/ui/modals/sponsor-add-update-modal.tsx @@ -1,21 +1,86 @@ "use client" import React, { useState } from 'react'; +import { addSponsor } from '@/services/sponsors.service'; +import { SponsorLevel } from '@/interfaces/ISponsors'; function SponsorAddUpdateModal() { const [isOpen, setIsOpen] = useState(false); + const [sponsorName, setSponsorName] = useState(''); + const [sponsorLevel, setSponsorLevel] = useState(''); + const [sponsorImageFile, setSponsorImageFile] = useState(null); + const [sponsorImgURL, setSponsorImgURL] = useState(''); + // Handle file selection or drag-and-drop + const handleFileChange = (e: any) => { + e.preventDefault(); + + const file = e.target.files ? e.target.files[0] : e.dataTransfer.files[0]; + if (file) { + setSponsorImageFile(file); + } + }; + + // Toggle modal visibility const toggleModal = () => { + setSponsorName(''); + setSponsorLevel(''); + setSponsorImageFile(null); + setSponsorImgURL(''); + setIsOpen(!isOpen); - console.log('Modal toggled', isOpen); }; + + + const handleSubmit = async (e: React.FormEvent) => { + + e.preventDefault(); + + if (!sponsorName || !sponsorLevel || !sponsorImageFile) { + return; + } + + if (sponsorLevel !== 'Gold' && sponsorLevel !== 'Silver' && sponsorLevel !== 'Bronze') { + alert('Please select a valid sponsor level'); + return; + } + + // validate image file type + if (!sponsorImageFile.type.includes('image')) { + alert('Please select a valid image file'); + return; + } + + // validate image file size + if (sponsorImageFile.size > 1024 * 1024 * 2) { + alert('Please select an image file less than 2MB'); + return; + } + + // Upload image to firebase storage and get URL + // TODO implement image upload to firebase storage + const fakePlaceholderImgURL = "https://images.unsplash.com/photo-1726931467680-713bb3f432f5?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + const imgURL = fakePlaceholderImgURL; + + // Create new sponsor object + const newSponsor = { + name: sponsorName, + level: sponsorLevel as SponsorLevel, + imgURL, + }; + + // Add new sponsor to firestore + await addSponsor(newSponsor); + + } + return ( <> {isOpen && ( @@ -23,7 +88,8 @@ function SponsorAddUpdateModal() { id="crud-modal" tabIndex={-1} aria-hidden="true" - className="fixed inset-0 z-50 flex justify-center items-center w-full h-full bg-black bg-opacity-50"> + className="fixed inset-0 z-50 flex justify-center items-center w-full h-full bg-black bg-opacity-50" + >
@@ -41,7 +107,7 @@ function SponsorAddUpdateModal() {
-
+
@@ -52,49 +118,82 @@ function SponsorAddUpdateModal() { className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="Type sponsor name" required + onChange={(e) => setSponsorName(e.target.value)} />
-
- - -
- -
- +
+
- - + +
e.preventDefault()} + onClick={() => document.getElementById('image-upload')?.click()} // Trigger file input click on div click + > + {sponsorImageFile ? ( +
+ Uploaded Image + +
+ ) : ( +
+ +

Click to upload or drag and drop

+

SVG, PNG, JPG or GIF (MAX. 800x400px)

+
+ )} + +
+
-
-
+
)} From 45f0b0818fca124b23787fe72dbcc5bcbe48512f Mon Sep 17 00:00:00 2001 From: Sachintha Lakmin <68807141+sachintha-lk@users.noreply.github.com> Date: Tue, 12 Nov 2024 23:51:05 +0530 Subject: [PATCH 2/6] Add SponsorDeleteModal and refresh sponsor table after delete and update --- next.config.ts | 7 + src/app/admin/dashboard/sponsors/page.tsx | 167 ++++++++++++------ .../modals/sponsor-add-update-modal.tsx | 16 +- .../blocks/modals/sponsor-delete-modal.tsx | 120 +++++++++++++ 4 files changed, 248 insertions(+), 62 deletions(-) rename src/components/{ui => blocks}/modals/sponsor-add-update-modal.tsx (96%) create mode 100644 src/components/blocks/modals/sponsor-delete-modal.tsx diff --git a/next.config.ts b/next.config.ts index 71ed172..33c32fe 100644 --- a/next.config.ts +++ b/next.config.ts @@ -10,11 +10,18 @@ const nextConfig: NextConfig = { port: '', pathname: '/images/**', }, + // THESE ARE FOR TEMP TESTING TODO REMOVE LATER { protocol: 'https', hostname: 'fastly.picsum.photos', port: '', pathname: '/**', + }, + { + protocol: 'https', + hostname: 'images.unsplash.com', + port: '', + pathname: '/**', } ], }, diff --git a/src/app/admin/dashboard/sponsors/page.tsx b/src/app/admin/dashboard/sponsors/page.tsx index c7a6290..bb9c29f 100644 --- a/src/app/admin/dashboard/sponsors/page.tsx +++ b/src/app/admin/dashboard/sponsors/page.tsx @@ -1,69 +1,120 @@ +"use client" + import Image from "next/image"; -import { AdminDashboardLayout } from "../admin-dashboard-layout" +import { AdminDashboardLayout } from "../admin-dashboard-layout"; import { getSponsors } from "@/services/sponsors.service"; -import { ISponsor } from "@/interfaces/ISponsors"; -import SponsorAddUpdateModal from "@/components/ui/modals/sponsor-add-update-modal"; +import { ISponsor } from "@/interfaces/ISponsors"; +import SponsorAddUpdateModal from "@/components/blocks/modals/sponsor-add-update-modal"; +import { useEffect, useState } from "react"; +import SponsorDeleteModal from "@/components/blocks/modals/sponsor-delete-modal"; + +export default function ManageSponsors() { + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [sponsorToDelete, setItemToDelete] = useState(null); + const [sponsors, setSponsors] = useState([] as ISponsor[]); + const [refresh, setRefresh] = useState(false); // State to trigger re-fetch + + useEffect(() => { + getSponsors("All").then((data) => { + setSponsors(data); + } + ); + }, [refresh]); + + const refreshData = () => { + setRefresh(!refresh); // run useEffect again to refresh list + }; + + const handleDeleteClick = (sponsor: ISponsor) => { + setItemToDelete(sponsor); + setIsDeleteModalOpen(true); + refreshData(); + }; + + const closeDeleteModal = () => { + setIsDeleteModalOpen(false); + setItemToDelete(null); + }; + -export default async function ManageSponsors() { - - let sponsors: ISponsor[] = await getSponsors("All", 100); // implement pagination later for now 100 return ( -
-

Sponsors

-

Manage Sponsors here

- {/*

Deliver great service experiences fast - without the complexity of traditional ITSM solutions. Accelerate critical development work, eliminate toil, and deploy changes with ease.

*/} +
+

Sponsors

+

Manage Sponsors here

+ {/*

Deliver great service experiences fast - without the complexity of traditional ITSM solutions. Accelerate critical development work, eliminate toil, and deploy changes with ease.

*/}
- - - - - - - - - - - - {sponsors.map((sponsor) => ( - - - - - - - - ))} - -
- Sponsor name - - Image - - Sponsor Level - - Created Time - - Action -
- {sponsor.name} - - {`${sponsor.name} - - {sponsor.level} - - {sponsor.timestamp?.toDate().toLocaleString()} - - -
-
-
- -
- -
+ + + + + + + + + + + + {sponsors.map((sponsor) => ( + + + + + + + + ))} + +
+ Sponsor name + + Image + + Sponsor Level + + Created Time + + Action +
+ {sponsor.name} + + {`${sponsor.name} + {sponsor.level} + {sponsor.timestamp?.toDate().toLocaleString()} + + +
+
+
+ +
+ {isDeleteModalOpen && sponsorToDelete && ( + + )} +
); } diff --git a/src/components/ui/modals/sponsor-add-update-modal.tsx b/src/components/blocks/modals/sponsor-add-update-modal.tsx similarity index 96% rename from src/components/ui/modals/sponsor-add-update-modal.tsx rename to src/components/blocks/modals/sponsor-add-update-modal.tsx index 3e0f4e7..1bdb1d3 100644 --- a/src/components/ui/modals/sponsor-add-update-modal.tsx +++ b/src/components/blocks/modals/sponsor-add-update-modal.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react'; import { addSponsor } from '@/services/sponsors.service'; import { SponsorLevel } from '@/interfaces/ISponsors'; -function SponsorAddUpdateModal() { +function SponsorAddUpdateModal({onAddSponsor}: {onAddSponsor: () => void}) { const [isOpen, setIsOpen] = useState(false); const [sponsorName, setSponsorName] = useState(''); @@ -61,7 +61,6 @@ function SponsorAddUpdateModal() { // TODO implement image upload to firebase storage const fakePlaceholderImgURL = "https://images.unsplash.com/photo-1726931467680-713bb3f432f5?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" const imgURL = fakePlaceholderImgURL; - // Create new sponsor object const newSponsor = { name: sponsorName, @@ -70,7 +69,17 @@ function SponsorAddUpdateModal() { }; // Add new sponsor to firestore - await addSponsor(newSponsor); + addSponsor(newSponsor) + .then(() => { + console.log('Sponsor added successfully'); + onAddSponsor(); + toggleModal(); + }) + .catch((error) => { + console.error('Error adding sponsor: ', error); + alert('Error adding sponsor'); + toggleModal(); + }); } @@ -129,7 +138,6 @@ function SponsorAddUpdateModal() { value={sponsorLevel} onChange={(e) => setSponsorLevel(e.target.value)} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" - defaultValue="" required > diff --git a/src/components/blocks/modals/sponsor-delete-modal.tsx b/src/components/blocks/modals/sponsor-delete-modal.tsx new file mode 100644 index 0000000..c0a9292 --- /dev/null +++ b/src/components/blocks/modals/sponsor-delete-modal.tsx @@ -0,0 +1,120 @@ +"use client" +import React, { useState } from 'react'; +import { deleteSponsor } from '@/services/sponsors.service'; +import { ISponsor } from '@/interfaces/ISponsors'; + +function SponsorDeleteModal( + {sponsor, onClose, onDelete }: {sponsor: ISponsor, onClose: () => void, onDelete: () => void} +) { + + const [sponsorToDelete, setSponsorToDelete] = useState(sponsor); + + const toggleModal = () => { + setSponsorToDelete(null); + onClose(); + }; + + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (sponsorToDelete?.id) { + deleteSponsor(sponsorToDelete.id) + .then(() => { + console.log("Sponsor deleted successfully"); + onDelete(); + toggleModal(); + }) + .catch((error) => { + console.error("Error deleting sponsor: ", error); + alert("Error deleting sponsor"); + toggleModal(); + }); + + } else { + console.error("Sponsor ID is undefined"); + } + + } + + return ( + <> + + + ); +} + +export default SponsorDeleteModal; \ No newline at end of file From a7cc16887169a62d2db6eb546e7c138395192300 Mon Sep 17 00:00:00 2001 From: Sachintha Lakmin <68807141+sachintha-lk@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:23:44 +0530 Subject: [PATCH 3/6] Add sponsor form modal. Create new sponsor basics working --- src/app/admin/dashboard/sponsors/page.tsx | 5 +- .../ui/modals/sponsor-add-update-modal.tsx | 167 ++++++++++++++---- 2 files changed, 136 insertions(+), 36 deletions(-) diff --git a/src/app/admin/dashboard/sponsors/page.tsx b/src/app/admin/dashboard/sponsors/page.tsx index c1346c5..c7a6290 100644 --- a/src/app/admin/dashboard/sponsors/page.tsx +++ b/src/app/admin/dashboard/sponsors/page.tsx @@ -59,8 +59,9 @@ export default async function ManageSponsors() { - - +
+ +
diff --git a/src/components/ui/modals/sponsor-add-update-modal.tsx b/src/components/ui/modals/sponsor-add-update-modal.tsx index 2d06316..3e0f4e7 100644 --- a/src/components/ui/modals/sponsor-add-update-modal.tsx +++ b/src/components/ui/modals/sponsor-add-update-modal.tsx @@ -1,21 +1,86 @@ "use client" import React, { useState } from 'react'; +import { addSponsor } from '@/services/sponsors.service'; +import { SponsorLevel } from '@/interfaces/ISponsors'; function SponsorAddUpdateModal() { const [isOpen, setIsOpen] = useState(false); + const [sponsorName, setSponsorName] = useState(''); + const [sponsorLevel, setSponsorLevel] = useState(''); + const [sponsorImageFile, setSponsorImageFile] = useState(null); + const [sponsorImgURL, setSponsorImgURL] = useState(''); + // Handle file selection or drag-and-drop + const handleFileChange = (e: any) => { + e.preventDefault(); + + const file = e.target.files ? e.target.files[0] : e.dataTransfer.files[0]; + if (file) { + setSponsorImageFile(file); + } + }; + + // Toggle modal visibility const toggleModal = () => { + setSponsorName(''); + setSponsorLevel(''); + setSponsorImageFile(null); + setSponsorImgURL(''); + setIsOpen(!isOpen); - console.log('Modal toggled', isOpen); }; + + + const handleSubmit = async (e: React.FormEvent) => { + + e.preventDefault(); + + if (!sponsorName || !sponsorLevel || !sponsorImageFile) { + return; + } + + if (sponsorLevel !== 'Gold' && sponsorLevel !== 'Silver' && sponsorLevel !== 'Bronze') { + alert('Please select a valid sponsor level'); + return; + } + + // validate image file type + if (!sponsorImageFile.type.includes('image')) { + alert('Please select a valid image file'); + return; + } + + // validate image file size + if (sponsorImageFile.size > 1024 * 1024 * 2) { + alert('Please select an image file less than 2MB'); + return; + } + + // Upload image to firebase storage and get URL + // TODO implement image upload to firebase storage + const fakePlaceholderImgURL = "https://images.unsplash.com/photo-1726931467680-713bb3f432f5?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + const imgURL = fakePlaceholderImgURL; + + // Create new sponsor object + const newSponsor = { + name: sponsorName, + level: sponsorLevel as SponsorLevel, + imgURL, + }; + + // Add new sponsor to firestore + await addSponsor(newSponsor); + + } + return ( <> {isOpen && ( @@ -23,7 +88,8 @@ function SponsorAddUpdateModal() { id="crud-modal" tabIndex={-1} aria-hidden="true" - className="fixed inset-0 z-50 flex justify-center items-center w-full h-full bg-black bg-opacity-50"> + className="fixed inset-0 z-50 flex justify-center items-center w-full h-full bg-black bg-opacity-50" + >
@@ -41,7 +107,7 @@ function SponsorAddUpdateModal() {
-
+
@@ -52,49 +118,82 @@ function SponsorAddUpdateModal() { className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="Type sponsor name" required + onChange={(e) => setSponsorName(e.target.value)} />
-
- - -
- -
- +
+
- - + +
e.preventDefault()} + onClick={() => document.getElementById('image-upload')?.click()} // Trigger file input click on div click + > + {sponsorImageFile ? ( +
+ Uploaded Image + +
+ ) : ( +
+ +

Click to upload or drag and drop

+

SVG, PNG, JPG or GIF (MAX. 800x400px)

+
+ )} + +
+
-
-
+
)} From 389bbdd4ad0f0b74faac2797a936dd7363671a7f Mon Sep 17 00:00:00 2001 From: Sachintha Lakmin <68807141+sachintha-lk@users.noreply.github.com> Date: Tue, 12 Nov 2024 23:51:05 +0530 Subject: [PATCH 4/6] Add SponsorDeleteModal and refresh sponsor table after delete and update --- next.config.ts | 7 + src/app/admin/dashboard/sponsors/page.tsx | 167 ++++++++++++------ .../modals/sponsor-add-update-modal.tsx | 16 +- .../blocks/modals/sponsor-delete-modal.tsx | 120 +++++++++++++ 4 files changed, 248 insertions(+), 62 deletions(-) rename src/components/{ui => blocks}/modals/sponsor-add-update-modal.tsx (96%) create mode 100644 src/components/blocks/modals/sponsor-delete-modal.tsx diff --git a/next.config.ts b/next.config.ts index 71ed172..33c32fe 100644 --- a/next.config.ts +++ b/next.config.ts @@ -10,11 +10,18 @@ const nextConfig: NextConfig = { port: '', pathname: '/images/**', }, + // THESE ARE FOR TEMP TESTING TODO REMOVE LATER { protocol: 'https', hostname: 'fastly.picsum.photos', port: '', pathname: '/**', + }, + { + protocol: 'https', + hostname: 'images.unsplash.com', + port: '', + pathname: '/**', } ], }, diff --git a/src/app/admin/dashboard/sponsors/page.tsx b/src/app/admin/dashboard/sponsors/page.tsx index c7a6290..bb9c29f 100644 --- a/src/app/admin/dashboard/sponsors/page.tsx +++ b/src/app/admin/dashboard/sponsors/page.tsx @@ -1,69 +1,120 @@ +"use client" + import Image from "next/image"; -import { AdminDashboardLayout } from "../admin-dashboard-layout" +import { AdminDashboardLayout } from "../admin-dashboard-layout"; import { getSponsors } from "@/services/sponsors.service"; -import { ISponsor } from "@/interfaces/ISponsors"; -import SponsorAddUpdateModal from "@/components/ui/modals/sponsor-add-update-modal"; +import { ISponsor } from "@/interfaces/ISponsors"; +import SponsorAddUpdateModal from "@/components/blocks/modals/sponsor-add-update-modal"; +import { useEffect, useState } from "react"; +import SponsorDeleteModal from "@/components/blocks/modals/sponsor-delete-modal"; + +export default function ManageSponsors() { + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [sponsorToDelete, setItemToDelete] = useState(null); + const [sponsors, setSponsors] = useState([] as ISponsor[]); + const [refresh, setRefresh] = useState(false); // State to trigger re-fetch + + useEffect(() => { + getSponsors("All").then((data) => { + setSponsors(data); + } + ); + }, [refresh]); + + const refreshData = () => { + setRefresh(!refresh); // run useEffect again to refresh list + }; + + const handleDeleteClick = (sponsor: ISponsor) => { + setItemToDelete(sponsor); + setIsDeleteModalOpen(true); + refreshData(); + }; + + const closeDeleteModal = () => { + setIsDeleteModalOpen(false); + setItemToDelete(null); + }; + -export default async function ManageSponsors() { - - let sponsors: ISponsor[] = await getSponsors("All", 100); // implement pagination later for now 100 return ( -
-

Sponsors

-

Manage Sponsors here

- {/*

Deliver great service experiences fast - without the complexity of traditional ITSM solutions. Accelerate critical development work, eliminate toil, and deploy changes with ease.

*/} +
+

Sponsors

+

Manage Sponsors here

+ {/*

Deliver great service experiences fast - without the complexity of traditional ITSM solutions. Accelerate critical development work, eliminate toil, and deploy changes with ease.

*/}
- - - - - - - - - - - - {sponsors.map((sponsor) => ( - - - - - - - - ))} - -
- Sponsor name - - Image - - Sponsor Level - - Created Time - - Action -
- {sponsor.name} - - {`${sponsor.name} - - {sponsor.level} - - {sponsor.timestamp?.toDate().toLocaleString()} - - -
-
-
- -
- -
+ + + + + + + + + + + + {sponsors.map((sponsor) => ( + + + + + + + + ))} + +
+ Sponsor name + + Image + + Sponsor Level + + Created Time + + Action +
+ {sponsor.name} + + {`${sponsor.name} + {sponsor.level} + {sponsor.timestamp?.toDate().toLocaleString()} + + +
+
+
+ +
+ {isDeleteModalOpen && sponsorToDelete && ( + + )} +
); } diff --git a/src/components/ui/modals/sponsor-add-update-modal.tsx b/src/components/blocks/modals/sponsor-add-update-modal.tsx similarity index 96% rename from src/components/ui/modals/sponsor-add-update-modal.tsx rename to src/components/blocks/modals/sponsor-add-update-modal.tsx index 3e0f4e7..1bdb1d3 100644 --- a/src/components/ui/modals/sponsor-add-update-modal.tsx +++ b/src/components/blocks/modals/sponsor-add-update-modal.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react'; import { addSponsor } from '@/services/sponsors.service'; import { SponsorLevel } from '@/interfaces/ISponsors'; -function SponsorAddUpdateModal() { +function SponsorAddUpdateModal({onAddSponsor}: {onAddSponsor: () => void}) { const [isOpen, setIsOpen] = useState(false); const [sponsorName, setSponsorName] = useState(''); @@ -61,7 +61,6 @@ function SponsorAddUpdateModal() { // TODO implement image upload to firebase storage const fakePlaceholderImgURL = "https://images.unsplash.com/photo-1726931467680-713bb3f432f5?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" const imgURL = fakePlaceholderImgURL; - // Create new sponsor object const newSponsor = { name: sponsorName, @@ -70,7 +69,17 @@ function SponsorAddUpdateModal() { }; // Add new sponsor to firestore - await addSponsor(newSponsor); + addSponsor(newSponsor) + .then(() => { + console.log('Sponsor added successfully'); + onAddSponsor(); + toggleModal(); + }) + .catch((error) => { + console.error('Error adding sponsor: ', error); + alert('Error adding sponsor'); + toggleModal(); + }); } @@ -129,7 +138,6 @@ function SponsorAddUpdateModal() { value={sponsorLevel} onChange={(e) => setSponsorLevel(e.target.value)} className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" - defaultValue="" required > diff --git a/src/components/blocks/modals/sponsor-delete-modal.tsx b/src/components/blocks/modals/sponsor-delete-modal.tsx new file mode 100644 index 0000000..c0a9292 --- /dev/null +++ b/src/components/blocks/modals/sponsor-delete-modal.tsx @@ -0,0 +1,120 @@ +"use client" +import React, { useState } from 'react'; +import { deleteSponsor } from '@/services/sponsors.service'; +import { ISponsor } from '@/interfaces/ISponsors'; + +function SponsorDeleteModal( + {sponsor, onClose, onDelete }: {sponsor: ISponsor, onClose: () => void, onDelete: () => void} +) { + + const [sponsorToDelete, setSponsorToDelete] = useState(sponsor); + + const toggleModal = () => { + setSponsorToDelete(null); + onClose(); + }; + + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (sponsorToDelete?.id) { + deleteSponsor(sponsorToDelete.id) + .then(() => { + console.log("Sponsor deleted successfully"); + onDelete(); + toggleModal(); + }) + .catch((error) => { + console.error("Error deleting sponsor: ", error); + alert("Error deleting sponsor"); + toggleModal(); + }); + + } else { + console.error("Sponsor ID is undefined"); + } + + } + + return ( + <> + + + ); +} + +export default SponsorDeleteModal; \ No newline at end of file From 588a8657b03b8fbbfb0b899081dbb7f8bcb1c046 Mon Sep 17 00:00:00 2001 From: Sachintha Lakmin <68807141+sachintha-lk@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:35:51 +0530 Subject: [PATCH 5/6] integrated img upload in adding sponsors --- next.config.ts | 6 ++ .../modals/sponsor-add-update-modal.tsx | 56 +++++++++++-------- .../blocks/modals/sponsor-delete-modal.tsx | 42 +++++++++++++- src/services/firebaseStorage.service.ts | 13 ++++- 4 files changed, 93 insertions(+), 24 deletions(-) diff --git a/next.config.ts b/next.config.ts index 33c32fe..85a986b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -10,6 +10,12 @@ const nextConfig: NextConfig = { port: '', pathname: '/images/**', }, + { + protocol: 'https', + hostname: 'firebasestorage.googleapis.com', + port: '', + pathname: '/**', + }, // THESE ARE FOR TEMP TESTING TODO REMOVE LATER { protocol: 'https', diff --git a/src/components/blocks/modals/sponsor-add-update-modal.tsx b/src/components/blocks/modals/sponsor-add-update-modal.tsx index 1bdb1d3..ecc3106 100644 --- a/src/components/blocks/modals/sponsor-add-update-modal.tsx +++ b/src/components/blocks/modals/sponsor-add-update-modal.tsx @@ -2,6 +2,8 @@ import React, { useState } from 'react'; import { addSponsor } from '@/services/sponsors.service'; import { SponsorLevel } from '@/interfaces/ISponsors'; +import { addFile } from '@/services/firebaseStorage.service'; +import { getDownloadURL } from 'firebase/storage'; function SponsorAddUpdateModal({onAddSponsor}: {onAddSponsor: () => void}) { @@ -58,28 +60,38 @@ function SponsorAddUpdateModal({onAddSponsor}: {onAddSponsor: () => void}) { } // Upload image to firebase storage and get URL - // TODO implement image upload to firebase storage - const fakePlaceholderImgURL = "https://images.unsplash.com/photo-1726931467680-713bb3f432f5?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" - const imgURL = fakePlaceholderImgURL; - // Create new sponsor object - const newSponsor = { - name: sponsorName, - level: sponsorLevel as SponsorLevel, - imgURL, - }; - - // Add new sponsor to firestore - addSponsor(newSponsor) - .then(() => { - console.log('Sponsor added successfully'); - onAddSponsor(); - toggleModal(); - }) - .catch((error) => { - console.error('Error adding sponsor: ', error); - alert('Error adding sponsor'); - toggleModal(); - }); + addFile(sponsorImageFile).then((ref) => { + if (ref) { + console.log("StorageReference :",ref); + getDownloadURL(ref).then((url) => { + console.log('Image URL: ', url); + + // Create new sponsor object + const newSponsor = { + name: sponsorName, + level: sponsorLevel as SponsorLevel, + imgURL: url, + }; + + // Add new sponsor to firestore + addSponsor(newSponsor) + .then(() => { + console.log('Sponsor added successfully', newSponsor); + onAddSponsor(); + toggleModal(); + }) + .catch((error) => { + console.error('Error adding sponsor: ', error); + alert('Error adding sponsor'); + toggleModal(); + }); + }); + } + }).catch((error) => { + console.error('Error uploading file: ', error); + alert('Error uploading file'); + return; + }); } diff --git a/src/components/blocks/modals/sponsor-delete-modal.tsx b/src/components/blocks/modals/sponsor-delete-modal.tsx index c0a9292..8647dd4 100644 --- a/src/components/blocks/modals/sponsor-delete-modal.tsx +++ b/src/components/blocks/modals/sponsor-delete-modal.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { deleteSponsor } from '@/services/sponsors.service'; import { ISponsor } from '@/interfaces/ISponsors'; +// import { deleteFile , getFileReferenceByUrl } from '@/services/firebaseStorage.service'; function SponsorDeleteModal( {sponsor, onClose, onDelete }: {sponsor: ISponsor, onClose: () => void, onDelete: () => void} @@ -17,7 +18,46 @@ function SponsorDeleteModal( const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - + + // if (sponsorToDelete?.id) { + // if img url is not null, delete the image from storage + // if (sponsorToDelete.imgURL) { + // getFileReferenceByUrl(sponsorToDelete.imgURL) + // .then((fileRef) => { + // if (fileRef) { + // deleteFile(fileRef) + // .then((res) => { + // if (res) { + // console.log("Image deleted successfully", sponsorToDelete.imgURL); + + // delete the sponsor from the database + // if (sponsorToDelete?.id) { + // deleteSponsor(sponsorToDelete.id) + // .then(() => { + // console.log("Sponsor deleted successfully"); + // onDelete(); + // toggleModal(); + // }) + // .catch((error) => { + // console.error("Error deleting sponsor: ", error); + // alert("Error deleting sponsor"); + // toggleModal(); + // }); + // } + // } else { + // console.error("Error deleting image"); + // } + // }); + // } else { + // console.error("File reference is null"); + // } + // }); + + + // } else { + // console.error("Sponsor ID is undefined"); + // } + if (sponsorToDelete?.id) { deleteSponsor(sponsorToDelete.id) .then(() => { diff --git a/src/services/firebaseStorage.service.ts b/src/services/firebaseStorage.service.ts index 9dd2421..bfaa67d 100644 --- a/src/services/firebaseStorage.service.ts +++ b/src/services/firebaseStorage.service.ts @@ -30,4 +30,15 @@ export const deleteFile = async (fileRef:StorageReference):Promise=>{ return false; }); return false; -} \ No newline at end of file +} + +// export const getFileReferenceByUrl = async (fileUrl: string): Promise => { +// // Extract the file path from the download URL +// const url = new URL(fileUrl); +// const path = decodeURIComponent(url.pathname.split('/o/')[1].split('?')[0]); + +// // Create a reference to the file using the extracted path +// const fileRef = ref(storage, path); + +// return fileRef; +// } From 4d81afa4abab5f7cebc93d1f537eda2c9bc34df9 Mon Sep 17 00:00:00 2001 From: Sachintha Lakmin <68807141+sachintha-lk@users.noreply.github.com> Date: Wed, 13 Nov 2024 08:48:03 +0530 Subject: [PATCH 6/6] Integrate with image deletion logic in SponsorDeleteModal --- src/app/admin/dashboard/sponsors/page.tsx | 10 +- .../blocks/modals/sponsor-delete-modal.tsx | 113 +++++++++--------- src/services/firebaseStorage.service.ts | 19 ++- 3 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/app/admin/dashboard/sponsors/page.tsx b/src/app/admin/dashboard/sponsors/page.tsx index bb9c29f..99039af 100644 --- a/src/app/admin/dashboard/sponsors/page.tsx +++ b/src/app/admin/dashboard/sponsors/page.tsx @@ -67,7 +67,7 @@ export default function ManageSponsors() { - {sponsors.map((sponsor) => ( + {sponsors.length > 0 && sponsors.map((sponsor) => ( ))} + + {sponsors.length === 0 && ( + + + No sponsors found + + + )} diff --git a/src/components/blocks/modals/sponsor-delete-modal.tsx b/src/components/blocks/modals/sponsor-delete-modal.tsx index 8647dd4..7b2e098 100644 --- a/src/components/blocks/modals/sponsor-delete-modal.tsx +++ b/src/components/blocks/modals/sponsor-delete-modal.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { deleteSponsor } from '@/services/sponsors.service'; import { ISponsor } from '@/interfaces/ISponsors'; -// import { deleteFile , getFileReferenceByUrl } from '@/services/firebaseStorage.service'; +import { deleteFile , getFileReferenceByUrl } from '@/services/firebaseStorage.service'; function SponsorDeleteModal( {sponsor, onClose, onDelete }: {sponsor: ISponsor, onClose: () => void, onDelete: () => void} @@ -19,62 +19,67 @@ function SponsorDeleteModal( const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - // if (sponsorToDelete?.id) { - // if img url is not null, delete the image from storage - // if (sponsorToDelete.imgURL) { - // getFileReferenceByUrl(sponsorToDelete.imgURL) - // .then((fileRef) => { - // if (fileRef) { - // deleteFile(fileRef) - // .then((res) => { - // if (res) { - // console.log("Image deleted successfully", sponsorToDelete.imgURL); - - // delete the sponsor from the database - // if (sponsorToDelete?.id) { - // deleteSponsor(sponsorToDelete.id) - // .then(() => { - // console.log("Sponsor deleted successfully"); - // onDelete(); - // toggleModal(); - // }) - // .catch((error) => { - // console.error("Error deleting sponsor: ", error); - // alert("Error deleting sponsor"); - // toggleModal(); - // }); - // } - // } else { - // console.error("Error deleting image"); - // } - // }); - // } else { - // console.error("File reference is null"); - // } - // }); - - - // } else { - // console.error("Sponsor ID is undefined"); - // } - if (sponsorToDelete?.id) { - deleteSponsor(sponsorToDelete.id) - .then(() => { - console.log("Sponsor deleted successfully"); - onDelete(); - toggleModal(); - }) - .catch((error) => { - console.error("Error deleting sponsor: ", error); - alert("Error deleting sponsor"); - toggleModal(); - }); + // if img url is not null, delete the image from storage and starts with firebasestorage.googleapis.com + if (sponsorToDelete.imgURL && sponsorToDelete.imgURL !== "" && sponsorToDelete.imgURL.startsWith("https://firebasestorage.googleapis.com")) { + getFileReferenceByUrl(sponsorToDelete.imgURL) + .then((fileRef) => { + if (fileRef) { + deleteFile(fileRef) + .then((res) => { + console.log("Res", res); + if (res == true) { + console.log("Image deleted successfully", sponsorToDelete.imgURL); + + // delete the sponsor from the database + if (sponsorToDelete?.id) { + deleteSponsor(sponsorToDelete.id) + .then(() => { + console.log("Sponsor deleted successfully"); + onDelete(); + toggleModal(); + }) + .catch((error) => { + console.error("Error deleting sponsor: ", error); + alert("Error deleting sponsor"); + toggleModal(); + }); + } + } else { + console.error("Error deleting image"); + } + }).catch((error) => { + console.error("Error deleting image: ", error); + } + ); + } else { + console.error("File reference is null"); + } + }); + + + } else { + console.error("Sponsor ID is undefined"); + } + + // if (sponsorToDelete?.id) { + // deleteSponsor(sponsorToDelete.id) + // .then(() => { + // console.log("Sponsor deleted successfully"); + // onDelete(); + // toggleModal(); + // }) + // .catch((error) => { + // console.error("Error deleting sponsor: ", error); + // alert("Error deleting sponsor"); + // toggleModal(); + // }); - } else { - console.error("Sponsor ID is undefined"); - } + // } else { + // console.error("Sponsor ID is undefined"); + // } + } } return ( diff --git a/src/services/firebaseStorage.service.ts b/src/services/firebaseStorage.service.ts index bfaa67d..e8a8e7d 100644 --- a/src/services/firebaseStorage.service.ts +++ b/src/services/firebaseStorage.service.ts @@ -22,23 +22,22 @@ export const addFile = async (file: Blob | Uint8Array | ArrayBuffer):Promise=>{ - await deleteObject(fileRef).then(() => { + const status = await deleteObject(fileRef).then(() => { console.log('File deleted'); return true; }).catch((error) => { console.error('Error deleting file: ', error); return false; }); - return false; + return status; } -// export const getFileReferenceByUrl = async (fileUrl: string): Promise => { -// // Extract the file path from the download URL -// const url = new URL(fileUrl); -// const path = decodeURIComponent(url.pathname.split('/o/')[1].split('?')[0]); +export const getFileReferenceByUrl = async (fileUrl: string): Promise => { + const url = new URL(fileUrl); + const path = decodeURIComponent(url.pathname.split('/o/')[1].split('?')[0]); -// // Create a reference to the file using the extracted path -// const fileRef = ref(storage, path); + // Create a reference to the file using the extracted path + const fileRef = ref(storage, path); -// return fileRef; -// } + return fileRef; +}