-
+ {showAngelCow && (
+
+ )}
{title}
diff --git a/app/(pages)/(hackers)/_components/AuthForms/DetailForm.module.scss b/app/(pages)/(hackers)/_components/AuthForms/DetailForm.module.scss
deleted file mode 100644
index 4b589984..00000000
--- a/app/(pages)/(hackers)/_components/AuthForms/DetailForm.module.scss
+++ /dev/null
@@ -1,249 +0,0 @@
-@import 'app/(pages)/_globals/mixins';
-
-.container {
- display: flex;
- flex-direction: column;
- width: 100%;
-
- @include tablet-l {
- margin-top: 5%;
- }
-}
-
-.header {
- display: flex;
- flex-direction: row;
- gap: 26px;
- margin-left: 5%;
- margin-right: 5%;
- margin-top: max(114px, 6%);
-
- .header_text {
- display: flex;
- flex-direction: column;
- gap: 16px;
-
- h1 {
- font-size: 1.75rem;
- font-style: normal;
- font-weight: 600;
- line-height: normal;
- letter-spacing: 0.56px;
-
- @include tablet-s {
- font-size: 1.25rem;
- }
-
- @include mobile-l {
- font-size: 1rem;
- }
- }
-
- p {
- font-size: 1.125rem;
- font-style: normal;
- font-weight: 400;
- line-height: 145%;
- letter-spacing: 0.36px;
-
- @include tablet-s {
- font-size: 0.875rem;
- }
-
- @include mobile-l {
- font-size: 0.75rem;
- }
- }
- }
-}
-
-.form {
- display: flex;
- flex-direction: column;
- align-self: center;
- align-items: center;
- width: 70%;
- margin-top: 5%;
-
- @include desktop-m {
- width: 80%;
- }
-
- @include tablet-l {
- padding-bottom: 32px;
- }
-}
-
-.characterGrid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 40px;
- height: 100%;
- width: 100%;
-
- @include desktop-m {
- gap: 20px;
- }
-
- @include tablet-l {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- justify-content: space-between;
- gap: 0;
- width: 50%;
- }
-
- @include tablet-s {
- width: 75%;
- }
-
- @include mobile-l {
- width: 85%;
- }
-}
-
-.characterOption {
- display: flex;
- align-self: flex-end;
- height: 100%;
-
-
- &.selected {
- border-radius: 20px;
- border: 1px dashed #005271;
- }
-}
-
-.radioInput {
- position: absolute;
- opacity: 0;
- width: 0;
- height: 0;
-}
-
-.character {
- display: flex;
- flex-direction: column;
- align-items: center;
- height: 100%;
- width: 100%;
- cursor: pointer;
-}
-
-.characterImage {
- width: 100%;
- height: 100%;
- display: flex;
- margin-bottom: 16px;
- transition: transform 0.2s;
-
- &.selected {
- filter: drop-shadow(32px 32px 2rem rgba(255, 243, 216, 0.60));
- }
-}
-
-.characterImage:hover {
- filter: drop-shadow(32px 32px 2rem rgba(255, 243, 216, 0.60));
-}
-
-.characterLabel {
- text-align: center;
- font-size: 1.5rem;
- font-style: normal;
- font-weight: 700;
- line-height: normal;
- padding-bottom: 32px;
-
- @include desktop-l {
- font-size: 1rem;
- }
-
- @include desktop-m {
- font-size: 0.875rem;
- padding-bottom: 16px;
- }
-
- &.selected {
- color: #005271;
- }
-}
-
-.bottom {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- width: 100%;
- margin-top: 28px;
-
- @include tablet-l {
- flex-direction: column;
- align-items: end;
- gap: 20px;
- }
-}
-
-.beginnerOption {
- display: flex;
- align-items: center;
- gap: 16px;
- margin-left: 40px;
-}
-
-.checkboxInput {
- appearance: none;
- width: 23px;
- height: 23px;
- border: 1px solid #000;
- border-radius: 4px;
- position: relative;
- cursor: pointer;
-
- &:checked {
- background-color: #4a90e2;
- border-color: #4a90e2;
-
- &:after {
- content: "✓";
- color: white;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
- }
-}
-
-.beginnerLabel {
- font-size: 1.125rem;
-}
-
-.submit_button {
- padding: 12px 24px;
- font-size: 1.125rem;
- font-weight: 600;
- line-height: normal;
- font-style: normal;
- line-height: 100%;
- letter-spacing: 0.36px;
- color: var(--text-light);
- background: var(--background-secondary);
- border: none;
- cursor: not-allowed;
- border-radius: 20px;
- opacity: 0.3;
-
- @include tablet-l {
- padding: 8px 16px;
- font-size: 1rem;
- letter-spacing: 0.24px;
- }
-}
-
-.valid {
- opacity: 1;
- cursor: pointer;
-}
-
-.error_msg {
- color: var(--text-error);
-}
diff --git a/app/(pages)/(hackers)/_components/AuthForms/DetailForm.tsx b/app/(pages)/(hackers)/_components/AuthForms/DetailForm.tsx
deleted file mode 100644
index d00d7d0c..00000000
--- a/app/(pages)/(hackers)/_components/AuthForms/DetailForm.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-'use client';
-
-import { useEffect, useState, FormEvent } from 'react';
-import { useRouter } from 'next/navigation';
-import Image from 'next/image';
-
-import { updateUser } from '@actions/users/updateUser';
-import Loader from '@components/Loader/Loader';
-import DeveloperCow from 'public/hackers/login/developer_cow.svg';
-import DesignerBunny from 'public/hackers/login/designer_bunny.svg';
-import PmFroggy from 'public/hackers/login/pm_froggy.svg';
-import OtherDucky from 'public/hackers/login/other_ducky.svg';
-import styles from './DetailForm.module.scss';
-
-const characters = [
- {
- label: 'DEVELOPER',
- role: 'developer',
- image: DeveloperCow,
- alt: 'Developer Cow',
- },
- {
- label: 'DESIGNER',
- role: 'designer',
- image: DesignerBunny,
- alt: 'Designer Bunny',
- },
- {
- label: 'PROJECT MANAGER',
- role: 'pm',
- image: PmFroggy,
- alt: 'PM Froggy',
- },
- {
- label: 'OTHER',
- role: 'other',
- image: OtherDucky,
- alt: 'Other Ducky',
- },
-];
-
-export default function DetailForm({ id }: any) {
- const router = useRouter();
-
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState('');
- const [valid, setValid] = useState(false);
- const [selectedPosition, setSelectedPosition] = useState('');
-
- const handleRegister = async (e: FormEvent
) => {
- e.preventDefault();
-
- setLoading(true);
- setError('');
-
- const formData = new FormData(e.currentTarget);
-
- const position = formData.get('position') as string;
- const is_beginner = formData.get('beginner') !== null;
-
- const userRes = await updateUser(id, {
- $set: {
- position,
- is_beginner,
- },
- });
-
- if (userRes.ok) {
- router.push('/');
- } else {
- setError(userRes.error ?? 'Error updating details');
- }
-
- setLoading(false);
- };
-
- useEffect(() => {
- setValid(selectedPosition !== '');
- }, [selectedPosition]);
-
- return (
-
- );
-}
diff --git a/app/(pages)/(hackers)/_components/AuthForms/register/ChooseLevel.tsx b/app/(pages)/(hackers)/_components/AuthForms/register/ChooseLevel.tsx
new file mode 100644
index 00000000..0d7f9142
--- /dev/null
+++ b/app/(pages)/(hackers)/_components/AuthForms/register/ChooseLevel.tsx
@@ -0,0 +1,164 @@
+'use client';
+
+import Image from 'next/image';
+import AuthFormBackground from '../../AuthFormBackground/AuthFormBackground';
+
+type ChooseLevelProps = {
+ value?: string;
+ onSelect: (value: string) => void;
+ onBack: () => void;
+ onNext: () => void;
+ loading?: boolean;
+ error?: string;
+};
+
+const levels = [
+ {
+ id: 'beginner',
+ title: 'Beginner',
+ tag: 'NEW TO THE SCENE',
+ description:
+ "You're here to build your foundation, explore new tools, and ship your first few projects.",
+ image: '/hackers/register/beginner-frog.svg',
+ },
+ {
+ id: 'experienced',
+ title: 'Experienced',
+ tag: 'A SEASONED HACKER',
+ description:
+ "You're comfortable with the 36-hour grind and ready to push the boundaries of your projects.",
+ image: '/hackers/register/experienced-frog.svg',
+ },
+];
+
+export default function ChooseLevel({
+ value,
+ onSelect,
+ onBack,
+ onNext,
+ loading = false,
+ error = '',
+}: ChooseLevelProps) {
+ return (
+
+
+ {/* cards (ALWAYS SIDE BY SIDE) */}
+
+ {levels.map((level) => {
+ const selected = value === level.id;
+ return (
+
+ );
+ })}
+
+
+ {/* footer */}
+
+ {/* indicators */}
+
+ {/* nav buttons */}
+
+ {/* error message - takes up vertical space*/}
+
+ {error}
+
+
+ {/* NEXT (top on mobile) */}
+
+
+ {/* BACK */}
+
+
+
+
+
+
+ );
+}
diff --git a/app/(pages)/(hackers)/_components/AuthForms/register/ChooseRole.tsx b/app/(pages)/(hackers)/_components/AuthForms/register/ChooseRole.tsx
new file mode 100644
index 00000000..64d52205
--- /dev/null
+++ b/app/(pages)/(hackers)/_components/AuthForms/register/ChooseRole.tsx
@@ -0,0 +1,144 @@
+'use client';
+
+import Image from 'next/image';
+import AuthFormBackground from '../../AuthFormBackground/AuthFormBackground';
+
+type ChooseRoleProps = {
+ value?: string;
+ onSelect: (value: string) => void;
+ onBack: () => void;
+ onNext: () => void;
+};
+
+const roles = [
+ {
+ id: 'developer',
+ label: 'Developer',
+ image: '/hackers/register/dev-cow.svg',
+ },
+ {
+ id: 'designer',
+ label: 'Designer',
+ image: '/hackers/register/designer-bunny.svg',
+ },
+ {
+ id: 'pm',
+ label: 'Product',
+ image: '/hackers/register/product-frog.svg',
+ },
+ {
+ id: 'other',
+ label: 'Other',
+ image: '/hackers/register/explorer-duck.svg',
+ },
+];
+
+export default function ChooseRole({
+ value,
+ onSelect,
+ onBack,
+ onNext,
+}: ChooseRoleProps) {
+ return (
+
+ {/* cards */}
+
+ {roles.map((role) => {
+ const selected = value === role.id;
+ return (
+
+ );
+ })}
+
+
+ {/* Bottom navigation */}
+
+ {/* indicators */}
+
+
+ {/* nav buttons */}
+
+ {/* NEXT (top on mobile) */}
+
+
+ {/* BACK */}
+
+
+
+
+ );
+}
diff --git a/app/(pages)/(hackers)/_components/AuthForms/register/DetailForm.tsx b/app/(pages)/(hackers)/_components/AuthForms/register/DetailForm.tsx
new file mode 100644
index 00000000..af9af33a
--- /dev/null
+++ b/app/(pages)/(hackers)/_components/AuthForms/register/DetailForm.tsx
@@ -0,0 +1,72 @@
+'use client';
+
+import { useState } from 'react';
+import { useRouter } from 'next/navigation';
+
+import { updateUser } from '@actions/users/updateUser';
+import ChooseRole from './ChooseRole';
+import ChooseLevel from './ChooseLevel';
+
+type DetailFormProps = {
+ id: string;
+};
+
+export default function DetailForm({ id }: DetailFormProps) {
+ const router = useRouter();
+
+ const [stage, setStage] = useState<0 | 1>(0);
+ const [role, setRole] = useState();
+ const [level, setLevel] = useState();
+ const [loading, setLoading] = useState(false);
+
+ const [error, setError] = useState('');
+
+ const handleRegister = async () => {
+ if (!role || !level) return;
+
+ setLoading(true);
+ setError('');
+
+ const is_beginner = level === 'beginner';
+
+ const userRes = await updateUser(id, {
+ $set: {
+ position: role,
+ is_beginner,
+ },
+ });
+
+ if (userRes.ok) {
+ router.push('/');
+ } else {
+ setError(userRes.error ?? 'Error updating details');
+ }
+
+ setLoading(false);
+ };
+
+ return (
+
+ {stage === 0 ? (
+ router.push('/register')}
+ onNext={() => {
+ if (!role) return;
+ setStage(1);
+ }}
+ />
+ ) : (
+ setStage(0)}
+ onNext={handleRegister}
+ loading={loading}
+ error={error}
+ />
+ )}
+
+ );
+}
diff --git a/app/(pages)/(hackers)/_components/AuthForms/RegisterForm.tsx b/app/(pages)/(hackers)/_components/AuthForms/register/RegisterForm.tsx
similarity index 100%
rename from app/(pages)/(hackers)/_components/AuthForms/RegisterForm.tsx
rename to app/(pages)/(hackers)/_components/AuthForms/register/RegisterForm.tsx
diff --git a/app/(pages)/(hackers)/register/details/page.tsx b/app/(pages)/(hackers)/register/details/page.tsx
index e35aad4c..4f7a45d8 100644
--- a/app/(pages)/(hackers)/register/details/page.tsx
+++ b/app/(pages)/(hackers)/register/details/page.tsx
@@ -1,7 +1,6 @@
import { redirect } from 'next/navigation';
-import DetailForm from '@pages/(hackers)/_components/AuthForms/DetailForm';
-import AuthFormBackground from '../../_components/AuthFormBackground/AuthFormBackground';
+import DetailForm from '@pages/(hackers)/_components/AuthForms/register/DetailForm';
import getActiveUser from 'app/(pages)/_utils/getActiveUser';
export default async function DetailPage() {
@@ -9,13 +8,5 @@ export default async function DetailPage() {
if (user.role === 'judge') redirect('/judges/register');
- return (
-
-
-
- );
+ return ;
}
diff --git a/app/(pages)/(hackers)/register/page.tsx b/app/(pages)/(hackers)/register/page.tsx
index b421fe5c..bcd8d9b8 100644
--- a/app/(pages)/(hackers)/register/page.tsx
+++ b/app/(pages)/(hackers)/register/page.tsx
@@ -2,9 +2,9 @@ import { redirect } from 'next/navigation';
import { auth } from '@/auth';
import { getInviteData } from '@actions/invite/getInviteData';
-import RegisterForm from '../_components/AuthForms/RegisterForm';
-import AuthFormBackground from '../_components/AuthFormBackground/AuthFormBackground';
import InviteOnlyRoute from '@components/InviteOnlyRoute/InviteOnlyRoute';
+import AuthFormBackground from '@pages/(hackers)/_components/AuthFormBackground/AuthFormBackground';
+import RegisterForm from '../_components/AuthForms/register/RegisterForm';
export default async function RegisterPage() {
const session = await auth();
@@ -19,9 +19,8 @@ export default async function RegisterPage() {
return (
diff --git a/public/hackers/login/designer_bunny.svg b/public/hackers/login/designer_bunny.svg
deleted file mode 100644
index eb23fe1e..00000000
--- a/public/hackers/login/designer_bunny.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-
diff --git a/public/hackers/login/developer_cow.svg b/public/hackers/login/developer_cow.svg
deleted file mode 100644
index a3ca0f45..00000000
--- a/public/hackers/login/developer_cow.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
diff --git a/public/hackers/login/other_ducky.svg b/public/hackers/login/other_ducky.svg
deleted file mode 100644
index ab11d59e..00000000
--- a/public/hackers/login/other_ducky.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-
diff --git a/public/hackers/login/pm_froggy.svg b/public/hackers/login/pm_froggy.svg
deleted file mode 100644
index 8b9790e7..00000000
--- a/public/hackers/login/pm_froggy.svg
+++ /dev/null
@@ -1,27 +0,0 @@
-
diff --git a/public/hackers/register/beginner-frog.svg b/public/hackers/register/beginner-frog.svg
new file mode 100644
index 00000000..65d3130c
--- /dev/null
+++ b/public/hackers/register/beginner-frog.svg
@@ -0,0 +1,19 @@
+
diff --git a/public/hackers/register/designer-bunny.svg b/public/hackers/register/designer-bunny.svg
new file mode 100644
index 00000000..228c44a5
--- /dev/null
+++ b/public/hackers/register/designer-bunny.svg
@@ -0,0 +1,42 @@
+
diff --git a/public/hackers/register/dev-cow.svg b/public/hackers/register/dev-cow.svg
new file mode 100644
index 00000000..221e7061
--- /dev/null
+++ b/public/hackers/register/dev-cow.svg
@@ -0,0 +1,41 @@
+
diff --git a/public/hackers/register/experienced-frog.svg b/public/hackers/register/experienced-frog.svg
new file mode 100644
index 00000000..6d58185c
--- /dev/null
+++ b/public/hackers/register/experienced-frog.svg
@@ -0,0 +1,22 @@
+
diff --git a/public/hackers/register/explorer-duck.svg b/public/hackers/register/explorer-duck.svg
new file mode 100644
index 00000000..a01f6056
--- /dev/null
+++ b/public/hackers/register/explorer-duck.svg
@@ -0,0 +1,45 @@
+
diff --git a/public/hackers/register/product-frog.svg b/public/hackers/register/product-frog.svg
new file mode 100644
index 00000000..637a53f8
--- /dev/null
+++ b/public/hackers/register/product-frog.svg
@@ -0,0 +1,29 @@
+