Skip to content

Commit 2d48d83

Browse files
author
Nevo David
committed
feat: airtable fix
1 parent ac2a5e4 commit 2d48d83

File tree

8 files changed

+257
-10
lines changed

8 files changed

+257
-10
lines changed

prisma/schema.prisma

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ model Winners {
192192
year Int
193193
userId String
194194
lastDateClaim DateTime
195-
type WinnerType
195+
type String
196196
user User @relation(fields: [userId], references: [id])
197197
claimed DateTime?
198198
@@ -203,12 +203,6 @@ model Winners {
203203
@@index([claimed])
204204
}
205205

206-
enum WinnerType {
207-
EXTRA
208-
COMPETITION
209-
NOVU
210-
}
211-
212206
model VerificationToken {
213207
identifier String
214208
token String @unique
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { getNames } from 'country-list';
2+
import moment from 'moment';
3+
import PropTypes from 'prop-types';
4+
import React from 'react';
5+
import { useFormContext } from 'react-hook-form';
6+
7+
import Input from '../../../shared/button/input';
8+
import Select from '../../../shared/button/select';
9+
10+
const Form = ({ info }) => {
11+
const {
12+
register,
13+
formState: { errors, isSubmitSuccessful },
14+
} = useFormContext();
15+
return (
16+
<>
17+
<div>
18+
Select Prizes to claim:
19+
{info.map((winner) => (
20+
<div className="mt-2 flex">
21+
<div>
22+
<input
23+
disabled={isSubmitSuccessful}
24+
{...register('type', { required: true })}
25+
name="type"
26+
value={winner.id}
27+
type="checkbox"
28+
/>
29+
</div>
30+
<div className="ml-2">
31+
{winner.type === 'COMPETITION' && 'Competition Winner'}
32+
{winner.type === 'NOVU' && 'Novu Swag Claim'}
33+
{winner.type === 'EXTRA' && 'Giveaway or other'} - Expires on{' '}
34+
{moment.utc(winner.lastDateClaim).local().format('DD/MM/YYYY HH:mm')}
35+
</div>
36+
</div>
37+
))}
38+
{!!errors.type && (
39+
<div className="mt-3" style={{ color: 'red' }}>
40+
You must select a prize to claim
41+
</div>
42+
)}
43+
</div>
44+
<Input
45+
name="first_name"
46+
extra={{ minLength: 2, required: true }}
47+
label="First Name"
48+
type="text"
49+
/>
50+
<Input
51+
extra={{ minLength: 2, required: true }}
52+
label="Last Name"
53+
type="text"
54+
name="last_name"
55+
/>
56+
<Input
57+
extra={{
58+
required: true,
59+
pattern: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i,
60+
}}
61+
label="Email"
62+
type="text"
63+
name="email"
64+
/>
65+
<Input
66+
label="Phone Number"
67+
type="text"
68+
name="phone_number"
69+
placeHolder="+972"
70+
extra={{
71+
required: true,
72+
pattern:
73+
/\+(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{1,14}$/i,
74+
}}
75+
/>
76+
<Input
77+
extra={{ minLength: 5, required: true }}
78+
label="Address"
79+
type="text"
80+
name="shipping_address1"
81+
/>
82+
<Input label="Address 2" type="text" name="shipping_address2" />
83+
<Input
84+
extra={{ required: true, minLength: 2 }}
85+
label="City"
86+
type="text"
87+
name="shipping_city"
88+
/>
89+
<Select label="Country" name="shipping_country">
90+
<option value="">-- Choose Country --</option>
91+
{getNames().map((c) => (
92+
<option value={c}>{c}</option>
93+
))}
94+
</Select>
95+
<Input extra={{ required: true }} label="State" type="text" name="shipping_state" />
96+
<Input extra={{ required: true, minLength: 3 }} label="ZIP" type="text" name="shipping_zip" />
97+
<Select label="Shirt Size" name="shirt_size">
98+
<option value="">-- Choose Shirt Size --</option>
99+
<option value="Small">Small</option>
100+
<option value="Medium">Medium</option>
101+
<option value="Large">Large</option>
102+
<option value="X-Large">Extra Large</option>
103+
<option value="2X-Large">2 Extra Large</option>
104+
</Select>
105+
<button
106+
type="submit"
107+
disabled={isSubmitSuccessful}
108+
className={`${
109+
isSubmitSuccessful && 'pointer-events-none opacity-50'
110+
} cta-btn-animation relative flex max-w-full cursor-pointer items-center justify-center leading-none`}
111+
>
112+
<svg
113+
className="cta-btn-animation-border xs:w-full"
114+
width="200"
115+
height="59"
116+
viewBox="0 0 268 59"
117+
fill="none"
118+
>
119+
<path d="M1 58V1H251.586L267 16.4142V58H1Z" stroke="white" strokeWidth="2" />
120+
</svg>
121+
122+
<div className="absolute inset-0 flex items-center justify-center space-x-2.5">
123+
<span className="text-lg sm:text-[18px]">Claim Swag!</span>
124+
</div>
125+
</button>
126+
</>
127+
);
128+
};
129+
130+
Form.propTypes = {
131+
info: PropTypes.object,
132+
};
133+
134+
export default Form;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useRouter } from 'next/router';
2+
import { useSession } from 'next-auth/react';
3+
import PropTypes from 'prop-types';
4+
import React from 'react';
5+
import { useForm, FormProvider } from 'react-hook-form';
6+
import { toast } from 'react-toastify';
7+
8+
import Hero2 from 'components/pages/no-win/hero';
9+
10+
import Novu from '../../../shared/socials/novu';
11+
12+
import Form from './form';
13+
import NoLogged from './no-logged';
14+
15+
const title = '>> Claim Prizes 🎉';
16+
17+
const Hero = ({ info }) => {
18+
const { push } = useRouter();
19+
20+
const all = useForm({
21+
defaultValues: {
22+
first_name: '',
23+
},
24+
});
25+
26+
const onSubmit = (data) => {
27+
(async () => {
28+
const newData = await fetch('/api/claim', {
29+
headers: {
30+
Accept: 'application/json',
31+
'Content-Type': 'application/json',
32+
},
33+
method: 'POST',
34+
body: JSON.stringify({ ...data, type: Array.isArray(data.type) ? data.type : [data.type] }),
35+
});
36+
37+
const isValid = newData.json();
38+
if (isValid.invalid) {
39+
toast.error('Invalid submission, please refresh the page and try again');
40+
return;
41+
}
42+
43+
push('/claim-success');
44+
})();
45+
};
46+
47+
const { status } = useSession();
48+
if (status === 'loading') {
49+
return <></>;
50+
}
51+
if (status !== 'authenticated') {
52+
return <NoLogged />;
53+
}
54+
55+
if (info.length === 0) {
56+
return <Hero2 />;
57+
}
58+
59+
return (
60+
<FormProvider {...all}>
61+
<form
62+
className="safe-paddings relative mb-20 min-h-[600px]"
63+
onSubmit={all.handleSubmit(onSubmit)}
64+
>
65+
<div className="container relative z-10 flex h-full flex-col items-center justify-center pt-10">
66+
<Novu />
67+
<h1 className="leading-tight mt-10 font-mono text-xl font-bold uppercase lg:text-[50px] md:text-[40px] xs:text-[32px]">
68+
{title}
69+
</h1>
70+
<div className="w-500 mt-10 grid w-full max-w-[800px] grid-cols-[1fr] gap-x-5 gap-y-5">
71+
<Form info={info} />
72+
</div>
73+
</div>
74+
</form>
75+
</FormProvider>
76+
);
77+
};
78+
79+
Hero.propTypes = {
80+
info: PropTypes.object,
81+
};
82+
83+
export default Hero;
455 KB
Loading
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './hero';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Image from 'next/image';
2+
import React from 'react';
3+
4+
import SignUpButton from 'components/shared/sign-up-button';
5+
import Socials from 'components/shared/socials';
6+
7+
import bg from './images/bg.jpg';
8+
9+
const title = '>>Sign in!';
10+
const description = <>Oh no, you have probably signed out of the system</>;
11+
12+
const NoLogged = () => (
13+
<section className="safe-paddings relative h-screen min-h-[600px]">
14+
<div className="container relative z-10 flex h-full flex-col items-center justify-center">
15+
<h1 className="leading-tight font-mono text-xl font-bold uppercase lg:text-[50px] md:text-[40px] xs:text-[32px]">
16+
{title}
17+
</h1>
18+
<p className="mt-10 text-center text-lg sm:mt-6 sm:text-base">{description}</p>
19+
<SignUpButton alternativeText="Sign In" className="relative z-10 mx-auto mt-10" />
20+
<Socials className="absolute bottom-20 md:bottom-12" />
21+
</div>
22+
23+
<Image
24+
className="absolute left-1/2 top-0 min-h-screen w-full min-w-[1920px] -translate-x-1/2 md:min-w-[1230px]"
25+
src={bg}
26+
width={1920}
27+
height={1080}
28+
loading="eager"
29+
alt=""
30+
priority
31+
aria-hidden
32+
/>
33+
</section>
34+
);
35+
36+
export default NoLogged;

src/layouts/layouts/layout-main/layout-main.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { ToastContainer } from 'react-toastify';
55
import 'react-toastify/dist/ReactToastify.css';
66
// import ReactTooltip from 'react-tooltip';
77

8-
import Banner from 'components/shared/banner';
98
import MobileMenu from 'components/shared/mobile-menu';
109
import Footer from 'components/shared/old-footer';
1110
import Header from 'components/shared/old-header';
@@ -34,7 +33,7 @@ const LayoutMain = ({
3433
closeButton={false}
3534
/>
3635
<div className="relative flex min-h-screen flex-col">
37-
<Banner />
36+
{/* <Banner /> */}
3837
<Header
3938
absolute={absolute}
4039
isMobileMenuOpen={isMobileMenuOpen}

src/pages/api/claim.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default async function handler(req, res) {
5252
winnersMap.map(async (win) => {
5353
await airTable
5454
.base(process.env.AIRTABLE_BASE)
55-
.table('Hacksquad 2022')
55+
.table('Hacksquad 2023')
5656
.create({
5757
first_name: req.body.first_name,
5858
last_name: req.body.last_name,

0 commit comments

Comments
 (0)