Skip to content

Commit

Permalink
Add Grant to profile dashboard (#3993)
Browse files Browse the repository at this point in the history
Co-authored-by: Alessia Marcolini <98marcolini@gmail.com>
  • Loading branch information
estyxx and alessiamarcolini authored Oct 12, 2024
1 parent 90ebef4 commit cafd1fb
Show file tree
Hide file tree
Showing 18 changed files with 695 additions and 118 deletions.
2 changes: 1 addition & 1 deletion backend/api/grants/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Grant:
need_accommodation: bool
why: str
notes: str
travelling_from: str
travelling_from: Optional[str]
applicant_reply_deadline: Optional[datetime]
website: str
twitter_handle: str
Expand Down
48 changes: 25 additions & 23 deletions frontend/src/components/grant-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { FormattedMessage } from "react-intl";
import { useFormState } from "react-use-form-state";

import { Alert } from "~/components/alert";
import { MyGrant } from "~/components/profile/my-grant";
import { useCountries } from "~/helpers/use-countries";
import { useCurrentUser } from "~/helpers/use-current-user";
import { useTranslatedMessage } from "~/helpers/use-translated-message";
Expand All @@ -37,7 +36,7 @@ import {
type SendGrantMutation,
type UpdateGrantInput,
type UpdateGrantMutation,
useMyGrantQuery,
useGrantDeadlineQuery,
useSendGrantMutation,
} from "~/types";

Expand Down Expand Up @@ -76,18 +75,9 @@ export type GrantFormFields = {
acceptedPrivacyPolicy: boolean;
};

export const MyGrantOrForm = () => {
export const GrantSendForm = () => {
const code = process.env.conferenceCode;

const { error, data } = useMyGrantQuery({
errorPolicy: "all",
variables: {
conference: code,
},
skip: typeof window === "undefined",
});
const grant = data?.me?.grant;

const [submitGrant, { loading, error: grantError, data: grantData }] =
useSendGrantMutation({
onError(err) {
Expand All @@ -103,14 +93,6 @@ export const MyGrantOrForm = () => {
});
};

if (error) {
return <Alert variant="alert">{error.message}</Alert>;
}

if (grant) {
return <MyGrant />;
}

return (
<>
<Section>
Expand Down Expand Up @@ -155,6 +137,16 @@ export const GrantForm = ({
const language = useCurrentLanguage();
const countries = useCountries();

const {
data: {
conference: { deadline },
},
} = useGrantDeadlineQuery({
variables: {
conference: conference,
},
});

const inputPlaceholderText = useTranslatedMessage("input.placeholder");
const { user, loading: loadingUser } = useCurrentUser({});
const [formState, { text, textarea, select, checkbox }] =
Expand All @@ -167,6 +159,12 @@ export const GrantForm = ({
},
);

const dateFormatter = new Intl.DateTimeFormat(language, {
day: "numeric",
month: "long",
year: "numeric",
});

useEffect(() => {
// to not override if we are editing the grant
if (user && !grant) {
Expand Down Expand Up @@ -274,20 +272,24 @@ export const GrantForm = ({
<FormattedMessage
id="grants.form.sent"
values={{
linkGrant: (
linkMyGrant: (
<Link
href={createHref({ path: "/grants/edit", locale: language })}
href={createHref({
path: "/profile/my-grant",
locale: language,
})}
>
<Text
color="none"
decoration="underline"
size="inherit"
weight="strong"
>
<FormattedMessage id="grants.form.sent.linkGrant.text" />
<FormattedMessage id="grants.form.sent.linkMyGrant.text" />
</Text>
</Link>
),
grantsDeadline: dateFormatter.format(new Date(deadline.end)),
}}
/>
</Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Grid, Spacer, Text } from "@python-italia/pycon-styleguide";
import { FormattedMessage } from "react-intl";

import type { MyProfileWithGrantQuery } from "~/types";

import { TableItemHeader } from "~/components/table-item-header";
import { getCountryLabel } from "~/helpers/country-utils";
import { useCountries } from "~/helpers/use-countries";

type Props = {
grant: MyProfileWithGrantQuery["me"]["grant"];
};

export const GrantTableInfo = ({ grant }: Props) => {
const countries = useCountries();

return (
<Grid cols={3} gap="small" fullWidth>
<GrantInfo label={<FormattedMessage id="grants.form.fields.name" />}>
{grant.name}
</GrantInfo>

<GrantInfo label={<FormattedMessage id="grants.form.fields.fullName" />}>
{grant.fullName}
</GrantInfo>

<GrantInfo label={<FormattedMessage id="grants.form.fields.ageGroup" />}>
{grant.ageGroup && (
<FormattedMessage
id={`grants.form.fields.ageGroup.values.${grant.ageGroup}`}
/>
)}
</GrantInfo>

<GrantInfo
label={<FormattedMessage id="grants.form.fields.travellingFrom" />}
>
{getCountryLabel(countries, grant.travellingFrom)}
</GrantInfo>

<GrantInfo label={<FormattedMessage id="grants.form.fields.gender" />}>
{grant.gender ? (
<FormattedMessage id={`profile.gender.${grant.gender}`} />
) : (
"-"
)}
</GrantInfo>

<GrantInfo
label={<FormattedMessage id="grants.form.fields.occupation" />}
>
<FormattedMessage
id={`grants.form.fields.occupation.values.${grant.occupation}`}
/>
</GrantInfo>
</Grid>
);
};

const GrantInfo = ({
label,
children,
}: {
label: React.ReactNode;
children: React.ReactNode;
}) => {
return (
<div>
<TableItemHeader>{label}</TableItemHeader>
<Spacer size="xs" />
<Text>{children}</Text>
</div>
);
};
40 changes: 40 additions & 0 deletions frontend/src/components/my-grant-profile-page-handler/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Heading, Page, Section } from "@python-italia/pycon-styleguide";
import { FormattedMessage } from "react-intl";

import { useMyProfileWithGrantQuery } from "~/types";

import { MyGrant } from "~/components/my-grant-profile-page-handler/my-grant";
import { NoGrant } from "~/components/my-grant-profile-page-handler/no-grant";
import { MetaTags } from "../meta-tags";

export const MyGrantProfilePageHandler = () => {
const {
data: {
me: { grant },
conference: { deadline },
},
} = useMyProfileWithGrantQuery({
variables: {
conference: process.env.conferenceCode,
},
});

return (
<Page endSeparator={false}>
<FormattedMessage id="profile.myGrant">
{(text) => <MetaTags title={text} />}
</FormattedMessage>

<Section background="green">
<Heading size="display2">
<FormattedMessage id="profile.myGrant" />
</Heading>
</Section>

<Section>
{grant && <MyGrant grant={grant} deadline={deadline} />}
{!grant && <NoGrant deadline={deadline} />}
</Section>
</Page>
);
};
135 changes: 135 additions & 0 deletions frontend/src/components/my-grant-profile-page-handler/my-grant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import {
Button,
Grid,
GridColumn,
Link,
Spacer,
Text,
VerticalStack,
} from "@python-italia/pycon-styleguide";
import { FormattedMessage } from "react-intl";

import { useCurrentLanguage } from "~/locale/context";
import { DeadlineStatus, Status as GrantStatus } from "~/types";
import type { MyProfileWithGrantQuery } from "~/types";

import { createHref } from "../link";
import { GrantTableInfo } from "./grant-table-info";
import { Sidebar } from "./sidebar";

type Props = {
grant: MyProfileWithGrantQuery["me"]["grant"];
deadline: MyProfileWithGrantQuery["conference"]["deadline"];
};

const grantManageableStatuses = [
GrantStatus.WaitingForConfirmation,
GrantStatus.Confirmed,
GrantStatus.WaitingList,
GrantStatus.WaitingListMaybe,
];

export const MyGrant = ({ grant, deadline }: Props) => {
const language = useCurrentLanguage();

const canManageGrant = grantManageableStatuses.includes(grant.status);

const dateFormatter = new Intl.DateTimeFormat(language, {
day: "numeric",
month: "long",
year: "numeric",
});

return (
<>
<Grid cols={12}>
<GridColumn colSpan={4}>
<Sidebar
status={grant.status}
grantType={grant.grantType}
needsFundsForTravel={grant.needsFundsForTravel}
needAccommodation={grant.needAccommodation}
/>
{deadline?.status === DeadlineStatus.HappeningNow && (
<>
<Spacer size="medium" />
<Button
href={createHref({
path: "/grants/edit",
locale: language,
})}
size="small"
variant="secondary"
>
<FormattedMessage id="profile.myGrant.edit" />
</Button>
</>
)}
</GridColumn>

<GridColumn colSpan={8}>
<VerticalStack gap="medium" justifyContent="spaceBetween" fullHeight>
<div>
<Text size="label3" uppercase weight="strong">
<FormattedMessage id="profile.myGrant.nextSteps" />
</Text>
<Spacer size="xs" />
<Text>
<FormattedMessage
id={`profile.myGrant.status.${grant.status}.nextSteps`}
values={{
replyDeadline: (
<Text as="span" weight="strong">
{dateFormatter.format(
new Date(grant.applicantReplyDeadline),
)}
</Text>
),
}}
/>
</Text>
</div>

{canManageGrant && (
<div>
<Button
href={createHref({
path: "/grants/reply",
locale: language,
})}
size="small"
variant="secondary"
>
<FormattedMessage id="profile.myGrant.manage" />
</Button>
</div>
)}

<GrantTableInfo grant={grant} />

{deadline?.status === DeadlineStatus.HappeningNow && (
<Text>
<FormattedMessage
id="profile.myGrant.editInfo"
values={{
editDeadline: (
<Text as="span" weight="strong">
{dateFormatter.format(new Date(deadline.end))}
</Text>
),
}}
/>
</Text>
)}

{canManageGrant && (
<Text>
<FormattedMessage id="profile.myGrant.manage.warning" />
</Text>
)}
</VerticalStack>
</GridColumn>
</Grid>
</>
);
};
Loading

0 comments on commit cafd1fb

Please sign in to comment.