diff --git a/src/components/PaymentAssetManager.tsx b/src/components/PaymentAssetManager.tsx index 65516e9e..db6862cf 100644 --- a/src/components/PaymentAssetManager.tsx +++ b/src/components/PaymentAssetManager.tsx @@ -1,156 +1,143 @@ -import React, { ChangeEvent } from "react"; -import { BsUpload, BsDownload, BsTrash, BsArrowRepeat } from "react-icons/bs"; -import { FilesStorage } from "@/interfaces/storage.files"; +import React, {ChangeEvent} from "react"; +import {BsUpload, BsDownload, BsTrash, BsArrowRepeat} from "react-icons/bs"; +import {FilesStorage} from "@/interfaces/storage.files"; import axios from "axios"; interface Asset { - file: string | File; - complete: boolean; + file: string | File; + complete: boolean; } const PaymentAssetManager = (props: { - asset: string | undefined; - permissions: "read" | "write"; - type: FilesStorage; - paymentId: string; + asset: string | undefined; + permissions: "read" | "write"; + type: FilesStorage; + reload: () => void; + paymentId: string; }) => { - const { asset, permissions, type, paymentId } = props; + const {asset, permissions, type, paymentId} = props; - const fileInputRef = React.useRef(null); - const fileInputReplaceRef = React.useRef(null); + const fileInputRef = React.useRef(null); + const fileInputReplaceRef = React.useRef(null); - const [managingAsset, setManagingAsset] = React.useState({ - file: asset || "", - complete: asset ? true : false, - }); + const [managingAsset, setManagingAsset] = React.useState({ + file: asset || "", + complete: asset ? true : false, + }); - const { file, complete } = managingAsset; + const {file, complete} = managingAsset; - const deleteAsset = () => { - if (confirm("Are you sure you want to delete this document?")) { - axios - .delete(`/api/payments/files/${type}/${paymentId}`) - .then((response) => { - if (response.status === 200) { - console.log("File deleted successfully!"); - setManagingAsset({ - file: "", - complete: false, - }); - return; - } + const deleteAsset = () => { + if (confirm("Are you sure you want to delete this document?")) { + axios + .delete(`/api/payments/files/${type}/${paymentId}`) + .then((response) => { + if (response.status === 200) { + console.log("File deleted successfully!"); + setManagingAsset({ + file: "", + complete: false, + }); + return; + } - console.error("File deletion failed"); - }) - .catch((error) => { - console.error("Error occurred during file deletion:", error); - }); - } - }; + console.error("File deletion failed"); + }) + .catch((error) => { + console.error("Error occurred during file deletion:", error); + }) + .finally(props.reload); + } + }; - const renderFileInput = ( - onChange: any, - ref: React.RefObject - ) => ( - - ); + const renderFileInput = (onChange: any, ref: React.RefObject) => ( + + ); - const handleFileChange = async (e: Event, method: "post" | "patch") => { - const newFile = (e.target as HTMLInputElement).files?.[0]; - if (newFile) { - setManagingAsset({ - file: newFile, - complete: false, - }); + const handleFileChange = async (e: Event, method: "post" | "patch") => { + const newFile = (e.target as HTMLInputElement).files?.[0]; + if (newFile) { + setManagingAsset({ + file: newFile, + complete: false, + }); - const formData = new FormData(); - formData.append("file", newFile); + const formData = new FormData(); + formData.append("file", newFile); - axios[method](`/api/payments/files/${type}/${paymentId}`, formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }) - .then((response) => { - if (response.status === 200) { - console.log("File uploaded successfully!"); - console.log("Uploaded File URL:", response.data.ref); - // Further actions upon successful upload - setManagingAsset({ - file: response.data.ref, - complete: true, - }); - return; - } + axios[method](`/api/payments/files/${type}/${paymentId}`, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) + .then((response) => { + if (response.status === 200) { + console.log("File uploaded successfully!"); + console.log("Uploaded File URL:", response.data.ref); + // Further actions upon successful upload + setManagingAsset({ + file: response.data.ref, + complete: true, + }); + return; + } - console.error("File upload failed"); - }) - .catch((error) => { - console.error("Error occurred during file upload:", error); - }); - } - }; + console.error("File upload failed"); + }) + .catch((error) => { + console.error("Error occurred during file upload:", error); + }) + .finally(props.reload); + } + }; - const downloadAsset = () => { - axios - .get(`/api/payments/files/${type}/${paymentId}`) - .then((response) => { - if (response.status === 200) { - console.log("Uploaded File URL:", response.data.url); - const link = document.createElement("a"); - link.download = response.data.filename; - link.href = response.data.url; - link.click(); - return; - } + const downloadAsset = () => { + axios + .get(`/api/payments/files/${type}/${paymentId}`) + .then((response) => { + if (response.status === 200) { + console.log("Uploaded File URL:", response.data.url); + const link = document.createElement("a"); + link.download = response.data.filename; + link.href = response.data.url; + link.click(); + return; + } - console.error("Failed to download file"); - }) - .catch((error) => { - console.error("Error occurred during file upload:", error); - }); - }; + console.error("Failed to download file"); + }) + .catch((error) => { + console.error("Error occurred during file upload:", error); + }); + }; - if (permissions === "read") { - if (file) return ; - return null; - } + if (permissions === "read") { + if (file) return ; + return null; + } - if (file) { - if (complete) { - return ( - <> - - fileInputReplaceRef.current?.click()} /> - - {renderFileInput( - (e: Event) => handleFileChange(e, "patch"), - fileInputReplaceRef - )} - {renderFileInput( - (e: Event) => handleFileChange(e, "post"), - fileInputRef - )} - - ); - } + if (file) { + if (complete) { + return ( + <> + + fileInputReplaceRef.current?.click()} /> + + {renderFileInput((e: Event) => handleFileChange(e, "patch"), fileInputReplaceRef)} + {renderFileInput((e: Event) => handleFileChange(e, "post"), fileInputRef)} + + ); + } - return ; - } + return ; + } - return ( - <> - fileInputRef.current?.click()} /> - {renderFileInput((e: Event) => handleFileChange(e, "post"), fileInputRef)} - - ); + return ( + <> + fileInputRef.current?.click()} /> + {renderFileInput((e: Event) => handleFileChange(e, "post"), fileInputRef)} + + ); }; export default PaymentAssetManager; diff --git a/src/pages/api/payments/files/[type]/[paymentId].ts b/src/pages/api/payments/files/[type]/[paymentId].ts index 88c4fa24..2d07b439 100644 --- a/src/pages/api/payments/files/[type]/[paymentId].ts +++ b/src/pages/api/payments/files/[type]/[paymentId].ts @@ -1,194 +1,181 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from "next"; -import { app, storage } from "@/firebase"; -import { - getFirestore, - getDoc, - doc, - updateDoc, - deleteField, -} from "firebase/firestore"; -import { withIronSessionApiRoute } from "iron-session/next"; -import { sessionOptions } from "@/lib/session"; -import { FilesStorage } from "@/interfaces/storage.files"; +import type {NextApiRequest, NextApiResponse} from "next"; +import {app, storage} from "@/firebase"; +import {getFirestore, getDoc, doc, updateDoc, deleteField, setDoc} from "firebase/firestore"; +import {withIronSessionApiRoute} from "iron-session/next"; +import {sessionOptions} from "@/lib/session"; +import {FilesStorage} from "@/interfaces/storage.files"; -import { Payment } from "@/interfaces/paypal"; +import {Payment} from "@/interfaces/paypal"; import fs from "fs"; -import { - ref, - uploadBytes, - deleteObject, - getDownloadURL, -} from "firebase/storage"; +import {ref, uploadBytes, deleteObject, getDownloadURL} from "firebase/storage"; import formidable from "formidable-serverless"; const db = getFirestore(app); const getPaymentField = (type: FilesStorage) => { - switch (type) { - case "commission": - return "commissionTransfer"; - case "corporate": - return "corporateTransfer"; - default: - return null; - } + switch (type) { + case "commission": + return "commissionTransfer"; + case "corporate": + return "corporateTransfer"; + default: + return null; + } }; -const handleDelete = async ( - paymentId: string, - paymentField: "commissionTransfer" | "corporateTransfer" -) => { - const paymentRef = doc(db, "payments", paymentId); - const paymentDoc = await getDoc(paymentRef); - const { [paymentField]: paymentFieldPath } = paymentDoc.data() as Payment; - // Create a reference to the file to delete - const documentRef = ref(storage, paymentFieldPath); - await deleteObject(documentRef); - await updateDoc(paymentRef, { - [paymentField]: deleteField(), - }); +const handleDelete = async (paymentId: string, paymentField: "commissionTransfer" | "corporateTransfer") => { + const paymentRef = doc(db, "payments", paymentId); + const paymentDoc = await getDoc(paymentRef); + const {[paymentField]: paymentFieldPath} = paymentDoc.data() as Payment; + // Create a reference to the file to delete + const documentRef = ref(storage, paymentFieldPath); + await deleteObject(documentRef); + await updateDoc(paymentRef, { + [paymentField]: deleteField(), + isPaid: false, + }); }; -const handleUpload = async ( - req: NextApiRequest, - paymentId: string, - paymentField: "commissionTransfer" | "corporateTransfer" -) => - new Promise((resolve, reject) => { - const form = formidable({ keepExtensions: true }); - form.parse(req, async (err: any, fields: any, files: any) => { - if (err) { - reject(err); - return; - } - try { - const { file } = files; - const fileName = Date.now() + "-" + file.name; - const fileRef = ref(storage, fileName); +const handleUpload = async (req: NextApiRequest, paymentId: string, paymentField: "commissionTransfer" | "corporateTransfer") => + new Promise((resolve, reject) => { + const form = formidable({keepExtensions: true}); + form.parse(req, async (err: any, fields: any, files: any) => { + if (err) { + reject(err); + return; + } + try { + const {file} = files; + const fileName = Date.now() + "-" + file.name; + const fileRef = ref(storage, fileName); - const binary = fs.readFileSync(file.path).buffer; - const snapshot = await uploadBytes(fileRef, binary); - fs.rmSync(file.path); + const binary = fs.readFileSync(file.path).buffer; + const snapshot = await uploadBytes(fileRef, binary); + fs.rmSync(file.path); - const paymentRef = doc(db, "payments", paymentId); + const paymentRef = doc(db, "payments", paymentId); - await updateDoc(paymentRef, { - [paymentField]: snapshot.ref.fullPath, - }); - resolve(snapshot.ref.fullPath); - } catch (err) { - reject(err); - } - }); - }); + await updateDoc(paymentRef, { + [paymentField]: snapshot.ref.fullPath, + }); + resolve(snapshot.ref.fullPath); + } catch (err) { + reject(err); + } + }); + }); export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!req.session.user) { - res.status(401).json({ ok: false }); - return; - } + if (!req.session.user) { + res.status(401).json({ok: false}); + return; + } - if (req.method === "GET") return await get(req, res); - if (req.method === "POST") return await post(req, res); - if (req.method === "DELETE") return await del(req, res); - if (req.method === "PATCH") return await patch(req, res); + if (req.method === "GET") return await get(req, res); + if (req.method === "POST") return await post(req, res); + if (req.method === "DELETE") return await del(req, res); + if (req.method === "PATCH") return await patch(req, res); - res.status(404).json(undefined); + res.status(404).json(undefined); } async function get(req: NextApiRequest, res: NextApiResponse) { - const { type, paymentId } = req.query as { - type: FilesStorage; - paymentId: string; - }; - const paymentField = getPaymentField(type); + const {type, paymentId} = req.query as { + type: FilesStorage; + paymentId: string; + }; + const paymentField = getPaymentField(type); - if (paymentField === null) { - res.status(500).json({ error: "Failed to identify payment field" }); - return; - } - const paymentRef = doc(db, "payments", paymentId); - const { [paymentField]: paymentFieldPath } = ( - await getDoc(paymentRef) - ).data() as Payment; + if (paymentField === null) { + res.status(500).json({error: "Failed to identify payment field"}); + return; + } + const paymentRef = doc(db, "payments", paymentId); + const {[paymentField]: paymentFieldPath} = (await getDoc(paymentRef)).data() as Payment; - // Create a reference to the file to delete - const documentRef = ref(storage, paymentFieldPath); - const url = await getDownloadURL(documentRef); - res.status(200).json({ url, name: documentRef.name }); + // Create a reference to the file to delete + const documentRef = ref(storage, paymentFieldPath); + const url = await getDownloadURL(documentRef); + res.status(200).json({url, name: documentRef.name}); } async function post(req: NextApiRequest, res: NextApiResponse) { - const { type, paymentId } = req.query as { - type: FilesStorage; - paymentId: string; - }; - const paymentField = getPaymentField(type); + const {type, paymentId} = req.query as { + type: FilesStorage; + paymentId: string; + }; + const paymentField = getPaymentField(type); - if (paymentField === null) { - res.status(500).json({ error: "Failed to identify payment field" }); - return; - } + if (paymentField === null) { + res.status(500).json({error: "Failed to identify payment field"}); + return; + } - try { - const ref = await handleUpload(req, paymentId, paymentField); - res.status(200).json({ ref }); - } catch (error) { - res.status(500).json({ error }); - } + try { + const ref = await handleUpload(req, paymentId, paymentField); + console.log(ref); + + const updatedDoc = (await getDoc(doc(db, "payments", paymentId))).data() as Payment; + if (updatedDoc.commissionTransfer && updatedDoc.corporateTransfer) { + await setDoc(doc(db, "payments", paymentId), {isPaid: true}, {merge: true}); + } + res.status(200).json({ref}); + } catch (error) { + res.status(500).json({error}); + } } async function del(req: NextApiRequest, res: NextApiResponse) { - const { type, paymentId } = req.query as { - type: FilesStorage; - paymentId: string; - }; - const paymentField = getPaymentField(type); - if (paymentField === null) { - res.status(500).json({ error: "Failed to identify payment field" }); - return; - } + const {type, paymentId} = req.query as { + type: FilesStorage; + paymentId: string; + }; + const paymentField = getPaymentField(type); + if (paymentField === null) { + res.status(500).json({error: "Failed to identify payment field"}); + return; + } - try { - await handleDelete(paymentId, paymentField); - res.status(200).json({ ok: true }); - } catch (err) { - console.error(err); - res.status(500).json({ error: "Failed to delete file" }); - } + try { + await handleDelete(paymentId, paymentField); + res.status(200).json({ok: true}); + } catch (err) { + console.error(err); + res.status(500).json({error: "Failed to delete file"}); + } } async function patch(req: NextApiRequest, res: NextApiResponse) { - const { type, paymentId } = req.query as { - type: FilesStorage; - paymentId: string; - }; - const paymentField = getPaymentField(type); - if (paymentField === null) { - res.status(500).json({ error: "Failed to identify payment field" }); - return; - } + const {type, paymentId} = req.query as { + type: FilesStorage; + paymentId: string; + }; + const paymentField = getPaymentField(type); + if (paymentField === null) { + res.status(500).json({error: "Failed to identify payment field"}); + return; + } - try { - await handleDelete(paymentId, paymentField); - } catch (err) { - console.error(err); - res.status(500).json({ error: "Failed to delete file" }); - return; - } + try { + await handleDelete(paymentId, paymentField); + } catch (err) { + console.error(err); + res.status(500).json({error: "Failed to delete file"}); + return; + } - try { - const ref = await handleUpload(req, paymentId, paymentField); - res.status(200).json({ ref }); - } catch (err) { - res.status(500).json({ error: "Failed to upload file" }); - } + try { + const ref = await handleUpload(req, paymentId, paymentField); + res.status(200).json({ref}); + } catch (err) { + res.status(500).json({error: "Failed to upload file"}); + } } export const config = { - api: { - bodyParser: false, - }, + api: { + bodyParser: false, + }, }; diff --git a/src/pages/payment-record.tsx b/src/pages/payment-record.tsx index 81b1fed8..30a2d775 100644 --- a/src/pages/payment-record.tsx +++ b/src/pages/payment-record.tsx @@ -387,6 +387,7 @@ export default function PaymentRecord() { cell: (info) => (
(
(
(
(
(