Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ next-env.d.ts

# supabase local development
supabase/.branches
supabase/.temp
supabase/.temp

# local test
local-test/
90 changes: 90 additions & 0 deletions app/membership/signup/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { SignUpFormData, StepInfo } from '@/types/signup';

/**
* Step configuration for the 3-step signup process
*/
export const SIGNUP_STEPS: StepInfo[] = [
{
id: 1,
title: 'Account Creation',
description: 'Create your credentials',
},
{
id: 2,
title: 'Form Completion',
description: 'Your information',
},
{
id: 3,
title: 'Submission',
description: 'Review and submit',
},
];

/**
* for selection dropdown
*/
export const CANADIAN_PROVINCES: string[] = [
'Alberta',
'British Columbia',
'Manitoba',
'New Brunswick',
'Newfoundland and Labrador',
'Northwest Territories',
'Nova Scotia',
'Nunavut',
'Ontario',
'Prince Edward Island',
'Quebec',
'Saskatchewan',
'Yukon',
];

/**
* Membership interest options
*/
export const MEMBERSHIP_INTERESTS: string[] = [
'Health',
'Education',
'Arts+Culture',
'Housing',
'Environment',
'Other',
];

/**
* Mock data
*/
export const SIGNUP_MOCK: SignUpFormData = {
email: '[email protected]',
password: 'Test1234',
confirmPassword: 'Test1234',
fullName: 'John Doe',
phoneNumber: '6041234567',
phoneType: 'cell',
mailingAddress: '123 Main Street',
city: 'Vancouver',
province: 'British Columbia',
postalCode: 'V5K 1A1',
interests: ['Health', 'Housing'],
whyJoin:
'I want to help reduce poverty in Richmond and support community initiatives.',
};

/**
* Empty form data - initial state
*/
export const SIGNUP_INITIAL: SignUpFormData = {
email: '',
password: '',
confirmPassword: '',
fullName: '',
phoneNumber: '',
phoneType: 'cell',
mailingAddress: '',
city: '',
province: '',
postalCode: '',
interests: [],
whyJoin: '',
};
Empty file added app/membership/signup/page.tsx
Empty file.
90 changes: 90 additions & 0 deletions components/signup/FormInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';

import React from 'react';

type FormInputProps = {
label: string;
type?: 'text' | 'email' | 'password' | 'tel';
value: string;
onChange: (value: string) => void;
error?: string;
placeholder?: string;
required?: boolean;
showValidation?: boolean;
};

export function FormInput({
label,
type = 'text',
value,
onChange,
error,
placeholder,
required = false,
showValidation = false,
}: FormInputProps) {
// Determine border color based on validation state
const getBorderClass = () => {
// Only show validation colors if showValidation is true
if (!showValidation) {
return 'border-gray-300 focus:border-blue-500 focus:ring-blue-500';
}

// Show red if there's an error
if (error) {
return 'border-red-500 focus:border-red-500 focus:ring-red-500';
}

// Show green if value exists and no error
if (value && !error) {
return 'border-green-500 focus:border-green-500 focus:ring-green-500';
}

// Default
return 'border-gray-300 focus:border-blue-500 focus:ring-blue-500';
};

return (
<div className="w-full">
{/* Label */}
<label
htmlFor={label}
className="block font-medium text-[14px] md:text-[16px] text-gray-700 mb-1"
>
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>

{/* Input */}
<input
title="input"
id={label}
type={type}
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
className={`
w-full px-3 py-2
border-2 rounded-md
text-[14px] md:text-[16px]
focus:outline-none focus:ring-2
transition-colors
${getBorderClass()}
`}
aria-invalid={error ? 'true' : 'false'}
aria-describedby={error ? `${label}-error` : undefined}
/>

{/* Error message - only show if validation attempted */}
{showValidation && error && (
<p
id={`${label}-error`}
className="text-red-500 text-[14px] mt-1"
role="alert"
>
{error}
</p>
)}
</div>
);
}
100 changes: 100 additions & 0 deletions components/signup/FormSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use client';

import React from 'react';

type FormSelectProps = {
label: string;
value: string;
onChange: (value: string) => void;
options: string[]; // Simple string array
error?: string;
placeholder?: string;
required?: boolean;
showValidation?: boolean;
};

export function FormSelect({
label,
value,
onChange,
options,
error,
placeholder = 'Select an option',
required = false,
showValidation = false,
}: FormSelectProps) {
// Determine border color based on validation state
const getBorderClass = () => {
// Only show validation colors if showValidation is true
if (!showValidation) {
return 'border-gray-300 focus:border-blue-500 focus:ring-blue-500';
}

// Show red if there's an error
if (error) {
return 'border-red-500 focus:border-red-500 focus:ring-red-500';
}

// Show green if value exists and no error
if (value && !error) {
return 'border-green-500 focus:border-green-500 focus:ring-green-500';
}

// Default
return 'border-gray-300 focus:border-blue-500 focus:ring-blue-500';
};

return (
<div className="w-full">
{/* Label */}
<label
htmlFor={label}
className="block font-medium text-[14px] md:text-[16px] text-gray-700 mb-1"
>
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>

{/* Select Dropdown */}
<select
id={label}
value={value}
onChange={(e) => onChange(e.target.value)}
className={`
w-full px-3 py-2
border-2 rounded-md
text-[14px] md:text-[16px]
focus:outline-none focus:ring-2
transition-colors
bg-white
${getBorderClass()}
`}
aria-invalid={error ? 'true' : 'false'}
aria-describedby={error ? `${label}-error` : undefined}
>
{/* Placeholder option */}
<option value="" disabled>
{placeholder}
</option>

{/* Map through options */}
{options.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>

{/* Error message - only show if validation attempted */}
{showValidation && error && (
<p
id={`${label}-error`}
className="text-red-500 text-[14px] mt-1"
role="alert"
>
{error}
</p>
)}
</div>
);
}
90 changes: 90 additions & 0 deletions components/signup/FormTextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';

import React from 'react';

type FormTextAreaProps = {
label: string;
value: string;
onChange: (value: string) => void;
error?: string;
placeholder?: string;
rows?: number;
required?: boolean;
showValidation?: boolean;
};

export function FormTextArea({
label,
value,
onChange,
error,
placeholder,
rows = 4,
required = false,
showValidation = false,
}: FormTextAreaProps) {
// Determine border color based on validation state
const getBorderClass = () => {
// Only show validation colors if showValidation is true
if (!showValidation) {
return 'border-gray-300 focus:border-blue-500 focus:ring-blue-500';
}

// Show red if there's an error
if (error) {
return 'border-red-500 focus:border-red-500 focus:ring-red-500';
}

// Show green if value exists and no error
if (value && !error) {
return 'border-green-500 focus:border-green-500 focus:ring-green-500';
}

// Default
return 'border-gray-300 focus:border-blue-500 focus:ring-blue-500';
};

return (
<div className="w-full">
{/* Label */}
<label
htmlFor={label}
className="block font-medium text-[14px] md:text-[16px] text-gray-700 mb-1"
>
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>

{/* TextArea */}
<textarea
id={label}
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
rows={rows}
className={`
w-full px-3 py-2
border-2 rounded-md
text-[14px] md:text-[16px]
focus:outline-none focus:ring-2
transition-colors
resize-vertical
${getBorderClass()}
`}
aria-invalid={error ? 'true' : 'false'}
aria-describedby={error ? `${label}-error` : undefined}
/>

{/* Error message - only show if validation attempted */}
{showValidation && error && (
<p
id={`${label}-error`}
className="text-red-500 text-[14px] mt-1"
role="alert"
>
{error}
</p>
)}
</div>
);
}
Loading
Loading