Skip to content

Commit 558b16a

Browse files
committed
added update application logic and ui
1 parent d33a9f1 commit 558b16a

File tree

10 files changed

+150
-57
lines changed

10 files changed

+150
-57
lines changed

app/applications/[applicationId]/page.tsx

+1-5
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ const ApplicationPage = async ({ params: { applicationId } }: PageProps) => {
2121

2222
return (
2323
<div>
24-
<AppHeader
25-
title={application.title}
26-
logo={application.logo}
27-
applicationUrl={application.applicationUrl}
28-
/>
24+
<AppHeader application={application} />
2925

3026
<AppDetails application={application} />
3127
</div>

components/AppHeader.tsx

+32-20
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
"use client";
22

3+
import { useState } from "react";
34
import Image from "next/image";
45
import Button from "./Button";
56
import LinkIcon from "./icons/LinkIcon";
67
import EditIcon from "./icons/EditIcon";
7-
import { useState } from "react";
8+
import { useAccount } from "wagmi";
9+
import UpadateApplication from "./ApplicationForm";
810

911
type AppHeaderProps = {
10-
title: string;
11-
logo?: string;
12-
applicationUrl?: string;
12+
application: IApplication;
1313
};
1414

15-
const AppHeader: React.FC<AppHeaderProps> = ({
16-
title,
17-
logo,
18-
applicationUrl,
19-
}) => {
15+
const AppHeader: React.FC<AppHeaderProps> = ({ application }) => {
2016
const [error, setError] = useState(false);
17+
18+
const [open, setOpen] = useState(false);
19+
const { address } = useAccount();
20+
const { title, logo, applicationUrl, user } = application;
21+
22+
const isMyApplication = user === address;
23+
2124
return (
2225
<header className="flex items-center justify-between px-2 py-2 mt-8">
2326
<div className="flex items-center justify-start sm:justify-center">
@@ -38,17 +41,20 @@ const AppHeader: React.FC<AppHeaderProps> = ({
3841
</h1>
3942
</div>
4043
<div className="flex gap-2 lg:gap-4">
41-
<Button
42-
variant="borderless"
43-
href={applicationUrl}
44-
className="px-4 rounded-3xl lg:rounded-full md:px-7 cursor-not-allowed opacity-40 dark:opacity-100 dark:border-gray-400"
45-
>
46-
<EditIcon />
47-
<span className="hidden lg:block dark:text-gray-400">
48-
{" "}
49-
Edit Application
50-
</span>
51-
</Button>
44+
{isMyApplication ? (
45+
<Button
46+
onClick={() => setOpen(true)}
47+
variant="borderless"
48+
href={applicationUrl}
49+
className="px-4 rounded-3xl lg:rounded-full md:px-7 dark:border-white"
50+
>
51+
<EditIcon />
52+
<span className="hidden lg:block dark:text-gray-400">
53+
{" "}
54+
Edit Application
55+
</span>
56+
</Button>
57+
) : null}
5258

5359
{applicationUrl && (
5460
<Button
@@ -64,6 +70,12 @@ const AppHeader: React.FC<AppHeaderProps> = ({
6470
</Button>
6571
)}
6672
</div>
73+
<UpadateApplication
74+
modalOpen={open}
75+
closeModal={() => setOpen(false)}
76+
isEditMode={true}
77+
application={application}
78+
/>
6779
</header>
6880
);
6981
};

components/NewApplication/Steps/index.tsx components/ApplicationForm/Steps/index.tsx

+94-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
2-
import { FC, useState, useTransition } from "react";
2+
3+
import { FC, useEffect, useState, useTransition } from "react";
34
import cn from "classnames";
45
import { useAccount } from "wagmi";
56
import { Tab } from "@headlessui/react";
@@ -17,21 +18,80 @@ import Button from "../../Button";
1718
import { FormProvider, useForm } from "react-hook-form";
1819
import { submitApplication } from "../../../lib/actions";
1920

20-
type NewApplicationStepsProps = {
21+
interface ApplicationFormStepsProps {
2122
closeModal: () => void;
22-
};
23+
}
24+
25+
interface CreateApplicationFormStepsProps extends ApplicationFormStepsProps {
26+
isEditMode: false;
27+
}
2328

24-
const NewApplicationSteps: FC<NewApplicationStepsProps> = ({ closeModal }) => {
29+
interface EditApplicationFormStepsProps extends ApplicationFormStepsProps {
30+
isEditMode: true;
31+
application: IApplication;
32+
}
33+
34+
const ApplicationFormSteps: FC<
35+
CreateApplicationFormStepsProps | EditApplicationFormStepsProps
36+
> = ({ closeModal, ...otherProps }) => {
2537
const TOTAL_STEPS = 3;
2638
const [currentStep, setCurrentStep] = useState(0);
2739
const { address } = useAccount();
2840
const [isPending, startTransition] = useTransition();
41+
2942
const methods = useForm<IApplicationInput>({
3043
defaultValues: {
3144
category: [],
3245
},
3346
});
3447

48+
const createFileFromImageUrl = async (
49+
imageUrl: string,
50+
fileName: string
51+
): Promise<File> => {
52+
const response = await fetch(imageUrl);
53+
const blob = await response.blob();
54+
return new File([blob], fileName);
55+
};
56+
57+
const getDefaultValues = async () => {
58+
if (otherProps.isEditMode === true) {
59+
const {
60+
id,
61+
logo: logoUrl,
62+
screenshots: screenshotUrls,
63+
...otherApplicationProps
64+
} = otherProps.application;
65+
66+
let logo: File | undefined = undefined;
67+
let screenshots: { value: File }[] | undefined = undefined;
68+
69+
// logo and screenshot we receive from the db are just the CIDs
70+
// converting those to File objects here
71+
if (logoUrl) {
72+
logo = await createFileFromImageUrl(logoUrl, "logo");
73+
}
74+
75+
if (screenshotUrls?.length) {
76+
screenshots = await Promise.all(
77+
screenshotUrls.map(async (url, index) => ({
78+
value: await createFileFromImageUrl(url, `screenshot-${index}`),
79+
}))
80+
);
81+
}
82+
83+
methods.reset({
84+
logo,
85+
screenshots,
86+
...otherApplicationProps,
87+
});
88+
}
89+
};
90+
91+
useEffect(() => {
92+
getDefaultValues();
93+
}, []);
94+
3595
const [steps] = useState<Steps>({
3696
basicInfo: {
3797
id: 1,
@@ -59,27 +119,39 @@ const NewApplicationSteps: FC<NewApplicationStepsProps> = ({ closeModal }) => {
59119
setCurrentStep((currentStep) => currentStep - 1);
60120
};
61121

62-
const submit = methods.handleSubmit(({ screenshots, logo, ...rest }) => {
63-
const formData = new FormData();
122+
const submit = methods.handleSubmit(
123+
({ screenshots, logo, ...otherApplicationProps }) => {
124+
const formData = new FormData();
64125

65-
if (screenshots && screenshots.length > 0) {
66-
screenshots.forEach((image) => {
67-
formData.append("screenshots", image.value);
68-
});
69-
}
126+
if (screenshots && screenshots.length > 0) {
127+
screenshots.forEach((image) => {
128+
formData.append("screenshots", image.value);
129+
});
130+
}
70131

71-
if (logo) {
72-
formData.append("logo", logo);
73-
}
132+
if (logo) {
133+
formData.append("logo", logo);
134+
}
135+
136+
const isEditMode = otherProps.isEditMode;
137+
138+
// If there is an id in the application data,
139+
// the application in the db will be overwritten with the latest data
140+
// because we have a js Map
141+
// see the `addNode` in the database implementation.
142+
const id: string | undefined = isEditMode
143+
? otherProps.application.id
144+
: undefined;
74145

75-
startTransition(() => {
76-
submitApplication({
77-
images: formData,
78-
data: JSON.stringify({ ...rest, user: address }),
146+
startTransition(() => {
147+
submitApplication({
148+
images: formData,
149+
data: JSON.stringify({ id, user: address, ...otherApplicationProps }),
150+
});
151+
closeModal();
79152
});
80-
closeModal();
81-
});
82-
});
153+
}
154+
);
83155

84156
return (
85157
<Tab.Group selectedIndex={currentStep} as="div" className="px-2">
@@ -169,4 +241,4 @@ const NewApplicationSteps: FC<NewApplicationStepsProps> = ({ closeModal }) => {
169241
);
170242
};
171243

172-
export default NewApplicationSteps;
244+
export default ApplicationFormSteps;

components/NewApplication/index.tsx components/ApplicationForm/index.tsx

+16-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
import { FC, Fragment } from "react";
22
import { Dialog, Transition } from "@headlessui/react";
33
import Steps from "./Steps";
4+
import { IApplicationInput } from "./types";
45

5-
interface NewApplicationProps {
6+
interface ApplicationFormProps {
67
modalOpen: boolean;
78
closeModal: () => void;
89
}
910

10-
const NewApplication: FC<NewApplicationProps> = ({
11-
modalOpen,
12-
closeModal,
13-
}) => {
11+
interface ApplicationFormCreateProps extends ApplicationFormProps {
12+
isEditMode: false;
13+
}
14+
15+
interface ApplicationFormEditProps extends ApplicationFormProps {
16+
isEditMode: true;
17+
application: IApplication;
18+
}
19+
20+
const ApplicationForm: FC<
21+
ApplicationFormCreateProps | ApplicationFormEditProps
22+
> = ({ closeModal, modalOpen, ...rest }) => {
1423
return (
1524
<Transition.Root show={modalOpen} as={Fragment}>
1625
<Dialog as="div" className="relative z-10" onClose={closeModal}>
@@ -38,7 +47,7 @@ const NewApplication: FC<NewApplicationProps> = ({
3847
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
3948
>
4049
<Dialog.Panel className="relative px-4 pt-5 pb-4 overflow-hidden text-left transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:w-full sm:max-w-xl sm:p-6">
41-
<Steps closeModal={closeModal} />
50+
<Steps closeModal={closeModal} {...rest} />
4251
</Dialog.Panel>
4352
</Transition.Child>
4453
</div>
@@ -48,4 +57,4 @@ const NewApplication: FC<NewApplicationProps> = ({
4857
);
4958
};
5059

51-
export default NewApplication;
60+
export default ApplicationForm;
File renamed without changes.

components/Nav.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Fragment, useState } from "react";
44
import { useAccount } from "wagmi";
55
import { Menu, Transition } from "@headlessui/react";
66
import { Bars4Icon } from "@heroicons/react/24/outline";
7-
import NewApplication from "./NewApplication";
7+
import ApplicationForm from "./ApplicationForm";
88
import Link from "next/link";
99
import PolisLogo from "./icons/PolisLogo";
1010
import { ConnectButton } from "./ConnectButton";
@@ -91,7 +91,11 @@ export const Nav = () => {
9191
</Transition>
9292
</Menu>
9393
</div>
94-
<NewApplication modalOpen={open} closeModal={() => setOpen(false)} />
94+
<ApplicationForm
95+
modalOpen={open}
96+
closeModal={() => setOpen(false)}
97+
isEditMode={false}
98+
/>
9599
</nav>
96100
</>
97101
);

scripts/init-db.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import ora from "ora";
6060

6161
console.log("\n");
6262

63-
const spinner = ora("Adding a empty database to IPFS...\n").start();
63+
const spinner = ora("Adding an empty database to IPFS...\n").start();
6464

6565
const { infuraUrl, infurakey, infuraSecret } = answers;
6666

0 commit comments

Comments
 (0)