Skip to content

Commit ac45566

Browse files
adjust for new cohort form
1 parent bd32625 commit ac45566

File tree

3 files changed

+262
-14
lines changed

3 files changed

+262
-14
lines changed

src/app/cohorts/cohorts.module.css

+74
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,80 @@
3131
color: hsl(var(--white));
3232
}
3333

34+
.notificationFormContainer {
35+
border: 5px solid hsl(var(--light-blue));
36+
max-width: 650px;
37+
margin: 0 auto;
38+
padding: 2rem;
39+
border-radius: 2rem;
40+
}
41+
42+
.notificationFormContainer button {
43+
margin-top: 1rem;
44+
width: 100%;
45+
background-color: hsl(var(--light-blue));
46+
color: hsl(var(--white));
47+
border: none;
48+
border-radius: 5px;
49+
padding: 1rem;
50+
}
51+
52+
.formGroup {
53+
display: flex;
54+
justify-content: space-between;
55+
margin-bottom: 15px;
56+
align-items: center;
57+
}
58+
59+
.formGroup label {
60+
flex: 1;
61+
text-align: left;
62+
}
63+
64+
.formGroup input,
65+
.formGroup textarea,
66+
.formGroup select {
67+
flex: 2;
68+
padding: 10px;
69+
border: 1px solid #ccc;
70+
border-radius: 4px;
71+
}
72+
.formGroup textarea {
73+
resize: vertical;
74+
}
75+
.submit-btn {
76+
display: block;
77+
margin: 0 auto;
78+
padding: 10px 20px;
79+
background-color: #007bff;
80+
color: white;
81+
border: none;
82+
border-radius: 4px;
83+
cursor: pointer;
84+
}
85+
.submit-btn:hover {
86+
background-color: #0056b3;
87+
}
88+
89+
.recaptcha {
90+
display: flex;
91+
justify-content: center;
92+
}
93+
94+
.formMessage {
95+
margin-top: 1rem;
96+
}
97+
98+
.errorMessage {
99+
color: #dc3545;
100+
font-size: 1rem;
101+
}
102+
103+
.successMessage {
104+
color: #198754;
105+
font-size: 1;
106+
}
107+
34108
/* Mobile devices */
35109
@media only screen and (max-width: 850px) {
36110
}

src/app/cohorts/notificationForm.tsx

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { useRef, useState } from 'react';
2+
import ReCAPTCHA from 'react-google-recaptcha';
3+
import styles from './cohorts.module.css';
4+
5+
class NotificationFormClass {
6+
name: string;
7+
email: string;
8+
token: string;
9+
10+
constructor(name: string, email: string, token: string) {
11+
this.name = name;
12+
this.email = email;
13+
this.token = token;
14+
}
15+
}
16+
17+
type Message = {
18+
message: string;
19+
type: 'error' | 'success';
20+
};
21+
22+
const localEnv =
23+
process.env.NODE_ENV === 'development' &&
24+
process.env.NEXT_PUBLIC_APPWRITE_HASKEY === 'false';
25+
26+
const siteKey = process.env.NEXT_PUBLIC_RECAPTCHA_SITEKEY ?? '';
27+
28+
export default function NotificationForm() {
29+
const [formData, setFormData] = useState(
30+
new NotificationFormClass('', '', '')
31+
);
32+
const [message, setMessage] = useState<Message | null>(null);
33+
34+
const captchaRef = useRef<ReCAPTCHA>(null);
35+
36+
const handleChange = (event: any) => {
37+
const { name, value } = event.target;
38+
setFormData({ ...formData, [name]: value });
39+
};
40+
41+
const handleSubmit = async (event: any) => {
42+
event.preventDefault();
43+
const token = localEnv ? 'localEnv' : captchaRef.current?.getValue();
44+
if (!(token || localEnv)) {
45+
console.log('display robot message');
46+
setMessage({
47+
message: 'Are you a robot? Please complete the reCAPTCHA',
48+
type: 'error',
49+
});
50+
return;
51+
}
52+
53+
formData.token = token ?? '';
54+
55+
try {
56+
const responese = await fetch('/api/notificationForm', {
57+
method: 'POST',
58+
headers: {
59+
'Content-Type': 'application/json',
60+
},
61+
body: JSON.stringify(formData),
62+
});
63+
64+
if (!responese.ok) {
65+
setMessage({
66+
message: responese.statusText,
67+
type: 'error',
68+
});
69+
throw new Error(`Failed to submit form: ${responese.statusText}`);
70+
}
71+
72+
setMessage({
73+
message: 'Success! You will be notified when the next cohort opens.',
74+
type: 'success',
75+
});
76+
77+
setFormData(new NotificationFormClass('', '', ''));
78+
} catch (error) {
79+
console.error('error', error);
80+
}
81+
};
82+
83+
return (
84+
<div className={styles.notificationFormContainer}>
85+
<form onSubmit={handleSubmit} method='post'>
86+
<div className={styles.formGroup}>
87+
<label htmlFor='name'>Name</label>
88+
<input
89+
type='text'
90+
id='name'
91+
name='name'
92+
required
93+
value={formData.name}
94+
onChange={handleChange}
95+
/>
96+
</div>
97+
<div className={styles.formGroup}>
98+
<label htmlFor='email'>Email</label>
99+
<input
100+
type='email'
101+
id='email'
102+
name='email'
103+
required
104+
value={formData.email}
105+
onChange={handleChange}
106+
/>
107+
</div>
108+
<div className={styles.recaptcha}>
109+
{!localEnv && <ReCAPTCHA sitekey={siteKey} ref={captchaRef} />}
110+
</div>
111+
<button className='mdText' type='submit'>
112+
Get Notified
113+
</button>
114+
</form>
115+
<div
116+
className={[
117+
styles.formMessage,
118+
message?.type === 'error'
119+
? styles.errorMessage
120+
: styles.successMessage,
121+
].join(' ')}
122+
>
123+
{message?.message}
124+
</div>
125+
</div>
126+
);
127+
}

src/app/cohorts/page.tsx

+61-14
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
'use client';
22

3-
import React from 'react';
3+
import React, { useMemo } from 'react';
44
import styles from './cohorts.module.css';
5-
import OfferingCard from '../components/offeringCard/offeringCard';
65
import CohortCard from '../components/cohortCard/cohortCard';
76
import Section from '../components/Section/section';
8-
9-
// TODO: Add appropriate links
7+
import NotificationForm from './notificationForm';
8+
import { useQuery } from '@tanstack/react-query';
9+
import Button from '../components/button/button';
10+
import { useGlobalState } from '../hooks/useGlobalState/useGlobalState';
1011

1112
interface Group {
1213
id: number;
@@ -16,12 +17,24 @@ interface Group {
1617
imageUrl?: string;
1718
}
1819

20+
interface CohortStatus {
21+
documentId: number;
22+
statusType: string;
23+
message: string;
24+
active: boolean;
25+
}
26+
1927
type CohortData = {
2028
[year: number]: Group[];
2129
};
2230

23-
const cohortStatusMessage =
24-
'Cohorts are currently closed and registration will be announced in Discord when the next one opens.';
31+
const defaultCohortStatusMessage = {
32+
documentId: 0,
33+
statusType: 'closed',
34+
message:
35+
'Cohorts are currently closed and registration will be announced in Discord when the next one opens.',
36+
active: false,
37+
} as CohortStatus;
2538

2639
// Data for the cohorts, add more elements to each year as needed
2740
const cohortData: CohortData = {
@@ -62,9 +75,31 @@ const cohortData: CohortData = {
6275
},
6376
],
6477
};
78+
6579
export default function CohortPage() {
6680
const [selectedYear, setSelectedYear] = React.useState<number>(2024);
6781

82+
const { actionLinks } = useGlobalState();
83+
84+
const { data: cohortStatusResponse, isLoading } = useQuery({
85+
queryKey: ['cohortStatus'],
86+
queryFn: async () => {
87+
const response = await fetch('/api/cohort', { cache: 'no-store' });
88+
return response.json();
89+
},
90+
});
91+
92+
const currentCohortStatusData = useMemo(() => {
93+
if (!cohortStatusResponse) {
94+
return defaultCohortStatusMessage;
95+
}
96+
return cohortStatusResponse ?? defaultCohortStatusMessage;
97+
}, [cohortStatusResponse]);
98+
99+
if (isLoading) {
100+
return <h1>Loading...</h1>;
101+
}
102+
68103
return (
69104
<>
70105
<Section isIntro classNames='bgBlue'>
@@ -78,16 +113,28 @@ export default function CohortPage() {
78113
</p>
79114
</Section>
80115

81-
{/*
82-
//TODO: Uncomment when we find a way to handle cohort registration and notifications
83116
<Section classNames='bgBlue'>
84117
<h2>Cohort Information</h2>
85-
<OfferingCard
86-
text={cohortStatusMessage}
87-
buttonText='Get Notified'
88-
buttonLink='/'
89-
/>
90-
</Section> */}
118+
<p>
119+
{currentCohortStatusData.message}
120+
{currentCohortStatusData.statusType === 'open' && (
121+
<Button
122+
buttonText='Apply Now'
123+
onClick={() => {
124+
if (!actionLinks) return;
125+
126+
const cohortSignupLink = actionLinks.find(
127+
(x: any) => x.linkName === 'cohortSignup'
128+
)?.link;
129+
window.open(cohortSignupLink, '_blank');
130+
}}
131+
/>
132+
)}
133+
</p>
134+
{currentCohortStatusData.statusType === 'closed' && (
135+
<NotificationForm />
136+
)}
137+
</Section>
91138

92139
<Section classNames='bgBlue'>
93140
<h2>Previous Cohorts</h2>

0 commit comments

Comments
 (0)