diff --git a/backend/api/grants/types.py b/backend/api/grants/types.py index a54708a09e..16c01ae210 100644 --- a/backend/api/grants/types.py +++ b/backend/api/grants/types.py @@ -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 diff --git a/frontend/src/components/grant-form/index.tsx b/frontend/src/components/grant-form/index.tsx index 3f589fbb30..5a86ab810f 100644 --- a/frontend/src/components/grant-form/index.tsx +++ b/frontend/src/components/grant-form/index.tsx @@ -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"; @@ -37,7 +36,7 @@ import { type SendGrantMutation, type UpdateGrantInput, type UpdateGrantMutation, - useMyGrantQuery, + useGrantDeadlineQuery, useSendGrantMutation, } from "~/types"; @@ -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) { @@ -103,14 +93,6 @@ export const MyGrantOrForm = () => { }); }; - if (error) { - return {error.message}; - } - - if (grant) { - return ; - } - return ( <>
@@ -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 }] = @@ -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) { @@ -274,9 +272,12 @@ export const GrantForm = ({ - + ), + grantsDeadline: dateFormatter.format(new Date(deadline.end)), }} /> diff --git a/frontend/src/components/my-grant-profile-page-handler/grant-table-info.tsx b/frontend/src/components/my-grant-profile-page-handler/grant-table-info.tsx new file mode 100644 index 0000000000..53bf68a273 --- /dev/null +++ b/frontend/src/components/my-grant-profile-page-handler/grant-table-info.tsx @@ -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 ( + + }> + {grant.name} + + + }> + {grant.fullName} + + + }> + {grant.ageGroup && ( + + )} + + + } + > + {getCountryLabel(countries, grant.travellingFrom)} + + + }> + {grant.gender ? ( + + ) : ( + "-" + )} + + + } + > + + + + ); +}; + +const GrantInfo = ({ + label, + children, +}: { + label: React.ReactNode; + children: React.ReactNode; +}) => { + return ( +
+ {label} + + {children} +
+ ); +}; diff --git a/frontend/src/components/my-grant-profile-page-handler/index.tsx b/frontend/src/components/my-grant-profile-page-handler/index.tsx new file mode 100644 index 0000000000..165afb210d --- /dev/null +++ b/frontend/src/components/my-grant-profile-page-handler/index.tsx @@ -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 ( + + + {(text) => } + + +
+ + + +
+ +
+ {grant && } + {!grant && } +
+
+ ); +}; diff --git a/frontend/src/components/my-grant-profile-page-handler/my-grant.tsx b/frontend/src/components/my-grant-profile-page-handler/my-grant.tsx new file mode 100644 index 0000000000..cf3077531a --- /dev/null +++ b/frontend/src/components/my-grant-profile-page-handler/my-grant.tsx @@ -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 ( + <> + + + + {deadline?.status === DeadlineStatus.HappeningNow && ( + <> + + + + )} + + + + +
+ + + + + + + {dateFormatter.format( + new Date(grant.applicantReplyDeadline), + )} + + ), + }} + /> + +
+ + {canManageGrant && ( +
+ +
+ )} + + + + {deadline?.status === DeadlineStatus.HappeningNow && ( + + + {dateFormatter.format(new Date(deadline.end))} + + ), + }} + /> + + )} + + {canManageGrant && ( + + + + )} +
+
+
+ + ); +}; diff --git a/frontend/src/components/my-grant-profile-page-handler/no-grant.tsx b/frontend/src/components/my-grant-profile-page-handler/no-grant.tsx new file mode 100644 index 0000000000..ef31bf1b7a --- /dev/null +++ b/frontend/src/components/my-grant-profile-page-handler/no-grant.tsx @@ -0,0 +1,62 @@ +import { + Button, + Container, + Heading, + Link, + Spacer, + Text, +} from "@python-italia/pycon-styleguide"; +import { FormattedMessage } from "react-intl"; + +import { useCurrentLanguage } from "~/locale/context"; +import { DeadlineStatus, type MyProfileWithSubmissionsQuery } from "~/types"; + +import { createHref } from "../link"; + +type Props = { + deadline: MyProfileWithSubmissionsQuery["conference"]["deadline"]; +}; + +export const NoGrant = ({ deadline }: Props) => { + const deadlineStatus = deadline?.status; + const language = useCurrentLanguage(); + + return ( + + + + + + + {!deadline && ( + + )} + {deadlineStatus === DeadlineStatus.HappeningNow && ( + + )} + {deadlineStatus === DeadlineStatus.InThePast && ( + + )} + {deadlineStatus === DeadlineStatus.InTheFuture && ( + + )} + + {(!deadline || + deadlineStatus === DeadlineStatus.HappeningNow || + deadlineStatus === DeadlineStatus.InTheFuture) && ( + <> + + + + )} + + ); +}; diff --git a/frontend/src/components/my-grant-profile-page-handler/profile-with-my-grant.graphql b/frontend/src/components/my-grant-profile-page-handler/profile-with-my-grant.graphql new file mode 100644 index 0000000000..735e4a31c9 --- /dev/null +++ b/frontend/src/components/my-grant-profile-page-handler/profile-with-my-grant.graphql @@ -0,0 +1,33 @@ +query MyProfileWithGrant($conference: String!) { + me { + id + name + fullName + email + + grant(conference: $conference) { + id + status + name + fullName + ageGroup + travellingFrom + gender + occupation + applicantReplyDeadline + + needVisa + needsFundsForTravel + needAccommodation + grantType + } + } + conference(code: $conference) { + id + deadline(type: "grants") { + id + status + end + } + } +} diff --git a/frontend/src/components/my-grant-profile-page-handler/sidebar.tsx b/frontend/src/components/my-grant-profile-page-handler/sidebar.tsx new file mode 100644 index 0000000000..6aecb7b3c2 --- /dev/null +++ b/frontend/src/components/my-grant-profile-page-handler/sidebar.tsx @@ -0,0 +1,101 @@ +import { + CardPart, + MultiplePartsCard, + Spacer, + Tag, + Text, + VerticalStack, +} from "@python-italia/pycon-styleguide"; +import type { Color } from "@python-italia/pycon-styleguide/dist/types"; +import type React from "react"; +import { Status as GrantStatus, type GrantType } from "~/types"; + +import { FormattedMessage } from "react-intl"; + +type Props = { + status: GrantStatus; + grantType: GrantType; + needsFundsForTravel: boolean; + needAccommodation: boolean; +}; + +export const Sidebar = ({ + status, + grantType, + needsFundsForTravel, + needAccommodation, +}: Props) => { + return ( + + }> + + + + + + }> + + + + }> + + + + {needsFundsForTravel && ( + + + + )} + + {needAccommodation && ( + + + + )} + + + + ); +}; + +const GrantInfo = ({ + label, + children, +}: { + children: React.ReactNode; + label?: React.ReactNode; +}) => ( + + + {label} + + + + + {children} + + +); + +const getStatusColor = (status: GrantStatus): Color => { + switch (status) { + case GrantStatus.Pending: + return "yellow"; + case GrantStatus.Refused: + case GrantStatus.Rejected: + return "red"; + case GrantStatus.Approved: + return "green"; + case GrantStatus.Confirmed: + return "success"; + case GrantStatus.DidNotAttend: + return "red"; + case GrantStatus.WaitingForConfirmation: + return "coral"; + case GrantStatus.WaitingList: + case GrantStatus.WaitingListMaybe: + return "grey"; + } +}; diff --git a/frontend/src/components/profile-page-handler/index.tsx b/frontend/src/components/profile-page-handler/index.tsx index 187976aba6..2f14df992f 100644 --- a/frontend/src/components/profile-page-handler/index.tsx +++ b/frontend/src/components/profile-page-handler/index.tsx @@ -119,6 +119,16 @@ export const ProfilePageHandler = () => { icon: "circle", iconBackground: "purple", }, + { + id: "grants", + link: createHref({ + path: "/profile/my-grant", + locale: language, + }), + label: , + icon: "star", + iconBackground: "yellow", + }, isStaffOrSponsor ? { id: "sponsors", diff --git a/frontend/src/components/profile/my-grant.tsx b/frontend/src/components/profile/my-grant.tsx deleted file mode 100644 index 99a5a96884..0000000000 --- a/frontend/src/components/profile/my-grant.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Heading, Link, Section, Text } from "@python-italia/pycon-styleguide"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import { Alert } from "~/components/alert"; -import { useCurrentLanguage } from "~/locale/context"; -import { useMyGrantQuery } from "~/types"; - -import { createHref } from "../link"; - -export const MyGrant = () => { - const code = process.env.conferenceCode; - const language = useCurrentLanguage(); - const { loading, error, data } = useMyGrantQuery({ - errorPolicy: "all", - variables: { - conference: code, - }, - skip: typeof window === "undefined", - }); - - if (loading) { - return null; - } - - if (!error && data!.me!.grant === undefined) { - return null; - } - - return ( - <> -
- - - -
-
- {error && {error.message}} - - {data && ( - - - - - - - ), - }} - /> - - )} -
- - ); -}; diff --git a/frontend/src/components/schedule-event-detail/index.tsx b/frontend/src/components/schedule-event-detail/index.tsx index d0771cac98..7a7ef5b484 100644 --- a/frontend/src/components/schedule-event-detail/index.tsx +++ b/frontend/src/components/schedule-event-detail/index.tsx @@ -20,6 +20,7 @@ import { compile } from "~/helpers/markdown"; import { useCurrentLanguage } from "~/locale/context"; import { Fragment } from "react"; +import { TableItemHeader } from "~/components/table-item-header"; import type { ProposalMaterial, TalkQueryResult } from "~/types"; import { ParticipantInfoSection } from "../participant-info-section"; import { EventTag } from "./event-tag"; @@ -163,9 +164,9 @@ export const ScheduleEventDetail = ({ )} {elevatorPitch && ( <> - + <TableItemHeader> <FormattedMessage id="scheduleEventDetail.elevatorPitch" /> - + {compile(elevatorPitch).tree} @@ -175,9 +176,9 @@ export const ScheduleEventDetail = ({ )} {abstract && ( <> - + <TableItemHeader> <FormattedMessage id="scheduleEventDetail.abstract" /> - + {compile(abstract).tree} @@ -187,9 +188,9 @@ export const ScheduleEventDetail = ({ )} {tags && ( <> - + <TableItemHeader> <FormattedMessage id="scheduleEventDetail.tags" /> - + {tags.join(", ")} @@ -239,12 +240,6 @@ const YouTubeSection = ({ youtubeVideoId }: { youtubeVideoId: string }) => { ); }; -const Title = ({ children }: { children: React.ReactNode }) => ( - - {children} - -); - const Materials = ({ materials, }: { @@ -252,9 +247,9 @@ const Materials = ({ }) => { return ( <> - + <TableItemHeader> <FormattedMessage id="scheduleEventDetail.materials" /> - +
    {materials.map((material) => ( diff --git a/frontend/src/components/table-item-header/index.tsx b/frontend/src/components/table-item-header/index.tsx new file mode 100644 index 0000000000..85942c96d5 --- /dev/null +++ b/frontend/src/components/table-item-header/index.tsx @@ -0,0 +1,9 @@ +import { Text } from "@python-italia/pycon-styleguide"; + +export const TableItemHeader = ({ + children, +}: { children: React.ReactNode }) => ( + + {children} + +); diff --git a/frontend/src/helpers/country-utils.ts b/frontend/src/helpers/country-utils.ts new file mode 100644 index 0000000000..4626b5255c --- /dev/null +++ b/frontend/src/helpers/country-utils.ts @@ -0,0 +1,7 @@ +export const getCountryLabel = ( + countries: { label: string; value: string; disabled: boolean }[], + value: string, +): string | undefined => { + const country = countries.find((country) => country.value === value); + return country ? country.label : undefined; +}; diff --git a/frontend/src/locale/index.ts b/frontend/src/locale/index.ts index db26ad4652..581a157620 100644 --- a/frontend/src/locale/index.ts +++ b/frontend/src/locale/index.ts @@ -118,6 +118,62 @@ Let's get in touch to find the best solution for your business' needs!`, "profile.welcome": "Ciao {name}!", "profile.myProfile": "My Profile", + + "profile.myGrant": "My Grant", + "profile.myGrant.nextSteps": "Next steps", + "profile.myGrant.grantType": "Type of Grant", + "profile.myGrant.appliedFor": "Applied for", + "profile.myGrant.appliedFor.ticket": "Ticket", + "profile.myGrant.appliedFor.travel": "Travel", + "profile.myGrant.appliedFor.accommodation": "Accommodation", + "profile.myGrant.status": "Status", + "profile.myGrant.status.pending": "Pending", + "profile.myGrant.status.pending.nextSteps": + "Your application is under review. No further action is needed from you at this moment.", + "profile.myGrant.status.rejected": "Rejected", + "profile.myGrant.status.rejected.nextSteps": + "Unfortunately, your grant request has been rejected due to limited funds and a high number of applications.", + "profile.myGrant.status.approved": "Approved", + "profile.myGrant.status.approved.nextSteps": + "Your grant request has been approved! We will send you an email very soon with further instructions.", + "profile.myGrant.status.waiting_list": "Waiting List", + "profile.myGrant.status.waiting_list.nextSteps": + "You are on the waiting list. We will notify you as soon as we can accept your grant.", + "profile.myGrant.status.waiting_list_maybe": "Waiting List", + "profile.myGrant.status.waiting_list_maybe.nextSteps": + "You are on the waiting list. We will notify you as soon as we can accept your grant.", + "profile.myGrant.status.waiting_for_confirmation": + "Waiting for Confirmation", + "profile.myGrant.status.waiting_for_confirmation.nextSteps": `Congratulations, your grant request has been approved! + +Use the 'Manage' button on the page to confirm or decline the grant. You have until {replyDeadline} to confirm or decline the grant, after which your grant will be automatically revoked. +`, + "profile.myGrant.status.refused": "Refused", + "profile.myGrant.status.refused.nextSteps": + "You have declined the grant offer. If this was a mistake, please contact us immediately.", + "profile.myGrant.status.confirmed": "Confirmed", + "profile.myGrant.status.confirmed.nextSteps": + "Thank you for confirming your grant. We look forward to seeing you at the conference! We will send you the voucher for the ticket soon, please check your email regularly.", + "profile.myGrant.status.did_not_attend": "Did Not Attend", + "profile.myGrant.status.did_not_attend.nextSteps": + "You did not attend the conference.", + "profile.myGrant.edit": "Edit", + "profile.myGrant.editInfo": + "Ensure all the information provided are correct. You have until {editDeadline} to edit your information.", + "profile.myGrant.manage": "Manage", + "profile.myGrant.manage.warning": `If you cannot attend the conference anymore, please let us know by clicking the 'Manage' button to refuse your grant as soon as possible. + +Failing to notify us may impact your eligibility for financial aid at future events.`, + "profile.myGrant.noGrant.heading": "You haven't requested a grant", + "profile.myGrant.noGrant.body.deadlineNull": + "Check our information page for more details", + "profile.myGrant.noGrant.body.canSubmit": + "If you're facing financial difficulties and wish to attend PyCon Italia, our grant application form is currently open!", + "profile.myGrant.noGrant.submitGrant": "Find out more", + "profile.myGrant.noGrant.body.closed": + "The grant application form is currently closed. Unfortunately we can't accept new applications after our deadline.", + "profile.myGrant.noGrant.body.openingSoon": + "Our grant application form will open soon! Check our information page for more details.", "profile.logout": "Sign Out", "profile.logout.title": "Sign Out", "profile.logout.body": "{name}, are you sure you want to sign out?", @@ -641,8 +697,8 @@ We look forward to reading about you and hope to see you at PyCon Italia 2024! "grants.form.sendingRequest": "Sending grant request", "grants.form.sent": - "Grant request sent! We will send you an email as soon as we have updates! Cick {linkGrant} to edit it.", - "grants.form.sent.linkGrant.text": "here", + "Grant request sent! We will email you as soon as we have updates. Meanwhile, you can check the status and edit your grant on your {linkMyGrant} until {grantsDeadline}.", + "grants.form.sent.linkMyGrant.text": "profile page", "grants.form.aboutYou": "About you", "grants.form.yourGrant": "Your grant", "grants.form.youAndPython": "You and Python", @@ -1399,8 +1455,8 @@ Non vediamo l'ora di leggere la tua storia e speriamo di vederti a PyCon Italia `, "grants.form.sendingRequest": "Invio richiesta in corso", "grants.form.sent": - "Richiesta inviata! Ti contatteremo via email appena ci saranno novità! Clicca {linkGrant} per editarlo.", - "grants.form.sent.linkGrant.text": "qui", + "Richiesta di grant inviata! Ti invieremo un'email appena ci saranno novità! Nel frattempo, puoi controllare lo stato e modificare il tuo grant nella tua {linkMyGrant} fino al {grantsDeadline}.", + "grants.form.sent.linkMyGrant.text": "pagina del profilo", "grants.form.aboutYou": "Su di te", "grants.form.optionalInformation": "Informazioni opzionali", "grants.form.optionalInformation.description": @@ -1431,10 +1487,11 @@ Non vediamo l'ora di leggere la tua storia e speriamo di vederti a PyCon Italia "grants.form.fields.occupation.description": "Seleziona la propria attuale occupazione.", "grants.form.fields.occupation.values.selectOption": "Seleziona un'opzione", - "grants.form.fields.occupation.values.developer": "Sviluppatore", - "grants.form.fields.occupation.values.student": "Studente", - "grants.form.fields.occupation.values.researcher": "Ricercatore", - "grants.form.fields.occupation.values.unemployed": "Disoccupato", + "grants.form.fields.occupation.values.developer": + "Sviluppatore/Sviluppatrice", + "grants.form.fields.occupation.values.student": "Studente/Studentessa", + "grants.form.fields.occupation.values.researcher": "Ricerca", + "grants.form.fields.occupation.values.unemployed": "Disoccupato/a", "grants.form.fields.occupation.values.other": "Altro", "grants.form.fields.grantType": "Che tipo di grant stai richiedendo?", "grants.form.fields.grantType.description": @@ -1911,6 +1968,59 @@ Affrettati a comprare il biglietto!`, "profile.myProfile": "Il mio profilo", "profile.myProposals": "Le mie proposte", + "profile.myGrant": "Il mio grant", + "profile.myGrant.nextSteps": "I prossimi passi", + "profile.myGrant.grantType": "Tipo di Grant", + "profile.myGrant.appliedFor": "Richiesto", + "profile.myGrant.appliedFor.ticket": "Biglietto", + "profile.myGrant.appliedFor.travel": "Viaggio", + "profile.myGrant.appliedFor.accommodation": "Alloggio", + "profile.myGrant.edit": "Modifica", + "profile.myGrant.editInfo": + "Assicurati che tutte le informazioni fornite siano corrette. Hai tempo fino al {editDeadline} per modificare il tuo grant.", + "profile.myGrant.manage": "Gestisci", + "profile.myGrant.manage.warning": `Se per qualsiasi motivo non puoi più partecipare alla conferenza, ti preghiamo di comunicarcelo al più presto cliccando sul pulsante 'Gestisci' per rifiutare il grant. + +Il mancato avviso potrebbe influire sulla tua idoneità a ricevere assistenza finanziaria per eventi futuri.`, + "profile.myGrant.status": "Stato", + "profile.myGrant.status.pending": "In attesa", + "profile.myGrant.status.pending.nextSteps": + "La tua domanda è in fase di revisione. Non è richiesta alcuna azione in questo momento.", + "profile.myGrant.status.rejected": "Respinto", + "profile.myGrant.status.rejected.nextSteps": + "Purtroppo, a causa di fondi limitati e del grande numero di richieste, la tua richiesta di grant non è stata approvata.", + "profile.myGrant.status.approved": "Approvato", + "profile.myGrant.status.approved.nextSteps": + "La tua richiesta di grant è stata approvata! Ti invieremo un'email molto presto con ulteriori istruzioni.", + "profile.myGrant.status.waiting_list": "Lista d'attesa", + "profile.myGrant.status.waiting_list.nextSteps": + "Sei in lista d'attesa. Ti informeremo non appena potremo accettare il tuo grant.", + "profile.myGrant.status.waiting_list_maybe": "Lista d'attesa", + "profile.myGrant.status.waiting_list_maybe.nextSteps": + "Sei in lista d'attesa. Ti informeremo non appena potremo accettare il tuo grant.", + "profile.myGrant.status.waiting_for_confirmation": "Da confermare", + "profile.myGrant.status.waiting_for_confirmation.nextSteps": `Congratulazioni, la tua richiesta di grant è stata approvata! + +Usa il pulsante 'Gestisci' nella pagina per confermare o rifiutare il grant. Hai tempo fino al {replyDeadline} per confermare o rifiutare il grant, dopodiché il tuo grant sarà automaticamente revocato.`, + "profile.myGrant.status.refused": "Rifiutato", + "profile.myGrant.status.refused.nextSteps": + "Hai rifiutato l'offerta di grant. Se si tratta di un errore, contattaci immediatamente.", + "profile.myGrant.status.confirmed": "Confermato", + "profile.myGrant.status.confirmed.nextSteps": + "Grazie per aver confermato il tuo grant. Non vediamo l'ora di vederti alla conferenza! Ti invieremo a breve il voucher per il biglietto, tieni controllata la tua email", + "profile.myGrant.status.did_not_attend": "Non ha partecipato", + "profile.myGrant.status.did_not_attend.nextSteps": + "Non hai partecipato alla conferenza.", + "profile.myGrant.noGrant.heading": "Non hai richiesto un grant", + "profile.myGrant.noGrant.body.deadlineNull": + "Visita la nostra pagina informativa per ulteriori dettagli.", + "profile.myGrant.noGrant.body.canSubmit": + "Se ti trovi in difficoltà economica e desideri partecipare a PyCon Italia, il nostro modulo per la richiesta di grant è al momento aperto.", + "profile.myGrant.noGrant.submitGrant": "Scopri di più", + "profile.myGrant.noGrant.body.closed": + "Il form per la richiesta di grant è al momento chiuso. Purtroppo non possiamo accettare nuove richieste dopo la scadenza.", + "profile.myGrant.noGrant.body.openingSoon": + "Il form per la richiesta del grant aprirà presto! Visita la nostra pagina informativa per ulteriori dettagli.", "profile.editProfile.generalInformation": "Informazioni generali", "profile.editProfile.emailPreferences": "Preferenze Email", "profile.tickets.attendeeName": "Nome partecipante", diff --git a/frontend/src/components/profile/my-grant.graphql b/frontend/src/pages/grants/edit/my-grant.graphql similarity index 100% rename from frontend/src/components/profile/my-grant.graphql rename to frontend/src/pages/grants/edit/my-grant.graphql diff --git a/frontend/src/pages/grants/index.tsx b/frontend/src/pages/grants/index.tsx index 75c41f02f8..cb1a6c6980 100644 --- a/frontend/src/pages/grants/index.tsx +++ b/frontend/src/pages/grants/index.tsx @@ -5,7 +5,7 @@ import { FormattedMessage } from "react-intl"; import type { GetServerSideProps } from "next"; import { addApolloState, getApolloClient } from "~/apollo/client"; -import { MyGrantOrForm } from "~/components/grant-form"; +import { GrantSendForm } from "~/components/grant-form"; import { MetaTags } from "~/components/meta-tags"; import { formatDeadlineDateTime } from "~/helpers/deadlines"; import { prefetchSharedQueries } from "~/helpers/prefetch"; @@ -76,7 +76,7 @@ export const GrantsPage = () => { {(text) => } - {status === DeadlineStatus.HappeningNow && } + {status === DeadlineStatus.HappeningNow && } {status === DeadlineStatus.InTheFuture && ( )} @@ -102,18 +102,33 @@ export const getServerSideProps: GetServerSideProps = async ({ const client = getApolloClient(null, req.cookies); try { - await Promise.all([ - prefetchSharedQueries(client, locale), - queryGrantDeadline(client, { + const [ + { + data: { + me: { grant }, + }, + }, + ] = await Promise.all([ + queryMyGrant(client, { conference: process.env.conferenceCode, }), - queryMyGrant(client, { + prefetchSharedQueries(client, locale), + queryGrantDeadline(client, { conference: process.env.conferenceCode, }), queryCurrentUser(client, { conference: process.env.conferenceCode, }), ]); + + if (grant) { + return { + redirect: { + destination: "/profile/my-grant", + permanent: false, + }, + }; + } } catch (e) { return { redirect: { diff --git a/frontend/src/pages/grants/reply/index.tsx b/frontend/src/pages/grants/reply/index.tsx index 685f9f58db..4b42640e5a 100644 --- a/frontend/src/pages/grants/reply/index.tsx +++ b/frontend/src/pages/grants/reply/index.tsx @@ -7,7 +7,6 @@ import { Section, Spacer, Text, - Textarea, VerticalStack, } from "@python-italia/pycon-styleguide"; import React, { useCallback, useEffect } from "react"; @@ -67,7 +66,7 @@ const GrantReply = () => { const language = useCurrentLanguage(); const code = process.env.conferenceCode; - const [formState, { radio, text }] = useFormState({ + const [formState, { radio }] = useFormState({ option: null, }); diff --git a/frontend/src/pages/profile/my-grant.tsx b/frontend/src/pages/profile/my-grant.tsx new file mode 100644 index 0000000000..9886d8d3cb --- /dev/null +++ b/frontend/src/pages/profile/my-grant.tsx @@ -0,0 +1,49 @@ +import type { GetServerSideProps } from "next"; + +import { addApolloState, getApolloClient } from "~/apollo/client"; +import { prefetchSharedQueries } from "~/helpers/prefetch"; +import { queryCountries, queryMyProfileWithGrant } from "~/types"; + +export const getServerSideProps: GetServerSideProps = async ({ + req, + locale, +}) => { + const identityToken = req.cookies.pythonitalia_sessionid; + if (!identityToken) { + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + } + + const client = getApolloClient(null, req.cookies); + + try { + await Promise.all([ + prefetchSharedQueries(client, locale), + queryCountries(client), + queryMyProfileWithGrant(client, { + conference: process.env.conferenceCode, + }), + ]); + } catch (e) { + return { + redirect: { + destination: "/login", + permanent: false, + }, + }; + } + + return addApolloState( + client, + { + props: {}, + }, + null, + ); +}; + +export { MyGrantProfilePageHandler as default } from "~/components/my-grant-profile-page-handler";