Made it so the isPaid property is controlled with the file uploads/deletes

This commit is contained in:
Tiago Ribeiro
2024-01-09 11:32:17 +00:00
parent 14d19257df
commit 0ed843125a
3 changed files with 261 additions and 281 deletions

View File

@@ -1,6 +1,6 @@
import React, { ChangeEvent } from "react"; import React, {ChangeEvent} from "react";
import { BsUpload, BsDownload, BsTrash, BsArrowRepeat } from "react-icons/bs"; import {BsUpload, BsDownload, BsTrash, BsArrowRepeat} from "react-icons/bs";
import { FilesStorage } from "@/interfaces/storage.files"; import {FilesStorage} from "@/interfaces/storage.files";
import axios from "axios"; import axios from "axios";
interface Asset { interface Asset {
@@ -12,9 +12,10 @@ const PaymentAssetManager = (props: {
asset: string | undefined; asset: string | undefined;
permissions: "read" | "write"; permissions: "read" | "write";
type: FilesStorage; type: FilesStorage;
reload: () => void;
paymentId: string; paymentId: string;
}) => { }) => {
const { asset, permissions, type, paymentId } = props; const {asset, permissions, type, paymentId} = props;
const fileInputRef = React.useRef<HTMLInputElement>(null); const fileInputRef = React.useRef<HTMLInputElement>(null);
const fileInputReplaceRef = React.useRef<HTMLInputElement>(null); const fileInputReplaceRef = React.useRef<HTMLInputElement>(null);
@@ -24,7 +25,7 @@ const PaymentAssetManager = (props: {
complete: asset ? true : false, complete: asset ? true : false,
}); });
const { file, complete } = managingAsset; const {file, complete} = managingAsset;
const deleteAsset = () => { const deleteAsset = () => {
if (confirm("Are you sure you want to delete this document?")) { if (confirm("Are you sure you want to delete this document?")) {
@@ -44,22 +45,13 @@ const PaymentAssetManager = (props: {
}) })
.catch((error) => { .catch((error) => {
console.error("Error occurred during file deletion:", error); console.error("Error occurred during file deletion:", error);
}); })
.finally(props.reload);
} }
}; };
const renderFileInput = ( const renderFileInput = (onChange: any, ref: React.RefObject<HTMLInputElement>) => (
onChange: any, <input type="file" ref={ref} style={{display: "none"}} onChange={onChange} multiple={false} accept="application/pdf" />
ref: React.RefObject<HTMLInputElement>
) => (
<input
type="file"
ref={ref}
style={{ display: "none" }}
onChange={onChange}
multiple={false}
accept="application/pdf"
/>
); );
const handleFileChange = async (e: Event, method: "post" | "patch") => { const handleFileChange = async (e: Event, method: "post" | "patch") => {
@@ -94,7 +86,8 @@ const PaymentAssetManager = (props: {
}) })
.catch((error) => { .catch((error) => {
console.error("Error occurred during file upload:", error); console.error("Error occurred during file upload:", error);
}); })
.finally(props.reload);
} }
}; };
@@ -130,14 +123,8 @@ const PaymentAssetManager = (props: {
<BsDownload onClick={downloadAsset} /> <BsDownload onClick={downloadAsset} />
<BsArrowRepeat onClick={() => fileInputReplaceRef.current?.click()} /> <BsArrowRepeat onClick={() => fileInputReplaceRef.current?.click()} />
<BsTrash onClick={deleteAsset} /> <BsTrash onClick={deleteAsset} />
{renderFileInput( {renderFileInput((e: Event) => handleFileChange(e, "patch"), fileInputReplaceRef)}
(e: Event) => handleFileChange(e, "patch"), {renderFileInput((e: Event) => handleFileChange(e, "post"), fileInputRef)}
fileInputReplaceRef
)}
{renderFileInput(
(e: Event) => handleFileChange(e, "post"),
fileInputRef
)}
</> </>
); );
} }

View File

@@ -1,25 +1,14 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next"; import type {NextApiRequest, NextApiResponse} from "next";
import { app, storage } from "@/firebase"; import {app, storage} from "@/firebase";
import { import {getFirestore, getDoc, doc, updateDoc, deleteField, setDoc} from "firebase/firestore";
getFirestore, import {withIronSessionApiRoute} from "iron-session/next";
getDoc, import {sessionOptions} from "@/lib/session";
doc, import {FilesStorage} from "@/interfaces/storage.files";
updateDoc,
deleteField,
} 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 fs from "fs";
import { import {ref, uploadBytes, deleteObject, getDownloadURL} from "firebase/storage";
ref,
uploadBytes,
deleteObject,
getDownloadURL,
} from "firebase/storage";
import formidable from "formidable-serverless"; import formidable from "formidable-serverless";
const db = getFirestore(app); const db = getFirestore(app);
@@ -35,35 +24,29 @@ const getPaymentField = (type: FilesStorage) => {
} }
}; };
const handleDelete = async ( const handleDelete = async (paymentId: string, paymentField: "commissionTransfer" | "corporateTransfer") => {
paymentId: string,
paymentField: "commissionTransfer" | "corporateTransfer"
) => {
const paymentRef = doc(db, "payments", paymentId); const paymentRef = doc(db, "payments", paymentId);
const paymentDoc = await getDoc(paymentRef); const paymentDoc = await getDoc(paymentRef);
const { [paymentField]: paymentFieldPath } = paymentDoc.data() as Payment; const {[paymentField]: paymentFieldPath} = paymentDoc.data() as Payment;
// Create a reference to the file to delete // Create a reference to the file to delete
const documentRef = ref(storage, paymentFieldPath); const documentRef = ref(storage, paymentFieldPath);
await deleteObject(documentRef); await deleteObject(documentRef);
await updateDoc(paymentRef, { await updateDoc(paymentRef, {
[paymentField]: deleteField(), [paymentField]: deleteField(),
isPaid: false,
}); });
}; };
const handleUpload = async ( const handleUpload = async (req: NextApiRequest, paymentId: string, paymentField: "commissionTransfer" | "corporateTransfer") =>
req: NextApiRequest,
paymentId: string,
paymentField: "commissionTransfer" | "corporateTransfer"
) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const form = formidable({ keepExtensions: true }); const form = formidable({keepExtensions: true});
form.parse(req, async (err: any, fields: any, files: any) => { form.parse(req, async (err: any, fields: any, files: any) => {
if (err) { if (err) {
reject(err); reject(err);
return; return;
} }
try { try {
const { file } = files; const {file} = files;
const fileName = Date.now() + "-" + file.name; const fileName = Date.now() + "-" + file.name;
const fileRef = ref(storage, fileName); const fileRef = ref(storage, fileName);
@@ -86,7 +69,7 @@ const handleUpload = async (
export default withIronSessionApiRoute(handler, sessionOptions); export default withIronSessionApiRoute(handler, sessionOptions);
async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) { if (!req.session.user) {
res.status(401).json({ ok: false }); res.status(401).json({ok: false});
return; return;
} }
@@ -99,75 +82,79 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
} }
async function get(req: NextApiRequest, res: NextApiResponse) { async function get(req: NextApiRequest, res: NextApiResponse) {
const { type, paymentId } = req.query as { const {type, paymentId} = req.query as {
type: FilesStorage; type: FilesStorage;
paymentId: string; paymentId: string;
}; };
const paymentField = getPaymentField(type); const paymentField = getPaymentField(type);
if (paymentField === null) { if (paymentField === null) {
res.status(500).json({ error: "Failed to identify payment field" }); res.status(500).json({error: "Failed to identify payment field"});
return; return;
} }
const paymentRef = doc(db, "payments", paymentId); const paymentRef = doc(db, "payments", paymentId);
const { [paymentField]: paymentFieldPath } = ( const {[paymentField]: paymentFieldPath} = (await getDoc(paymentRef)).data() as Payment;
await getDoc(paymentRef)
).data() as Payment;
// Create a reference to the file to delete // Create a reference to the file to delete
const documentRef = ref(storage, paymentFieldPath); const documentRef = ref(storage, paymentFieldPath);
const url = await getDownloadURL(documentRef); const url = await getDownloadURL(documentRef);
res.status(200).json({ url, name: documentRef.name }); res.status(200).json({url, name: documentRef.name});
} }
async function post(req: NextApiRequest, res: NextApiResponse) { async function post(req: NextApiRequest, res: NextApiResponse) {
const { type, paymentId } = req.query as { const {type, paymentId} = req.query as {
type: FilesStorage; type: FilesStorage;
paymentId: string; paymentId: string;
}; };
const paymentField = getPaymentField(type); const paymentField = getPaymentField(type);
if (paymentField === null) { if (paymentField === null) {
res.status(500).json({ error: "Failed to identify payment field" }); res.status(500).json({error: "Failed to identify payment field"});
return; return;
} }
try { try {
const ref = await handleUpload(req, paymentId, paymentField); const ref = await handleUpload(req, paymentId, paymentField);
res.status(200).json({ ref }); 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) { } catch (error) {
res.status(500).json({ error }); res.status(500).json({error});
} }
} }
async function del(req: NextApiRequest, res: NextApiResponse) { async function del(req: NextApiRequest, res: NextApiResponse) {
const { type, paymentId } = req.query as { const {type, paymentId} = req.query as {
type: FilesStorage; type: FilesStorage;
paymentId: string; paymentId: string;
}; };
const paymentField = getPaymentField(type); const paymentField = getPaymentField(type);
if (paymentField === null) { if (paymentField === null) {
res.status(500).json({ error: "Failed to identify payment field" }); res.status(500).json({error: "Failed to identify payment field"});
return; return;
} }
try { try {
await handleDelete(paymentId, paymentField); await handleDelete(paymentId, paymentField);
res.status(200).json({ ok: true }); res.status(200).json({ok: true});
} catch (err) { } catch (err) {
console.error(err); console.error(err);
res.status(500).json({ error: "Failed to delete file" }); res.status(500).json({error: "Failed to delete file"});
} }
} }
async function patch(req: NextApiRequest, res: NextApiResponse) { async function patch(req: NextApiRequest, res: NextApiResponse) {
const { type, paymentId } = req.query as { const {type, paymentId} = req.query as {
type: FilesStorage; type: FilesStorage;
paymentId: string; paymentId: string;
}; };
const paymentField = getPaymentField(type); const paymentField = getPaymentField(type);
if (paymentField === null) { if (paymentField === null) {
res.status(500).json({ error: "Failed to identify payment field" }); res.status(500).json({error: "Failed to identify payment field"});
return; return;
} }
@@ -175,15 +162,15 @@ async function patch(req: NextApiRequest, res: NextApiResponse) {
await handleDelete(paymentId, paymentField); await handleDelete(paymentId, paymentField);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
res.status(500).json({ error: "Failed to delete file" }); res.status(500).json({error: "Failed to delete file"});
return; return;
} }
try { try {
const ref = await handleUpload(req, paymentId, paymentField); const ref = await handleUpload(req, paymentId, paymentField);
res.status(200).json({ ref }); res.status(200).json({ref});
} catch (err) { } catch (err) {
res.status(500).json({ error: "Failed to upload file" }); res.status(500).json({error: "Failed to upload file"});
} }
} }

View File

@@ -387,6 +387,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div className={containerClassName}> <div className={containerClassName}>
<PaymentAssetManager <PaymentAssetManager
reload={reload}
permissions={info.row.original.isPaid ? "read" : "write"} permissions={info.row.original.isPaid ? "read" : "write"}
asset={info.row.original.corporateTransfer} asset={info.row.original.corporateTransfer}
paymentId={info.row.original.id} paymentId={info.row.original.id}
@@ -404,6 +405,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div className={containerClassName}> <div className={containerClassName}>
<PaymentAssetManager <PaymentAssetManager
reload={reload}
permissions="read" permissions="read"
asset={info.row.original.commissionTransfer} asset={info.row.original.commissionTransfer}
paymentId={info.row.original.id} paymentId={info.row.original.id}
@@ -421,6 +423,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div className={containerClassName}> <div className={containerClassName}>
<PaymentAssetManager <PaymentAssetManager
reload={reload}
permissions="read" permissions="read"
asset={info.row.original.corporateTransfer} asset={info.row.original.corporateTransfer}
paymentId={info.row.original.id} paymentId={info.row.original.id}
@@ -435,6 +438,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div className={containerClassName}> <div className={containerClassName}>
<PaymentAssetManager <PaymentAssetManager
reload={reload}
permissions={info.row.original.isPaid ? "read" : "write"} permissions={info.row.original.isPaid ? "read" : "write"}
asset={info.row.original.commissionTransfer} asset={info.row.original.commissionTransfer}
paymentId={info.row.original.id} paymentId={info.row.original.id}
@@ -452,6 +456,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div className={containerClassName}> <div className={containerClassName}>
<PaymentAssetManager <PaymentAssetManager
reload={reload}
permissions="write" permissions="write"
asset={info.row.original.corporateTransfer} asset={info.row.original.corporateTransfer}
paymentId={info.row.original.id} paymentId={info.row.original.id}
@@ -466,6 +471,7 @@ export default function PaymentRecord() {
cell: (info) => ( cell: (info) => (
<div className={containerClassName}> <div className={containerClassName}>
<PaymentAssetManager <PaymentAssetManager
reload={reload}
permissions="write" permissions="write"
asset={info.row.original.commissionTransfer} asset={info.row.original.commissionTransfer}
paymentId={info.row.original.id} paymentId={info.row.original.id}