Skip to content

Commit

Permalink
feat: airtable fix
Browse files Browse the repository at this point in the history
  • Loading branch information
nevo-david committed Nov 5, 2023
1 parent ac2a5e4 commit 2d48d83
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 10 deletions.
8 changes: 1 addition & 7 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ model Winners {
year Int
userId String
lastDateClaim DateTime
type WinnerType
type String
user User @relation(fields: [userId], references: [id])
claimed DateTime?
Expand All @@ -203,12 +203,6 @@ model Winners {
@@index([claimed])
}

enum WinnerType {
EXTRA
COMPETITION
NOVU
}

model VerificationToken {
identifier String
token String @unique
Expand Down
134 changes: 134 additions & 0 deletions src/components/pages/claim/hero/form.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { getNames } from 'country-list';
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import { useFormContext } from 'react-hook-form';

import Input from '../../../shared/button/input';
import Select from '../../../shared/button/select';

const Form = ({ info }) => {
const {
register,
formState: { errors, isSubmitSuccessful },
} = useFormContext();
return (
<>
<div>
Select Prizes to claim:
{info.map((winner) => (
<div className="mt-2 flex">
<div>
<input
disabled={isSubmitSuccessful}
{...register('type', { required: true })}
name="type"
value={winner.id}
type="checkbox"
/>
</div>
<div className="ml-2">
{winner.type === 'COMPETITION' && 'Competition Winner'}
{winner.type === 'NOVU' && 'Novu Swag Claim'}
{winner.type === 'EXTRA' && 'Giveaway or other'} - Expires on{' '}
{moment.utc(winner.lastDateClaim).local().format('DD/MM/YYYY HH:mm')}
</div>
</div>
))}
{!!errors.type && (
<div className="mt-3" style={{ color: 'red' }}>
You must select a prize to claim
</div>
)}
</div>
<Input
name="first_name"
extra={{ minLength: 2, required: true }}
label="First Name"
type="text"
/>
<Input
extra={{ minLength: 2, required: true }}
label="Last Name"
type="text"
name="last_name"
/>
<Input
extra={{
required: true,
pattern: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i,
}}
label="Email"
type="text"
name="email"
/>
<Input
label="Phone Number"
type="text"
name="phone_number"
placeHolder="+972"
extra={{
required: true,
pattern:
/\+(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,
}}
/>
<Input
extra={{ minLength: 5, required: true }}
label="Address"
type="text"
name="shipping_address1"
/>
<Input label="Address 2" type="text" name="shipping_address2" />
<Input
extra={{ required: true, minLength: 2 }}
label="City"
type="text"
name="shipping_city"
/>
<Select label="Country" name="shipping_country">
<option value="">-- Choose Country --</option>
{getNames().map((c) => (
<option value={c}>{c}</option>
))}
</Select>
<Input extra={{ required: true }} label="State" type="text" name="shipping_state" />
<Input extra={{ required: true, minLength: 3 }} label="ZIP" type="text" name="shipping_zip" />
<Select label="Shirt Size" name="shirt_size">
<option value="">-- Choose Shirt Size --</option>
<option value="Small">Small</option>
<option value="Medium">Medium</option>
<option value="Large">Large</option>
<option value="X-Large">Extra Large</option>
<option value="2X-Large">2 Extra Large</option>
</Select>
<button
type="submit"
disabled={isSubmitSuccessful}
className={`${
isSubmitSuccessful && 'pointer-events-none opacity-50'
} cta-btn-animation relative flex max-w-full cursor-pointer items-center justify-center leading-none`}
>
<svg
className="cta-btn-animation-border xs:w-full"
width="200"
height="59"
viewBox="0 0 268 59"
fill="none"
>
<path d="M1 58V1H251.586L267 16.4142V58H1Z" stroke="white" strokeWidth="2" />
</svg>

<div className="absolute inset-0 flex items-center justify-center space-x-2.5">
<span className="text-lg sm:text-[18px]">Claim Swag!</span>
</div>
</button>
</>
);
};

Form.propTypes = {
info: PropTypes.object,
};

export default Form;
83 changes: 83 additions & 0 deletions src/components/pages/claim/hero/hero.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';
import PropTypes from 'prop-types';
import React from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import { toast } from 'react-toastify';

import Hero2 from 'components/pages/no-win/hero';

import Novu from '../../../shared/socials/novu';

import Form from './form';
import NoLogged from './no-logged';

const title = '>> Claim Prizes 🎉';

const Hero = ({ info }) => {
const { push } = useRouter();

const all = useForm({
defaultValues: {
first_name: '',
},
});

const onSubmit = (data) => {
(async () => {
const newData = await fetch('/api/claim', {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify({ ...data, type: Array.isArray(data.type) ? data.type : [data.type] }),
});

const isValid = newData.json();
if (isValid.invalid) {
toast.error('Invalid submission, please refresh the page and try again');
return;
}

push('/claim-success');
})();
};

const { status } = useSession();
if (status === 'loading') {
return <></>;
}
if (status !== 'authenticated') {
return <NoLogged />;
}

if (info.length === 0) {
return <Hero2 />;
}

return (
<FormProvider {...all}>
<form
className="safe-paddings relative mb-20 min-h-[600px]"
onSubmit={all.handleSubmit(onSubmit)}
>
<div className="container relative z-10 flex h-full flex-col items-center justify-center pt-10">
<Novu />
<h1 className="leading-tight mt-10 font-mono text-xl font-bold uppercase lg:text-[50px] md:text-[40px] xs:text-[32px]">
{title}
</h1>
<div className="w-500 mt-10 grid w-full max-w-[800px] grid-cols-[1fr] gap-x-5 gap-y-5">
<Form info={info} />
</div>
</div>
</form>
</FormProvider>
);
};

Hero.propTypes = {
info: PropTypes.object,
};

export default Hero;
Binary file modified src/components/pages/claim/hero/images/bg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/components/pages/claim/hero/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './hero';
36 changes: 36 additions & 0 deletions src/components/pages/claim/hero/no-logged.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Image from 'next/image';
import React from 'react';

import SignUpButton from 'components/shared/sign-up-button';
import Socials from 'components/shared/socials';

import bg from './images/bg.jpg';

const title = '>>Sign in!';
const description = <>Oh no, you have probably signed out of the system</>;

const NoLogged = () => (
<section className="safe-paddings relative h-screen min-h-[600px]">
<div className="container relative z-10 flex h-full flex-col items-center justify-center">
<h1 className="leading-tight font-mono text-xl font-bold uppercase lg:text-[50px] md:text-[40px] xs:text-[32px]">
{title}
</h1>
<p className="mt-10 text-center text-lg sm:mt-6 sm:text-base">{description}</p>
<SignUpButton alternativeText="Sign In" className="relative z-10 mx-auto mt-10" />
<Socials className="absolute bottom-20 md:bottom-12" />
</div>

<Image
className="absolute left-1/2 top-0 min-h-screen w-full min-w-[1920px] -translate-x-1/2 md:min-w-[1230px]"
src={bg}
width={1920}
height={1080}
loading="eager"
alt=""
priority
aria-hidden
/>
</section>
);

export default NoLogged;
3 changes: 1 addition & 2 deletions src/layouts/layouts/layout-main/layout-main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
// import ReactTooltip from 'react-tooltip';

import Banner from 'components/shared/banner';
import MobileMenu from 'components/shared/mobile-menu';
import Footer from 'components/shared/old-footer';
import Header from 'components/shared/old-header';
Expand Down Expand Up @@ -34,7 +33,7 @@ const LayoutMain = ({
closeButton={false}
/>
<div className="relative flex min-h-screen flex-col">
<Banner />
{/* <Banner /> */}
<Header
absolute={absolute}
isMobileMenuOpen={isMobileMenuOpen}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/api/claim.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default async function handler(req, res) {
winnersMap.map(async (win) => {
await airTable
.base(process.env.AIRTABLE_BASE)
.table('Hacksquad 2022')
.table('Hacksquad 2023')
.create({
first_name: req.body.first_name,
last_name: req.body.last_name,
Expand Down

1 comment on commit 2d48d83

@vercel
Copy link

@vercel vercel bot commented on 2d48d83 Nov 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.