diff --git a/api/supabase/queries/profiles.ts b/api/supabase/queries/profiles.ts
index 3b54de8..f4ce965 100644
--- a/api/supabase/queries/profiles.ts
+++ b/api/supabase/queries/profiles.ts
@@ -1,3 +1,4 @@
+import { UUID } from 'crypto';
import { Profile } from '@/types/schema';
import supabase from '../createClient';
@@ -12,3 +13,16 @@ export async function upsertProfile(profile: Profile) {
return data;
}
+
+export async function fetchProfileById(userId: UUID) {
+ const { data, error } = await supabase
+ .from('profiles')
+ .select('*')
+ .eq('user_id', userId)
+ .single();
+
+ if (error)
+ throw new Error(`Error fetching profile id ${userId}: ${error.message}`);
+
+ return data;
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index c0101bc..52056e7 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,6 +1,7 @@
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import StyledComponentsRegistry from '@/lib/registry';
+import ProfileProvider from '@/utils/ProfileProvider';
// font definitions
const sans = Inter({
@@ -22,7 +23,9 @@ export default function RootLayout({
return (
- {children}
+
+ {children}
+
);
diff --git a/app/onboarding/page.tsx b/app/onboarding/page.tsx
index c75cf7d..7d068ea 100644
--- a/app/onboarding/page.tsx
+++ b/app/onboarding/page.tsx
@@ -1,37 +1,52 @@
'use client';
import React, { useState } from 'react';
-import { upsertProfile } from '@/api/supabase/queries/profiles';
-import { Profile } from '@/types/schema';
+import { UUID } from 'crypto';
+import { Profile, UserTypeEnum } from '@/types/schema';
+import { useProfile } from '@/utils/ProfileProvider';
+
+type Options = {
+ label: string; // Represents the text for UI
+ value: T; // Represents the value stored in the DB
+};
// Define the possible options for each question
-const states = ['Tennessee', 'Missouri'];
-const gardenTypes = ['Individual', 'Community', 'School'];
-const plotOptions = [
+const states: Options[] = [
+ { label: 'Tennesee', value: 'TENNESSEE' },
+ { label: 'Missouri', value: 'MISSOUR' },
+];
+const gardenTypes: Options[] = [
+ { label: 'Individual', value: 'INDIV' },
+ { label: 'School', value: 'SCHOOL' },
+ { label: 'Organization', value: 'ORG' },
+];
+const plotOptions: Options[] = [
{ label: 'yes', value: true },
{ label: 'no', value: false },
];
+const placeholderUserId: UUID = '0802d796-ace8-480d-851b-d16293c74a21';
+
//Interfaces and props to avoid typ errors on Lint
interface StateSelectionProps {
- selectedState: string;
- setSelectedState: React.Dispatch>;
+ selectedState?: string;
+ setSelectedState: (selected: string) => void;
}
-interface GardenTypeSelectionProps {
- selectedGardenType: string;
- setSelectedGardenType: React.Dispatch>;
+interface UserTypeSelectionProps {
+ selectedUserType?: UserTypeEnum;
+ setSelectedUserType: (selected: UserTypeEnum) => void;
}
interface PlotSelectionProps {
- selectedPlot: boolean | null;
- setSelectedPlot: React.Dispatch>;
+ selectedPlot?: boolean;
+ setSelectedPlot: (selected: boolean) => void;
}
// Select State
-const StateSelection: React.FC = ({
+const StateSelection = ({
selectedState,
setSelectedState,
-}) => {
+}: StateSelectionProps) => {
return (
Which state do you live in?
@@ -43,8 +58,8 @@ const StateSelection: React.FC
= ({
Select a state
{states.map(state => (
-
))}
@@ -52,25 +67,25 @@ const StateSelection: React.FC = ({
);
};
-// Step 2: Select garden type
+// Step 2: Select user type
-const GardenTypeSelection: React.FC = ({
- selectedGardenType,
- setSelectedGardenType,
-}) => {
+const UserTypeSelection = ({
+ selectedUserType,
+ setSelectedUserType,
+}: UserTypeSelectionProps) => {
return (
What type of garden do you want to create?
{gardenTypes.map(type => (
-
@@ -78,10 +93,10 @@ const GardenTypeSelection: React.FC = ({
};
// Step 3: Do you have a plot?
-const PlotSelection: React.FC = ({
+const PlotSelection = ({
selectedPlot,
setSelectedPlot,
-}) => {
+}: PlotSelectionProps) => {
return (
Do you already have a plot?
@@ -91,7 +106,11 @@ const PlotSelection: React.FC
= ({
type="radio"
name="plot"
value={String(option.value)}
- checked={selectedPlot === option.value}
+ checked={
+ typeof selectedPlot === 'undefined'
+ ? false
+ : selectedPlot === option.value
+ }
onChange={() => setSelectedPlot(option.value)}
/>
{option.label}
@@ -102,11 +121,16 @@ const PlotSelection: React.FC = ({
};
// Main Onboarding Component
-const OnboardingFlow = () => {
+export default function OnboardingFlow() {
+ const { setProfile, setHasPlot } = useProfile();
const [step, setStep] = useState(1);
const [selectedState, setSelectedState] = useState('');
- const [selectedGardenType, setSelectedGardenType] = useState('');
- const [selectedPlot, setSelectedPlot] = useState(false);
+ const [selectedUserType, setSelectedUserType] = useState<
+ UserTypeEnum | undefined
+ >(undefined);
+ const [selectedPlot, setSelectedPlot] = useState(
+ undefined,
+ );
const handleNext = () => {
setStep(step + 1);
@@ -115,24 +139,25 @@ const OnboardingFlow = () => {
const handleBack = () => {
setStep(step - 1);
};
-
const handleSubmit = async () => {
- const profile: Profile = {
- user_id: '2abd7296-374a-42d1-bb4f-b813da1615ae',
- state: selectedState,
- user_type: selectedGardenType,
- has_plot: selectedPlot,
- };
try {
- upsertProfile(profile);
+ const profileToUpload: Profile = {
+ user_id: placeholderUserId,
+ us_state: selectedState,
+ user_type: selectedUserType!,
+ };
+
+ await setProfile(profileToUpload);
+ await setHasPlot(selectedPlot!); // Update has_plot
} catch (error) {
- console.error('Error upserting profile:', error);
- throw new Error('Error upserting profile');
- } finally {
- //TODO: Remove console log.
- console.log('Submitted data: ', profile);
+ console.error('Error submitting profile:', error);
}
- // Handle form submission, e.g., send to a server or display a confirmation
+ };
+
+ const disableNext = () => {
+ if (step === 1) return !selectedState;
+ if (step === 2) return !selectedUserType;
+ if (step === 3) return !(typeof selectedPlot === 'undefined');
};
return (
@@ -144,9 +169,9 @@ const OnboardingFlow = () => {
/>
)}
{step === 2 && (
-
)}
{step === 3 && (
@@ -159,7 +184,7 @@ const OnboardingFlow = () => {
{step > 1 && }
{step < 3 && (
-
);
-};
-
-export default OnboardingFlow;
+}
diff --git a/app/page.tsx b/app/page.tsx
index d88a5b5..1e948e7 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,11 +1,10 @@
-import BPLogo from '@/assets/images/bp-logo.png';
-import { Container, Image } from './page.style';
+import OnboardingFlow from './onboarding/page';
+import { Container } from './page.style';
export default function Home() {
return (
-
- Open up app/page.tsx to get started!
+
);
}
diff --git a/hooks/useProfile.ts b/hooks/useProfile.ts
new file mode 100644
index 0000000..3543922
--- /dev/null
+++ b/hooks/useProfile.ts
@@ -0,0 +1,31 @@
+import { useState } from 'react';
+import { Profile } from '../types/schema';
+
+const initialProfiles: Profile[] = [];
+
+export const useProfile = () => {
+ const [profiles, setProfiles] = useState(initialProfiles);
+
+ const addProfile = (newProfile: Profile) => {
+ setProfiles(prev => [...prev, newProfile]);
+ };
+
+ const updateProfile = (index: number, updates: Partial) => {
+ setProfiles(prev =>
+ prev.map((profile, i) =>
+ i === index ? { ...profile, ...updates } : profile,
+ ),
+ );
+ };
+
+ const removeProfile = (index: number) => {
+ setProfiles(prev => prev.filter((_, i) => i !== index));
+ };
+
+ return {
+ profiles,
+ addProfile,
+ updateProfile,
+ removeProfile,
+ };
+};
diff --git a/types/schema.d.ts b/types/schema.d.ts
index ec92e59..1882b95 100644
--- a/types/schema.d.ts
+++ b/types/schema.d.ts
@@ -1,14 +1,13 @@
import type { UUID } from 'crypto';
export type SeasonEnum = 'SPRING' | 'SUMMER' | 'FALL' | 'WINTER';
-
+export type UserTypeEnum = 'INDIV' | 'SCHOOL' | 'ORG';
export type DifficultyLevelEnum = 'EASY' | 'MODERATE' | 'HARD';
export interface Profile {
user_id: UUID;
- state: string;
- user_type: string;
- has_plot: boolean;
+ us_state: string;
+ user_type: UserTypeEnum;
}
export interface Plant {
diff --git a/utils/ProfileProvider.tsx b/utils/ProfileProvider.tsx
new file mode 100644
index 0000000..13a63ff
--- /dev/null
+++ b/utils/ProfileProvider.tsx
@@ -0,0 +1,89 @@
+'use client';
+
+import React, {
+ createContext,
+ ReactNode,
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react';
+import {
+ fetchProfileById,
+ upsertProfile,
+} from '@/api/supabase/queries/profiles';
+import { Profile } from '@/types/schema';
+
+// Define a placeholder user ID for development purposes
+const placeholderUserId = '0802d796-ace8-480d-851b-d16293c74a21';
+
+export interface ProfileContextType {
+ profileData: Profile | null;
+ profileReady: boolean;
+ hasPlot: boolean | null;
+ setProfile: (completeProfile: Profile) => Promise; // Now expects full Profile
+ loadProfile: () => Promise;
+ setHasPlot: (plotValue: boolean | null) => void;
+}
+
+const ProfileContext = createContext(undefined);
+
+export const useProfile = () => {
+ const context = useContext(ProfileContext);
+ if (!context) {
+ throw new Error('useProfile must be used within a ProfileProvider');
+ }
+ return context;
+};
+
+interface ProfileProviderProps {
+ children: ReactNode;
+}
+export default function ProfileProvider({ children }: ProfileProviderProps) {
+ const [profileData, setProfileData] = useState(null);
+ const [profileReady, setProfileReady] = useState(false);
+ const [hasPlot, setHasPlot] = useState(null);
+
+ const loadProfile = useCallback(async () => {
+ setProfileReady(false);
+
+ const fetchedProfile = await fetchProfileById(placeholderUserId);
+
+ setProfileData(fetchedProfile);
+ setProfileReady(true);
+ }, []);
+
+ useEffect(() => {
+ loadProfile();
+ }, [loadProfile]);
+
+ const setProfile = useCallback(async (completeProfile: Profile) => {
+ try {
+ const updatedProfile = await upsertProfile(completeProfile);
+ setProfileData(updatedProfile);
+ // Update has_plot if necessary by separate logic
+ } catch (error) {
+ console.error('Error setting profile:', error);
+ throw new Error('Error setting profile');
+ }
+ }, []);
+
+ const providerValue = useMemo(
+ () => ({
+ profileData,
+ profileReady,
+ hasPlot: hasPlot,
+ setProfile,
+ loadProfile,
+ setHasPlot,
+ }),
+ [profileData, profileReady, hasPlot, setProfile, loadProfile, setHasPlot],
+ );
+
+ return (
+
+ {children}
+
+ );
+}