mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Almost got upload flow working, need to address cors error
This commit is contained in:
parent
6624690f39
commit
c8e200eda7
4 changed files with 95 additions and 34 deletions
|
|
@ -13,7 +13,7 @@ function generateS3Key(userId: number, portfolioId: number, filename: string) {
|
|||
return key;
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
export async function POST(request: NextRequest) {
|
||||
// For the moment, this api specifically handles uploads of csvs
|
||||
|
||||
const body = await request.json();
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ export function Toolbar({ portfolioId }: ToolbarProps) {
|
|||
setIsUploadCsvOpen={setModalIsOpen}
|
||||
/>
|
||||
</NavigationMenuList>
|
||||
<UploadCsvModal isOpen={modalIsOpen} setIsOpen={setModalIsOpen} />
|
||||
<UploadCsvModal
|
||||
isOpen={modalIsOpen}
|
||||
setIsOpen={setModalIsOpen}
|
||||
portfolioId={portfolioId}
|
||||
/>
|
||||
</NavigationMenu>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,63 +9,119 @@ import { Input } from "@/app/shadcn_components/ui/input";
|
|||
import { Label } from "@/app/shadcn_components/ui/label";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import Link from "next/link";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
async function generatePresignedUrl({
|
||||
userId,
|
||||
portfolioId,
|
||||
}: {
|
||||
userId: number;
|
||||
portfolioId: number;
|
||||
}) {
|
||||
const body = JSON.stringify({ userId: userId, portfolioId: portfolioId });
|
||||
console.log("body before: ", body);
|
||||
|
||||
const presignedResponse = await fetch(`/api/upload/csv`, {
|
||||
method: "POST",
|
||||
body: body,
|
||||
});
|
||||
if (!presignedResponse.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
const presignedUrl = await presignedResponse.json();
|
||||
return presignedUrl;
|
||||
}
|
||||
|
||||
async function uploadCsvToS3({
|
||||
presignedUrl,
|
||||
file,
|
||||
}: {
|
||||
presignedUrl: string;
|
||||
file: File;
|
||||
}) {
|
||||
console.log("Uploading file to s3");
|
||||
const upload = await fetch(presignedUrl, {
|
||||
method: "PUT",
|
||||
body: file,
|
||||
});
|
||||
|
||||
if (!upload.ok) {
|
||||
throw new Error("Upload failed.");
|
||||
}
|
||||
|
||||
return upload;
|
||||
}
|
||||
|
||||
export const SubmitPlan = ({
|
||||
buttonDisabled,
|
||||
goal,
|
||||
housingType,
|
||||
goalValue,
|
||||
file,
|
||||
portfolioId,
|
||||
}: {
|
||||
buttonDisabled: boolean;
|
||||
goal: string;
|
||||
housingType: string;
|
||||
goalValue: string;
|
||||
file: File | null;
|
||||
portfolioId: number;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const session = useSession();
|
||||
// This state will hold the presignedUrl once it is available
|
||||
const [presignedUrl, setPresignedUrl] = useState(null);
|
||||
|
||||
async function triggerPlanBuild() {
|
||||
const requestBody = JSON.stringify({
|
||||
goal: goal,
|
||||
goalValue: goalValue,
|
||||
housingType: housingType,
|
||||
});
|
||||
|
||||
const response = await fetch("/api/portfolio/plan", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
const { mutate: mutateUploadCsv, isLoading: isUploadLoading } = useMutation(
|
||||
uploadCsvToS3,
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
return data;
|
||||
},
|
||||
onError: (error) => {
|
||||
// handle error
|
||||
console.error(error);
|
||||
},
|
||||
body: requestBody,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
const { mutate, isLoading } = useMutation(triggerPlanBuild, {
|
||||
const { mutate, isLoading } = useMutation(generatePresignedUrl, {
|
||||
onSuccess: (data) => {
|
||||
console.log("uploading csv");
|
||||
// router.push(`/portfolio/${data.id}`);
|
||||
// After the presigned URL has been generated, we can upload the file to S3
|
||||
mutateUploadCsv({ presignedUrl: data.url, file: file });
|
||||
},
|
||||
onError: (error) => {
|
||||
// handle error
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = () => {
|
||||
mutate();
|
||||
if (!session.data) {
|
||||
// The user is not logged in, redirect them to sign in
|
||||
router.push("/");
|
||||
return null;
|
||||
}
|
||||
const userId = session.data.user.dbId;
|
||||
|
||||
const handlePlanBuild = () => {
|
||||
// The plan build is triggered by clicking submit which will:
|
||||
// 1) Generate a pre-signed url to upload to
|
||||
// 2) Upload the csv to the pre-signed url
|
||||
// 3) Trigger the job to build the plan
|
||||
// 4) Redirect the user to some loading page - this could be the portfolio page itself and we just trigger a regresh with skeleton cards for the properties
|
||||
mutate({ userId: userId, portfolioId: portfolioId });
|
||||
|
||||
// mutateUploadCsv(presignedUrl);
|
||||
|
||||
// console.log("Redirect user to loading page");
|
||||
// router.push("/portfolio/somewhere");
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="text-white inline-flex justify-center rounded-md border border-transparent bg-brandblue px-4 py-2 text-sm font-medium text-grey-900 hover:bg-hoverblue focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:bg-gray-300 disabled:opacity-50"
|
||||
onClick={handleSubmit}
|
||||
onClick={handlePlanBuild}
|
||||
disabled={buttonDisabled || isLoading}
|
||||
>
|
||||
{isLoading ? "Creating..." : "Create"}
|
||||
|
|
@ -233,9 +289,11 @@ const goalValueOptions = [
|
|||
export default function UploadCsvModal({
|
||||
isOpen = false,
|
||||
setIsOpen,
|
||||
portfolioId,
|
||||
}: {
|
||||
isOpen?: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
portfolioId: number;
|
||||
}) {
|
||||
const [budget, setBudget] = useState<undefined | number>(undefined);
|
||||
|
||||
|
|
@ -246,10 +304,6 @@ export default function UploadCsvModal({
|
|||
|
||||
const [csvFile, setCsvFile] = useState<File | null>(null);
|
||||
|
||||
function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
setCsvFile(e.target.files ? e.target.files[0] : null);
|
||||
}
|
||||
|
||||
function handleBudgeChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
setBudget(e.target.valueAsNumber);
|
||||
}
|
||||
|
|
@ -447,6 +501,8 @@ export default function UploadCsvModal({
|
|||
goal={goalValue}
|
||||
housingType={housingType}
|
||||
goalValue={goalValue}
|
||||
portfolioId={portfolioId}
|
||||
file={csvFile}
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
|
|
|
|||
|
|
@ -260,7 +260,8 @@ export default async function Page({
|
|||
// This is temp until we retrieve this data from the frontend
|
||||
// TODO: Update the objects to contains objective + any other required information
|
||||
|
||||
const portfolioId = params.slug;
|
||||
// We explcitly cast the slug to a number since it will be a string
|
||||
const portfolioId = Number(params.slug);
|
||||
const {
|
||||
name: portfolioName,
|
||||
budget,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue