-
-
Notifications
You must be signed in to change notification settings - Fork 206
feat: add 2026 registration flow with Google Sheets storage and confirmation email #891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
DarshanCode2005
wants to merge
65
commits into
asyncapi:master
Choose a base branch
from
DarshanCode2005:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
65 commits
Select commit
Hold shift + click to select a range
49fa461
chore: add config for 2026 registration Google Sheet
DarshanCode2005 3875a32
feat: add Google Sheets append helper for 2026 registration
DarshanCode2005 d21a67e
feat: scaffold 2026 registration API route
DarshanCode2005 d2b312e
feat: add validation to 2026 registration API
DarshanCode2005 b8e4d02
feat: store 2026 registration submissions in Google Sheets
DarshanCode2005 5a4168a
feat: send confirmation email for 2026 registration
DarshanCode2005 74ad5b8
feat: add 2026 registration page scaffold
DarshanCode2005 ae32358
feat: add multi-step form structure for 2026 registration
DarshanCode2005 41caffa
feat: add form fields and client validation for registration
DarshanCode2005 9d84ce6
feat: connect registration form to API with success state
DarshanCode2005 75e8204
add googleapis package
DarshanCode2005 3ecfeb5
chore: add navigation and privacy notice
DarshanCode2005 401c61c
fix: fix the notes ci ts error
DarshanCode2005 058c7c5
Fix TypeScript error for 'notes' property in registration stepFour an…
DarshanCode2005 8199198
feat: add gradient image on the registration page
DarshanCode2005 b13e7a5
Merge branch 'master' into master
thulieblack 7173ff6
Merge branch 'master' into master
thulieblack 9701c49
Merge branch 'master' into master
AceTheCreator 9fcf0cb
feat: add location dropdown to registration form
DarshanCode2005 e041ae2
refactor(ui): replace native button with Button component
DarshanCode2005 372abea
refactor(ui): replace native button with Button component
DarshanCode2005 656d9b1
refactor(ui): replace native button with Button component
DarshanCode2005 9004a08
refactor(ui): replace hardcoded year with dynamic current year
DarshanCode2005 6fc3625
refactor(ui): remove confusing UI stuff
DarshanCode2005 71c301c
chore(types): convert JavaScript file to TypeScript
DarshanCode2005 d23a1a7
chore(env): rename environment variables and remove year suffix
DarshanCode2005 b2b421c
chore(env): remove year suffix
DarshanCode2005 d67216f
chore(env): reverting back to simpler example env file
DarshanCode2005 b402b34
fix: move registration files to 2026 dir to restore routes
DarshanCode2005 bb47fa2
fix: use correct button classes
DarshanCode2005 e1314a3
fix: correct the folder name and final fixes
DarshanCode2005 c3e81fa
fix: cypress run CI tests
DarshanCode2005 eaa78ff
chore: remove unused content from register/2026 page
DarshanCode2005 32a89fb
refactor: improve email validation regex
DarshanCode2005 91b49c1
refactor: centralize email validation into shared utility
DarshanCode2005 7ebca0d
fix(a11y): add label and replace div for accessibility in stepOne
DarshanCode2005 a0c378b
fix(a11y): add label and replace div for accessibility in stepTwo
DarshanCode2005 1496624
fix(a11y): add label and replace div for accessibility in stepThree
DarshanCode2005 f86dcf3
fix(a11y): add label and replace div for accessibility in stepFour
DarshanCode2005 9d7d51c
style: increase label spacing and ensure block-level layout across st…
DarshanCode2005 79744a3
style: increase label spacing and ensure block-level layout across st…
DarshanCode2005 f346b0d
style: increase label spacing and ensure block-level layout across st…
DarshanCode2005 eb4f0a2
Merge branch 'master' into master
asyncapi-bot 348cdb0
fix(types): add missing registration fields to CfpForm
DarshanCode2005 cf43871
fix(types): clean up stepThreetyping
DarshanCode2005 76720e3
feat(utils): validEmail utility
DarshanCode2005 3c626a5
feat(types): cleanup up stepThree Typing
DarshanCode2005 5333e72
feat(types): cleanup up stepTwo Typing
DarshanCode2005 3223f59
feat(types): cleanup up stepOne Typing
DarshanCode2005 7897d2b
feat(types): cleanup up stepFour Typing
DarshanCode2005 9eeb728
Merge branch 'master' of https://github.com/DarshanCode2005/conferenc…
DarshanCode2005 f2ff984
a11y: add visible focus ring styles to form inputs
DarshanCode2005 47ef55e
a11y: add aria-live for error announcements
DarshanCode2005 0066626
a11y: wrap checkboxes in fieldset for grouping
DarshanCode2005 afa20da
a11y: add focus management on step navigation
DarshanCode2005 640f953
a11y: add aria-invalid and error messages for validation
DarshanCode2005 d0596f6
a11y: add skip link to jump to form
DarshanCode2005 832c801
a11y: hide decorative confetti from screen readers
DarshanCode2005 11eb581
a11y: add semantic nav with aria-current for step indicators
DarshanCode2005 54858b1
a11y: add role=group with aria-labelledby to form sections
DarshanCode2005 47c38a5
a11y: add new tab indicator for external links
DarshanCode2005 916f328
a11y: add role=status to success message for screen readers
DarshanCode2005 d285128
chore(register/2026): remove the warning comment
DarshanCode2005 47d7f08
Merge branch 'master' into master
DarshanCode2005 3968777
Merge branch 'master' into master
AceTheCreator File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| /* eslint-disable react/no-unescaped-entities */ | ||
| import React, { FormEvent, useState, JSX } from 'react'; | ||
| import Button from '../../Buttons/button'; | ||
| import { CfpStepProps } from '../../../types/types'; | ||
|
|
||
| type ApiResponse = { success?: boolean; error?: string }; | ||
|
|
||
| function StepFourRegistration({ setStep, setForm, data }: CfpStepProps) { | ||
| const [submitting, setSubmitting] = useState<boolean>(false); | ||
| const [disabled, setDisabled] = useState<boolean>(false); | ||
| const [error, setError] = useState<string>(''); | ||
|
|
||
| const onSubmit = (e: FormEvent<HTMLFormElement>) => { | ||
| e.preventDefault(); | ||
| setSubmitting(true); | ||
| setError(''); | ||
|
|
||
| const payload = { | ||
| fullName: data.fullName || '', | ||
| email: data.email || '', | ||
| company: data.company || '', | ||
| role: data.role || '', | ||
| preferredCity: data.preferredCity || '', | ||
| attendanceType: data.attendanceType || '', | ||
| timezone: data.timezone || '', | ||
| dietaryAccessibility: data.dietaryAccessibility || '', | ||
| updatesOptIn: Boolean(data.updatesOptIn), | ||
| sponsorDataSharing: Boolean(data.sponsorDataSharing), | ||
| notes: data.notes || '', | ||
| }; | ||
|
|
||
| fetch('/api/registration/2026', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify(payload), | ||
| }) | ||
| .then(async (res) => { | ||
| const body: ApiResponse = await res.json().catch(() => ({})); | ||
| if (!res.ok) { | ||
| const message = body.error || `Submission failed (${res.status})`; | ||
| throw new Error(message); | ||
| } | ||
| return body; | ||
| }) | ||
| .then(() => { | ||
| setSubmitting(false); | ||
| setDisabled(true); | ||
| setStep(e, 0); | ||
| }) | ||
| .catch((err: Error) => { | ||
| setSubmitting(false); | ||
| setError(err.message || 'Submission failed'); | ||
| }); | ||
| }; | ||
|
|
||
| return ( | ||
| <form className="mt-3" onSubmit={(e) => onSubmit(e)} aria-busy={submitting}> | ||
| <h1 id="step-four-heading" className="text-white font-bold text-4xl lg:text-3xl">Review & Submit</h1> | ||
| <p className="mt-3 text-dark-600">Review your details and submit your registration.</p> | ||
| <div className="mt-3 border w-full border-solid border-y-dark-600 divide-y" /> | ||
| <div className="mt-10" role="group" aria-labelledby="step-four-heading"> | ||
| <label htmlFor="notes" className="text-dark-600 text-lg">Notes</label> | ||
| <textarea | ||
| id="notes" | ||
| className="mt-3 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-[#E50E99] focus:ring-offset-2 focus:ring-offset-gray-900 border border-[#E50E99]" | ||
| onChange={(e) => setForm((prev) => ({ ...prev, notes: e.target.value }))} | ||
| data-test="reg-step-four-notes" | ||
| /> | ||
|
|
||
| <div className="mt-6 text-dark-600 text-md"> | ||
| By clicking submit, you confirm the information is correct. | ||
| </div> | ||
| <div className="mt-3 w-full flex items-center justify-between lg:flex-col-reverse lg:items-start"> | ||
| <div className="mt-3 w-full flex items-center justify-end lg:flex-col-reverse lg:items-start gap-4"> | ||
| {/* Back button */} | ||
| <Button | ||
| type="button" | ||
| disabled={disabled} | ||
| onClick={() => !disabled && setStep(null, 3)} | ||
| className="text-gray-500 hover:text-gray-700 transition-colors disabled:opacity-50 w-36 lg:w-full" | ||
| > | ||
| Back | ||
| </Button> | ||
|
|
||
| {/* Submit button */} | ||
| <Button | ||
| type="submit" | ||
| disabled={submitting || disabled} | ||
| className="text-gray-500 hover:text-gray-700 transition-colors disabled:opacity-50 w-36 lg:w-full" | ||
| test="reg-step-four-next" | ||
| > | ||
| {submitting ? 'Submitting...' : 'Submit'} | ||
| </Button> | ||
| </div> | ||
|
|
||
| {error && ( | ||
| <div | ||
| className="text-red-400 mt-2" | ||
| role="alert" | ||
| aria-live="assertive" | ||
| > | ||
| {error} | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| </form> | ||
| ); | ||
| } | ||
|
|
||
| export default StepFourRegistration; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /* eslint-disable react/no-unescaped-entities */ | ||
| import React, { JSX, useState } from 'react'; | ||
| import Button from '../../Buttons/button'; | ||
| import { CfpStepProps } from '../../../types/types'; | ||
|
|
||
| import { isValidEmail } from '../../../utils/validation'; | ||
|
|
||
| function StepOneRegistration({ setStep, setForm, data }: CfpStepProps): JSX.Element { | ||
| const [touched, setTouched] = useState(false); | ||
|
|
||
| const fullName = data.fullName ?? ''; | ||
| const email = data.email ?? ''; | ||
|
|
||
| const isValid = fullName.trim().length > 0 && isValidEmail(email.trim()); | ||
|
|
||
| return ( | ||
| <form | ||
| className="mt-3" | ||
| onSubmit={(e) => setStep(e, 2)} | ||
| data-test="reg-step-one" | ||
| onBlur={() => setTouched(true)} | ||
| > | ||
| <h1 id="step-one-heading" className="text-white font-bold text-4xl lg:text-3xl">Personal Info</h1> | ||
| <p className="mt-3 text-dark-600">Tell us who you are so we can prepare your ticket.</p> | ||
| <div className="mt-3 border w-full border-solid border-dark-400 divide-y" /> | ||
| <div className="mt-10" role="group" aria-labelledby="step-one-heading"> | ||
| <label htmlFor="fullName" className="text-dark-600 text-lg"> | ||
| Full name <span aria-hidden="true">*</span> | ||
| </label> | ||
| <input | ||
| id="fullName" | ||
| required | ||
| aria-required="true" | ||
| aria-invalid={touched && fullName.trim().length === 0} | ||
| aria-describedby={touched && fullName.trim().length === 0 ? "fullName-error" : undefined} | ||
| value={fullName} | ||
| className="mt-3 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-[#E50E99] focus:ring-offset-2 focus:ring-offset-gray-900 border border-[#E50E99]" | ||
| onChange={(e) => setForm((prev) => ({ ...prev, fullName: e.target.value }))} | ||
| data-test="reg-step-one-name" | ||
| /> | ||
| {touched && fullName.trim().length === 0 && ( | ||
| <p id="fullName-error" className="text-red-400 text-sm mt-1" role="alert"> | ||
| Full name is required | ||
| </p> | ||
| )} | ||
|
|
||
| <label htmlFor="email" className="text-dark-600 text-lg mt-6 block"> | ||
| Email address <span aria-hidden="true">*</span> | ||
| </label> | ||
| <input | ||
| id="email" | ||
| required | ||
| type="email" | ||
| aria-required="true" | ||
| aria-invalid={touched && !isValidEmail(email.trim())} | ||
| aria-describedby={touched && !isValidEmail(email.trim()) ? "email-error" : undefined} | ||
| value={email} | ||
| className="mt-3 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-[#E50E99] focus:ring-offset-2 focus:ring-offset-gray-900 border border-[#E50E99]" | ||
| onChange={(e) => setForm((prev) => ({ ...prev, email: e.target.value }))} | ||
| data-test="reg-step-one-email" | ||
| /> | ||
| {touched && !isValidEmail(email.trim()) && ( | ||
| <p id="email-error" className="text-red-400 text-sm mt-1" role="alert"> | ||
| Please enter a valid email address | ||
| </p> | ||
| )} | ||
|
|
||
| <div className="mt-6"> | ||
| <Button | ||
| type="submit" | ||
| className="bg-tetiary-pink p-3 rounded-md text-white mt-6 float-right w-36 lg:w-full lg:mt-8" | ||
| disabled={!isValid} | ||
| test="reg-step-one-next" | ||
| > | ||
| Next | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </form> | ||
| ); | ||
| } | ||
|
|
||
| export default StepOneRegistration; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| /* eslint-disable react/no-unescaped-entities */ | ||
| import React, { JSX, useState } from 'react'; | ||
| import Button from '../../Buttons/button'; | ||
| import { CfpStepProps } from '../../../types/types'; | ||
|
|
||
| function StepThreeRegistration({ setStep, setForm, data }: CfpStepProps): JSX.Element { | ||
|
|
||
| const dietary = data.dietaryAccessibility || ''; | ||
| const updatesOptIn = !!data.updatesOptIn; | ||
| const sponsorDataSharing = !!data.sponsorDataSharing; | ||
| const notes = data.notes || ''; | ||
|
|
||
| const [localUpdates, setLocalUpdates] = useState(updatesOptIn); | ||
| const [localSponsor, setLocalSponsor] = useState(sponsorDataSharing); | ||
|
|
||
| return ( | ||
| <form className="mt-3" onSubmit={(e) => setStep(e, 4)} data-test="reg-step-three"> | ||
| <h1 id="step-three-heading" className="text-white font-bold text-4xl lg:text-3xl">Consents & Notes</h1> | ||
| <p className="mt-3 text-dark-600">Tell us any dietary or accessibility needs and consent preferences.</p> | ||
| <div className="mt-3 border w-full border-solid border-dark-400 divide-y" /> | ||
| <div className="mt-10" role="group" aria-labelledby="step-three-heading"> | ||
| <label htmlFor="dietaryAccessibility" className="text-dark-600 text-lg">Dietary / Accessibility needs</label> | ||
| <textarea | ||
| id="dietaryAccessibility" | ||
| className="mt-3 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-[#E50E99] focus:ring-offset-2 focus:ring-offset-gray-900 border border-[#E50E99]" | ||
| value={dietary} | ||
| onChange={(e) => setForm((prev) => ({ ...prev, dietaryAccessibility: e.target.value }))} | ||
| data-test="reg-step-three-dietary" | ||
| /> | ||
|
|
||
| <fieldset className="mt-5 border-0 p-0"> | ||
| <legend className="text-dark-600 text-lg mb-3">Communication Preferences</legend> | ||
|
|
||
| <div className="text-dark-600"> | ||
| <label className="inline-flex items-center"> | ||
| <input | ||
| type="checkbox" | ||
| id="updatesOptIn" | ||
| checked={localUpdates} | ||
| onChange={(e) => { | ||
| setLocalUpdates(e.target.checked); | ||
| setForm((prev) => ({ ...prev, updatesOptIn: e.target.checked })); | ||
| }} | ||
| data-test="reg-step-three-updates" | ||
| className="mr-2 focus:ring-2 focus:ring-[#E50E99]" | ||
| /> | ||
| Receive occasional updates about the event | ||
| </label> | ||
| </div> | ||
|
|
||
| <div className="mt-3 text-dark-600"> | ||
| <label className="inline-flex items-center"> | ||
| <input | ||
| type="checkbox" | ||
| id="sponsorDataSharing" | ||
| checked={localSponsor} | ||
| onChange={(e) => { | ||
| setLocalSponsor(e.target.checked); | ||
| setForm((prev) => ({ ...prev, sponsorDataSharing: e.target.checked })); | ||
| }} | ||
| data-test="reg-step-three-sponsor" | ||
| className="mr-2 focus:ring-2 focus:ring-[#E50E99]" | ||
| /> | ||
| Share my contact with sponsors (optional) | ||
| </label> | ||
| </div> | ||
| </fieldset> | ||
|
|
||
| <div className="mt-3 text-sm text-dark-600"> | ||
| We process your data according to our <a href="/privacy" className="underline">Privacy Policy</a>. By submitting you agree to the <a href="https://github.com/asyncapi/community/blob/master/CODE_OF_CONDUCT.md" target="_blank" rel="noreferrer" className="underline" aria-label="Code of Conduct (opens in new tab)">Code of Conduct<span className="sr-only"> (opens in new tab)</span></a> and Privacy Policy. | ||
| </div> | ||
|
|
||
| <label htmlFor="notes" className="text-dark-600 text-lg mt-6 block">Notes (optional)</label> | ||
| <textarea | ||
| id="notes" | ||
| className="mt-3 w-full p-4 rounded-md focus:outline-none focus:ring-2 focus:ring-[#E50E99] focus:ring-offset-2 focus:ring-offset-gray-900 border border-[#E50E99]" | ||
| value={notes} | ||
| onChange={(e) => setForm((prev) => ({ ...prev, notes: e.target.value }))} | ||
| data-test="reg-step-three-notes" | ||
| /> | ||
|
|
||
| <div className="mt-3 flex items-center justify-end gap-6 lg:flex-col-reverse lg:w-full lg:items-stretch"> | ||
| {/* Back */} | ||
| <Button | ||
| type="button" | ||
| onClick={() => setStep(null, 2)} | ||
| className="text-gray-500 hover:text-gray-700 transition-colors lg:text-center w-36 lg:w-full" | ||
| > | ||
| Back | ||
| </Button> | ||
|
|
||
| {/* Next */} | ||
| <Button | ||
| type="submit" | ||
| className="text-gray-500 hover:text-gray-700 transition-colors lg:text-center w-36 lg:w-full" | ||
| test="reg-step-three-next" | ||
| > | ||
| Next | ||
| </Button> | ||
| </div> | ||
|
|
||
| </div> | ||
| </form> | ||
| ); | ||
| } | ||
|
|
||
| export default StepThreeRegistration; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @TenzDelek Sir, already it uses button component, if there is anything else required in this section then do let me know.