From d494c8630dc18b4cdfdfe856eb6c74cf170ef22d Mon Sep 17 00:00:00 2001 From: Sashank Balusu Date: Sat, 7 Dec 2024 01:58:13 -0800 Subject: [PATCH 01/15] profile context integration with view plants and add detaisl --- app/add-details/page.tsx | 77 +++++++--------------------- app/view-plants/page.tsx | 102 ++++++++++++++++++++++++-------------- utils/ProfileProvider.tsx | 9 +++- 3 files changed, 92 insertions(+), 96 deletions(-) diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index 2183a0a..5e1af72 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -2,65 +2,24 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; -import { UUID } from 'crypto'; import { insertUserPlants } from '@/api/supabase/queries/userPlants'; import PlantDetails from '@/components/PlantDetails'; -import { Plant, UserPlant } from '@/types/schema'; - -const plants: Plant[] = [ - { - id: 'cfed129c-1cdf-4089-89d2-83ae2fb2f83d', - plant_name: 'cabbage', - us_state: 'string', - harvest_season: 'SPRING', - water_frequency: 'string', - weeding_frequency: 'string', - indoors_start: 'string', - indoors_end: 'string', - outdoors_start: 'string', - outdoors_end: 'string', - transplant_start: 'string', - transplant_end: 'string', - harvest_start: 'string', - harvest_end: 'string', - beginner_friendly: true, - plant_tips: 'string', - img: 'string', - difficulty_level: 'HARD', - sunlight_min_hours: 1, - sunlight_max_hours: 1, - }, - { - id: '8f25fca8-6e86-486b-9a2b-79f68efa3658', - plant_name: 'tomato', - us_state: 'string', - harvest_season: 'SPRING', - water_frequency: 'string', - weeding_frequency: 'string', - indoors_start: 'string', - indoors_end: 'string', - outdoors_start: 'string', - outdoors_end: 'string', - transplant_start: 'string', - transplant_end: 'string', - harvest_start: 'string', - harvest_end: 'string', - beginner_friendly: true, - plant_tips: 'string', - img: 'string', - difficulty_level: 'HARD', - sunlight_min_hours: 1, - sunlight_max_hours: 1, - }, -]; -const user_id: UUID = '0802d796-ace8-480d-851b-d16293c74a21'; +import { UserPlant } from '@/types/schema'; +import { useAuth } from '@/utils/AuthProvider'; +import { useProfile } from '@/utils/ProfileProvider'; export default function Home() { + const { profileData, profileReady, plantsToAdd } = useProfile(); + const { userId } = useAuth(); + const router = useRouter(); + + if (profileReady && !profileData) { + router.push('/view-plants'); + } const [currentIndex, setCurrentIndex] = useState(1); const [details, setDetails] = useState[]>( - plants.map(plant => ({ plant_id: plant.id, user_id: user_id })), + plantsToAdd.map(plant => ({ plant_id: plant.id, user_id: userId! })), ); - const router = useRouter(); const getDefaultDate = () => new Date().toISOString().substring(0, 10); @@ -71,7 +30,7 @@ export default function Home() { // Set curr date in details to default date if not on submission page if ( (!currentDetail || !currentDetail.date_added) && - currentIndex <= plants.length + currentIndex <= plantsToAdd.length ) { updateInput('date_added', getDefaultDate()); } @@ -79,7 +38,7 @@ export default function Home() { if ( steps !== 0 && currentIndex + steps > 0 && - currentIndex + steps <= plants.length + 1 + currentIndex + steps <= plantsToAdd.length + 1 ) { setCurrentIndex(prevIndex => prevIndex + steps); } @@ -104,16 +63,16 @@ export default function Home() { } async function updateDB() { - await insertUserPlants(user_id, details); + await insertUserPlants(userId!, details); router.push('/view-plants'); } return (
- {currentIndex !== plants.length + 1 && ( + {currentIndex !== plantsToAdd.length + 1 && (
updateInput('date_added', date)} @@ -121,14 +80,14 @@ export default function Home() { />

- {currentIndex} / {plants.length} + {currentIndex} / {plantsToAdd.length}

)} - {currentIndex === plants.length + 1 && ( + {currentIndex === plantsToAdd.length + 1 && (
diff --git a/app/view-plants/page.tsx b/app/view-plants/page.tsx index 9129167..db5f5a4 100644 --- a/app/view-plants/page.tsx +++ b/app/view-plants/page.tsx @@ -2,7 +2,6 @@ import { useEffect, useMemo, useState } from 'react'; import { useRouter } from 'next/navigation'; -import { UUID } from 'crypto'; import { getAllPlants, getMatchingPlantForUserPlant, @@ -21,6 +20,7 @@ import { SeasonEnum, SunlightEnum, } from '@/types/schema'; +import { useAuth } from '@/utils/AuthProvider'; import { checkDifficulty, checkGrowingSeason, @@ -62,7 +62,9 @@ const growingSeasonOptions: DropdownOption[] = [ export default function Page() { const router = useRouter(); - const { hasPlot } = useProfile(); + const { hasPlot, profileData, profileReady, setPlantsToAdd } = useProfile(); + const { userId } = useAuth(); + const [viewingOption, setViewingOption] = useState<'myPlants' | 'all'>( hasPlot ? 'myPlants' : 'all', ); @@ -78,44 +80,45 @@ export default function Page() { DropdownOption[] >([]); const [searchTerm, setSearchTerm] = useState(''); - const user_id: UUID = '0802d796-ace8-480d-851b-d16293c74a21'; const [selectedPlants, setSelectedPlants] = useState([]); const [ownedPlants, setOwnedPlants] = useState([]); - // TODO: replace this with state from ProfileContext - const userState = 'TENNESSEE'; + const userState = profileData?.us_state ?? 'TENNESSEE'; // Fetch All Plants useEffect(() => { - (async () => { - const plantList = await getAllPlants(); - // Filter by user's state, since they can only access when onboarded - // TODO: add userState to dependency array? - // Sort alphabetically first - const result = plantList - .filter(plant => plant.us_state === userState) - .sort((a, b) => a.plant_name.localeCompare(b.plant_name)); - setPlants(result); - })(); - }, []); + // Only fetch plants when profile is ready and we have a state + if (profileReady && userState) { + (async () => { + const plantList = await getAllPlants(); + const result = plantList + .filter(plant => plant.us_state === userState) + .sort((a, b) => a.plant_name.localeCompare(b.plant_name)); + setPlants(result); + })(); + } + }, [profileReady, userState]); // Fetch User Plants for My Garden tab useEffect(() => { - const fetchUserPlants = async () => { - const fetchedUserPlants = await getCurrentUserPlantsByUserId(user_id); + // Only fetch user plants if we have a valid userId + if (userId) { + const fetchUserPlants = async () => { + const fetchedUserPlants = await getCurrentUserPlantsByUserId(userId); - const ownedPlants: OwnedPlant[] = await Promise.all( - fetchedUserPlants.map(async userPlant => { - const plant = await getMatchingPlantForUserPlant(userPlant); - return { - userPlantId: userPlant.id, - plant, - }; - }), - ); - setOwnedPlants(ownedPlants); - }; - fetchUserPlants(); - }, []); + const ownedPlants: OwnedPlant[] = await Promise.all( + fetchedUserPlants.map(async userPlant => { + const plant = await getMatchingPlantForUserPlant(userPlant); + return { + userPlantId: userPlant.id, + plant, + }; + }), + ); + setOwnedPlants(ownedPlants); + }; + fetchUserPlants(); + } + }, [userId]); const clearFilters = () => { setSelectedGrowingSeason([]); @@ -171,14 +174,37 @@ export default function Page() { } } function handleAddPlants() { - //TODO: route to add details with proper information - router.push('/add-details'); // use CONFIG later + setPlantsToAdd(selectedPlants); + + router.push('/add-details'); } function handleCancelAddMode() { setSelectedPlants([]); setInAddMode(false); } + // Not logged in + if (!userId) { + return ( +
+

Login to view all plants

+ +
+ ); + } + + // Not onboarded + if (profileReady && !profileData) { + return ( +
+

Complete Your Profile

+

Please complete your onboarding to access view plants

+ +
+ ); + } const plantPluralityString = selectedPlants.length > 1 ? 'Plants' : 'Plant'; @@ -274,15 +300,18 @@ export default function Page() { ) : (
- +

No plants match your current filters.

)}
)} {viewingOption === 'all' && ( <> + filteredPlantList.length === 0 ? ( +
+

No plants match your current filters.

+
+ ) : ( {filteredPlantList.map(plant => ( ))} + ) {inAddMode && ( Promise; // Now expects full Profile loadProfile: () => Promise; setHasPlot: (plotValue: boolean | null) => void; + setPlantsToAdd: (plants: Plant[]) => void; } const ProfileContext = createContext(undefined); @@ -43,6 +45,7 @@ export default function ProfileProvider({ children }: ProfileProviderProps) { const [profileData, setProfileData] = useState(null); const [profileReady, setProfileReady] = useState(false); const [hasPlot, setHasPlot] = useState(null); + const [plantsToAdd, setPlantsToAdd] = useState([]); const loadProfile = useCallback(async () => { if (!userId) { @@ -87,6 +90,8 @@ export default function ProfileProvider({ children }: ProfileProviderProps) { profileData, profileReady, hasPlot, + plantsToAdd, + setPlantsToAdd, setProfile, loadProfile, setHasPlot: updateHasPlot, @@ -95,6 +100,8 @@ export default function ProfileProvider({ children }: ProfileProviderProps) { profileData, profileReady, hasPlot, + plantsToAdd, + setPlantsToAdd, setProfile, loadProfile, setHasPlot, From 1596b4d630c88afb438ea638ae3c50472abee590 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Sat, 7 Dec 2024 14:10:31 -0800 Subject: [PATCH 02/15] add loading logic --- app/view-plants/page.tsx | 46 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/app/view-plants/page.tsx b/app/view-plants/page.tsx index db5f5a4..c5e0520 100644 --- a/app/view-plants/page.tsx +++ b/app/view-plants/page.tsx @@ -286,7 +286,11 @@ export default function Page() { {viewingOption === 'myPlants' && (
- {filteredUserPlantList.length ? ( + {ownedPlants.length === 0 ? ( + <>Add Plants To Your Garden + ) : filteredUserPlantList.length === 0 ? ( +

No plants match your current filters.

+ ) : ( {filteredUserPlantList.map(ownedPlant => ( ))} - ) : ( -
-

No plants match your current filters.

-
)}
)} {viewingOption === 'all' && ( <> - filteredPlantList.length === 0 ? ( -
-

No plants match your current filters.

-
+ {plants.length === 0 ? ( + <>Loading... + ) : filteredPlantList.length === 0 ? ( +
+

No plants match your current filters.

+
) : ( - - {filteredPlantList.map(plant => ( - handlePlantCardClick(plant)} - // aspectRatio="168 / 200" - /> - ))} - - ) + + {filteredPlantList.map(plant => ( + handlePlantCardClick(plant)} + // aspectRatio="168 / 200" + /> + ))} + + )} {inAddMode && ( Date: Sat, 7 Dec 2024 14:32:47 -0800 Subject: [PATCH 03/15] implement add functionality on GeneralPlantPage --- app/plant-page/all-plants/[plantId]/page.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/plant-page/all-plants/[plantId]/page.tsx b/app/plant-page/all-plants/[plantId]/page.tsx index da0c4c8..d30e163 100644 --- a/app/plant-page/all-plants/[plantId]/page.tsx +++ b/app/plant-page/all-plants/[plantId]/page.tsx @@ -11,6 +11,7 @@ import PlantCareDescription from '@/components/PlantCareDescription'; import { Flex } from '@/styles/containers'; import { H4 } from '@/styles/text'; import { Plant } from '@/types/schema'; +import { useProfile } from '@/utils/ProfileProvider'; import { BackButton, ButtonWrapper, @@ -29,6 +30,8 @@ export default function GeneralPlantPage() { const params = useParams(); const plantId: UUID = params.plantId as UUID; const [currentPlant, setCurrentPlant] = useState(); + const { profileReady, profileData, setPlantsToAdd } = useProfile(); + useEffect(() => { const getPlant = async () => { const plant = await getPlantById(plantId); @@ -36,6 +39,14 @@ export default function GeneralPlantPage() { }; getPlant(); }, [plantId]); + + const handleAdd = () => { + // assume user is onboarded + if (!currentPlant) return; + setPlantsToAdd([currentPlant]); + router.push('/add-details'); + }; + return currentPlant ? ( <> @@ -58,7 +69,10 @@ export default function GeneralPlantPage() { difficultyLevel={currentPlant.difficulty_level} /> - Add + + {/*Add button only appears if user is logged in and onboarded*/} + {profileReady && profileData && ( + Add + + )} Date: Sun, 8 Dec 2024 15:44:39 -0800 Subject: [PATCH 04/15] yikes --- app/add-details/page.tsx | 28 ++++++++-- components/DateInput/index.tsx | 89 +++++++++++++++++++++++++++++++ components/DateInput/styles.ts | 74 +++++++++++++++++++++++++ components/PlantDetails/index.tsx | 31 +++++++---- 4 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 components/DateInput/index.tsx create mode 100644 components/DateInput/styles.ts diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index 5e1af72..1489c29 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -61,7 +61,15 @@ export default function Home() { }; setDetails(updatedDetails); } - + // const handleSubmit = async () => { + // try { + // await insertUserPlants(userId!, details); + // router.push('/view-plants'); + // } catch (error) { + // console.error('Error inserting user plants:', error); + // // Optionally, add user-facing error handling + // } + // }; async function updateDB() { await insertUserPlants(userId!, details); router.push('/view-plants'); @@ -78,19 +86,29 @@ export default function Home() { onDateChange={date => updateInput('date_added', date)} onPlantingTypeChange={type => updateInput('planting_type', type)} /> - +

{currentIndex} / {plantsToAdd.length}

-
)} {currentIndex === plantsToAdd.length + 1 && (
- - + +
)} diff --git a/components/DateInput/index.tsx b/components/DateInput/index.tsx new file mode 100644 index 0000000..9cf9f26 --- /dev/null +++ b/components/DateInput/index.tsx @@ -0,0 +1,89 @@ +import React, { useEffect, useRef } from 'react'; +import Icon from '../Icon'; +import { + DateInputWrapper, + DropdownIcon, + HiddenDateInput, + SelectContainer, + SelectedValue, +} from './styles'; + +interface DateInputProps { + value: string; + onChange: (value: string) => void; + label?: string; + min?: string; + max?: string; +} + +const DateInput = ({ + value, + onChange, + label = 'Select Date', + min, + max, +}: DateInputProps) => { + const containerRef = useRef(null); + const hiddenInputRef = useRef(null); + + // Close the dropdown when clicking outside + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + containerRef.current && + !containerRef.current.contains(event.target as Node) + ) { + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + // Automatically show picker when dropdown icon is clicked + const handleDropdownClick = () => { + // Use requestAnimationFrame to ensure the input is rendered before showing picker + requestAnimationFrame(() => { + hiddenInputRef.current?.showPicker(); + }); + }; + + const formatDate = (dateString: string) => { + if (!dateString) return label; + + try { + return new Date(dateString).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + } catch { + return dateString; + } + }; + + return ( + + + {formatDate(value)} + + + + + { + onChange(e.target.value); + }} + min={min} + max={max} + /> + + + ); +}; + +export default DateInput; diff --git a/components/DateInput/styles.ts b/components/DateInput/styles.ts new file mode 100644 index 0000000..ee26d06 --- /dev/null +++ b/components/DateInput/styles.ts @@ -0,0 +1,74 @@ +import styled from 'styled-components'; +import COLORS from '@/styles/colors'; + +export const DropdownIcon = styled.button` + background: none; + border: none; + cursor: pointer; + color: ${COLORS.sprout}; + font-size: 1.25rem; + display: flex; + align-items: center; + svg { + fill: ${COLORS.shrub}; + width: 1.25rem; + height: 1.25rem; + } +`; + +export const OptionsContainer = styled.div` + position: absolute; + top: 100%; + left: 0; + right: 0; + background: white; + border: 1px solid ${COLORS.lightgray}; + border-radius: 5px; + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1); + margin-top: 5px; + z-index: 10; + width: 100%; +`; + +export const Option = styled.div` + padding: 0.5rem 1rem; + background: #f9f9f9; + cursor: pointer; + color: ${COLORS.shrub}; + &:hover { + background: ${COLORS.sprout}; + } +`; +export const SelectContainer = styled.div` + display: flex; + align-items: center; + position: relative; + padding: 1rem; + border: 2px solid ${COLORS.lightgray}; + border-radius: 5px; + cursor: pointer; + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1); + height: 3rem; + box-sizing: border-box; + width: 100%; +`; + +export const SelectedValue = styled.span` + flex-grow: 1; + font-size: 1rem; + color: ${COLORS.shrub}; +`; +export const DateInputWrapper = styled.div` + position: relative; + width: 100%; +`; + +export const HiddenDateInput = styled.input` + position: absolute; + top: 100%; + left: 0; + width: 0; + height: 0; + opacity: 0; + pointer-events: none; +`; diff --git a/components/PlantDetails/index.tsx b/components/PlantDetails/index.tsx index 543e37b..42db844 100644 --- a/components/PlantDetails/index.tsx +++ b/components/PlantDetails/index.tsx @@ -1,4 +1,6 @@ import { Plant } from '@/types/schema'; +import CustomSelect from '../CustomSelect'; +import DateInput from '../DateInput'; export default function PlantDetails({ plant, @@ -13,29 +15,38 @@ export default function PlantDetails({ onDateChange: (date: string) => void; onPlantingTypeChange: (type: string) => void; }) { + const plantingTypeOptions = [ + { value: 'SELECT', label: 'Select option' }, + { value: 'TRANSPLANT', label: 'Transplant' }, + { value: 'INDOORS', label: 'Indoors' }, + { value: 'OUTDOORS', label: 'Outdoors' }, + ]; + return (

{plant.plant_name}

- onDateChange(e.target.value)} + /> */} + - + options={plantingTypeOptions} + onChange={onPlantingTypeChange} + label="Select option" + />
); } From 9d3d390100837ae3759c20f21e9175f11fe6e90e Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Sun, 8 Dec 2024 17:04:42 -0800 Subject: [PATCH 05/15] style PlantDetails --- components/DateInput/index.tsx | 6 +- components/PlantDetails/index.tsx | 95 +++++++++++++++++++++---------- 2 files changed, 69 insertions(+), 32 deletions(-) diff --git a/components/DateInput/index.tsx b/components/DateInput/index.tsx index 9cf9f26..bd33795 100644 --- a/components/DateInput/index.tsx +++ b/components/DateInput/index.tsx @@ -11,7 +11,7 @@ import { interface DateInputProps { value: string; onChange: (value: string) => void; - label?: string; + placeholder?: string; min?: string; max?: string; } @@ -19,7 +19,7 @@ interface DateInputProps { const DateInput = ({ value, onChange, - label = 'Select Date', + placeholder = '', min, max, }: DateInputProps) => { @@ -50,7 +50,7 @@ const DateInput = ({ }; const formatDate = (dateString: string) => { - if (!dateString) return label; + if (!dateString) return placeholder; try { return new Date(dateString).toLocaleDateString('en-US', { diff --git a/components/PlantDetails/index.tsx b/components/PlantDetails/index.tsx index 42db844..f08a204 100644 --- a/components/PlantDetails/index.tsx +++ b/components/PlantDetails/index.tsx @@ -1,7 +1,19 @@ -import { Plant } from '@/types/schema'; +import Image from 'next/image'; +import BPLogo from '@/assets/images/bp-logo.png'; // to do: remove this + +import COLORS from '@/styles/colors'; +import { Box, Flex } from '@/styles/containers'; +import { H3, P2 } from '@/styles/text'; +import { DropdownOption, Plant, PlantingTypeEnum } from '@/types/schema'; import CustomSelect from '../CustomSelect'; import DateInput from '../DateInput'; +const plantingTypeOptions: DropdownOption[] = [ + { value: 'TRANSPLANT', label: 'Transplant' }, + { value: 'INDOORS', label: 'Indoors' }, + { value: 'OUTDOORS', label: 'Outdoors' }, +]; + export default function PlantDetails({ plant, date, @@ -15,38 +27,63 @@ export default function PlantDetails({ onDateChange: (date: string) => void; onPlantingTypeChange: (type: string) => void; }) { - const plantingTypeOptions = [ - { value: 'SELECT', label: 'Select option' }, - { value: 'TRANSPLANT', label: 'Transplant' }, - { value: 'INDOORS', label: 'Indoors' }, - { value: 'OUTDOORS', label: 'Outdoors' }, - ]; + const defautImg = ''; return (
-

{plant.plant_name}

- - - {/* onDateChange(e.target.value)} - /> */} - - - + + {`Plant + + +

+ {plant.plant_name} +

+ {/* onDateChange(e.target.value)} + /> */} + + + {/*TODO: Move label into DateInput component*/} + + Date Planted + + + - + + + Planting Type + + + + +
); } From be1ed9998c8ad06faf512ce3e3c09d9562690494 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Sun, 8 Dec 2024 17:49:54 -0800 Subject: [PATCH 06/15] add placeholder image in PlantDetails --- components/PlantDetails/index.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/components/PlantDetails/index.tsx b/components/PlantDetails/index.tsx index f08a204..bc6f888 100644 --- a/components/PlantDetails/index.tsx +++ b/components/PlantDetails/index.tsx @@ -1,5 +1,5 @@ import Image from 'next/image'; -import BPLogo from '@/assets/images/bp-logo.png'; // to do: remove this +import BPLogo from '@/assets/images/bp-logo.png'; // TODO: remove this import COLORS from '@/styles/colors'; import { Box, Flex } from '@/styles/containers'; @@ -27,10 +27,8 @@ export default function PlantDetails({ onDateChange: (date: string) => void; onPlantingTypeChange: (type: string) => void; }) { - const defautImg = ''; - return ( -
+ <> {`Plant @@ -84,6 +82,6 @@ export default function PlantDetails({ -
+ ); } From 93ca2d3614b6a0f9ed2771ea5ada55dcdce68ab0 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Sun, 8 Dec 2024 17:51:37 -0800 Subject: [PATCH 07/15] add header in add-details --- app/add-details/page.tsx | 60 ++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index 1489c29..94f9f5e 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -4,6 +4,9 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { insertUserPlants } from '@/api/supabase/queries/userPlants'; import PlantDetails from '@/components/PlantDetails'; +import COLORS from '@/styles/colors'; +import { Flex } from '@/styles/containers'; +import { H1, P1 } from '@/styles/text'; import { UserPlant } from '@/types/schema'; import { useAuth } from '@/utils/AuthProvider'; import { useProfile } from '@/utils/ProfileProvider'; @@ -76,30 +79,39 @@ export default function Home() { } return ( -
+ <> {currentIndex !== plantsToAdd.length + 1 && ( -
- updateInput('date_added', date)} - onPlantingTypeChange={type => updateInput('planting_type', type)} - /> - -

- {currentIndex} / {plantsToAdd.length} -

- -
+ + + +

Add Plant Details

+ + {currentIndex} / {plantsToAdd.length} + +
+ updateInput('date_added', date)} + onPlantingTypeChange={type => updateInput('planting_type', type)} + /> +
+ + {' '} + {/*Style the Button Footer*/} + + + +
)} {currentIndex === plantsToAdd.length + 1 && (
@@ -111,6 +123,6 @@ export default function Home() {
)} -
+ ); } From dcaba4adcbab7ea39f630f59a20c806e52de07b1 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Sun, 8 Dec 2024 17:58:05 -0800 Subject: [PATCH 08/15] remove fallback userState --- app/view-plants/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/view-plants/page.tsx b/app/view-plants/page.tsx index c5e0520..31c58e0 100644 --- a/app/view-plants/page.tsx +++ b/app/view-plants/page.tsx @@ -82,7 +82,7 @@ export default function Page() { const [searchTerm, setSearchTerm] = useState(''); const [selectedPlants, setSelectedPlants] = useState([]); const [ownedPlants, setOwnedPlants] = useState([]); - const userState = profileData?.us_state ?? 'TENNESSEE'; + const userState = profileData?.us_state ?? null; // Fetch All Plants useEffect(() => { From 779c8c84982c13201c63d430f6e154242f6b5a82 Mon Sep 17 00:00:00 2001 From: Sashank Balusu Date: Tue, 10 Dec 2024 16:02:41 -0800 Subject: [PATCH 09/15] footer button styling and nav bar --- app/add-details/page.tsx | 51 +++++++--- app/add-details/styles.ts | 28 ++++++ components/CustomSelect/index.tsx | 7 +- components/DateInput/index.tsx | 2 +- components/NavigationBar/index.tsx | 152 +++++++++++++++++++++++++++++ components/PlantDetails/index.tsx | 1 + 6 files changed, 224 insertions(+), 17 deletions(-) create mode 100644 app/add-details/styles.ts create mode 100644 components/NavigationBar/index.tsx diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index 94f9f5e..08b6ea1 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -3,13 +3,15 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { insertUserPlants } from '@/api/supabase/queries/userPlants'; +import NavigationBar from '@/components/NavigationBar'; import PlantDetails from '@/components/PlantDetails'; import COLORS from '@/styles/colors'; import { Flex } from '@/styles/containers'; -import { H1, P1 } from '@/styles/text'; +import { H1 } from '@/styles/text'; import { UserPlant } from '@/types/schema'; import { useAuth } from '@/utils/AuthProvider'; import { useProfile } from '@/utils/ProfileProvider'; +import { FooterButton, MoveButton } from './styles'; export default function Home() { const { profileData, profileReady, plantsToAdd } = useProfile(); @@ -77,6 +79,9 @@ export default function Home() { await insertUserPlants(userId!, details); router.push('/view-plants'); } + const handlePlantSelection = (index: number) => { + setCurrentIndex(index); + }; return ( <> @@ -85,9 +90,17 @@ export default function Home() {

Add Plant Details

- + {/* {currentIndex} / {plantsToAdd.length} - + */} + move(-1)} + onNext={() => move(1)} + onSelectPlant={handlePlantSelection} + />
- {' '} - {/*Style the Button Footer*/} - - + + {currentIndex > 1 && ( + move(-1)} + $backgroundColor={COLORS.shrub} + > + Back + + )} + + move(1)} + $backgroundColor={disableNext() ? COLORS.midgray : COLORS.shrub} + > + Next + + )} diff --git a/app/add-details/styles.ts b/app/add-details/styles.ts new file mode 100644 index 0000000..c049996 --- /dev/null +++ b/app/add-details/styles.ts @@ -0,0 +1,28 @@ +import styled from 'styled-components'; + +export const MoveButton = styled.button<{ $backgroundColor: string }>` + background-color: ${({ $backgroundColor }) => $backgroundColor}; + color: white; + border-radius: 20px; + border: none; + font-family: inherit; + margin-bottom: 10px; + width: 170px; + height: 50px; + font-size: 15px; + font-style: normal; + font-weight: 400; + line-height: normal; + margin-left: 24px; +`; +export const FooterButton = styled.div` + display: flex; + flex-direction: row; + justify-content: end; + width: 100%; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + padding: 24px; +`; diff --git a/components/CustomSelect/index.tsx b/components/CustomSelect/index.tsx index b8d5b63..eb97ad3 100644 --- a/components/CustomSelect/index.tsx +++ b/components/CustomSelect/index.tsx @@ -14,6 +14,7 @@ interface CustomSelectProps { options: DropdownOption[]; onChange: (value: T) => void; label?: string; + isContainerClickable?: boolean; // New boolean prop } const CustomSelect = ({ @@ -21,6 +22,7 @@ const CustomSelect = ({ options, onChange, label, + isContainerClickable = false, // Default to false }: CustomSelectProps) => { const [isOpen, setIsOpen] = useState(false); const containerRef = useRef(null); @@ -47,7 +49,10 @@ const CustomSelect = ({ }, []); return ( - + setIsOpen(!isOpen) : undefined} + > {options.find(option => option.value === value)?.label || label} diff --git a/components/DateInput/index.tsx b/components/DateInput/index.tsx index bd33795..34f78f4 100644 --- a/components/DateInput/index.tsx +++ b/components/DateInput/index.tsx @@ -64,7 +64,7 @@ const DateInput = ({ }; return ( - + {formatDate(value)} diff --git a/components/NavigationBar/index.tsx b/components/NavigationBar/index.tsx new file mode 100644 index 0000000..20c4227 --- /dev/null +++ b/components/NavigationBar/index.tsx @@ -0,0 +1,152 @@ +// NavigationBar.tsx +import React, { useState } from 'react'; +import styled from 'styled-components'; +import COLORS from '@/styles/colors'; +import { Plant } from '@/types/schema'; + +interface NavigationBarProps { + currentIndex: number; + totalCount: number; + plantsToAdd: Plant[]; + onPrev: () => void; + onNext: () => void; + onSelectPlant: (index: number) => void; +} + +const NavigationBarContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 24px; +`; + +const NavigationButton = styled.button` + background-color: white; + color: ${COLORS.midgray}; + border: 1px solid ${COLORS.midgray}; + border-radius: 5px; + font-size: 14px; + cursor: pointer; + height: 30px; + width: 29px; + + &:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: none; + } + + &:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: none; + } + + &:not(:first-child) { + border-left: none; + } + + &:disabled { + cursor: not-allowed; + } +`; + +const DropdownToggle = styled.button` + height: 30px; + background-color: white; + color: ${COLORS.midgray}; + border: 1px solid #888; + font-size: 14px; + cursor: pointer; + width: 85px; +`; + +const DropdownContainer = styled.div` + position: relative; + display: flex; + justify-content: center; +`; + +const DropdownMenu = styled.div` + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + background-color: white; + border: 1px solid ${COLORS.midgray}; + border-radius: 4px; + padding: 8px 0; + z-index: 1; + min-width: 200px; +`; +const DropdownItem = styled.button` + display: block; + width: 100%; + padding: 8px 16px; + text-align: left; + background-color: transparent; + border: none; + cursor: pointer; + + &:hover { + background-color: ${COLORS.lightgray}; + } +`; + +const NavigationBar: React.FC = ({ + currentIndex, + totalCount, + plantsToAdd, + onPrev, + onNext, + onSelectPlant, +}) => { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + const toggleDropdown = () => { + setIsDropdownOpen(prevState => !prevState); + }; + + const handlePlantSelection = (index: number) => { + onSelectPlant(index); + setIsDropdownOpen(false); + }; + + return ( + + + {'<'} + + + + {currentIndex}/{plantsToAdd.length} + + {isDropdownOpen && ( + + {plantsToAdd.map((plant, index) => ( + handlePlantSelection(index + 1)} + > + {plant.plant_name} + + ))} + + )} + + + {'>'} + + + ); +}; + +export default NavigationBar; diff --git a/components/PlantDetails/index.tsx b/components/PlantDetails/index.tsx index bc6f888..251f140 100644 --- a/components/PlantDetails/index.tsx +++ b/components/PlantDetails/index.tsx @@ -77,6 +77,7 @@ export default function PlantDetails({ options={plantingTypeOptions} onChange={onPlantingTypeChange} label="Choose Planting Type" + isContainerClickable={true} // TODO: rename prop label to placeholder /> From e40731408d9bc07ccd7b309fd2138b2c3f33121d Mon Sep 17 00:00:00 2001 From: Sashank Balusu Date: Tue, 10 Dec 2024 16:19:00 -0800 Subject: [PATCH 10/15] messed up back button --- app/add-details/page.tsx | 5 +++-- app/add-details/styles.ts | 8 +++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index 08b6ea1..2c69deb 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -116,7 +116,7 @@ export default function Home() { move(-1)} - $backgroundColor={COLORS.shrub} + $secondaryColor={COLORS.shrub} > Back @@ -126,7 +126,8 @@ export default function Home() { type="button" disabled={disableNext()} onClick={() => move(1)} - $backgroundColor={disableNext() ? COLORS.midgray : COLORS.shrub} + $primaryColor={disableNext() ? COLORS.midgray : COLORS.shrub} + $secondaryColor="white" > Next diff --git a/app/add-details/styles.ts b/app/add-details/styles.ts index c049996..487f8df 100644 --- a/app/add-details/styles.ts +++ b/app/add-details/styles.ts @@ -1,10 +1,8 @@ import styled from 'styled-components'; +import { SmallRoundedButton } from '@/components/Button'; -export const MoveButton = styled.button<{ $backgroundColor: string }>` - background-color: ${({ $backgroundColor }) => $backgroundColor}; - color: white; - border-radius: 20px; - border: none; +export const MoveButton = styled(SmallRoundedButton)` + border: 1px solid; font-family: inherit; margin-bottom: 10px; width: 170px; From 99baddd55d3b107cec6531340feae9c4bb3ff694 Mon Sep 17 00:00:00 2001 From: Sashank Balusu Date: Tue, 10 Dec 2024 16:20:29 -0800 Subject: [PATCH 11/15] remove nav button usage --- app/add-details/page.tsx | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index 2c69deb..c8661ad 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -3,11 +3,10 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { insertUserPlants } from '@/api/supabase/queries/userPlants'; -import NavigationBar from '@/components/NavigationBar'; import PlantDetails from '@/components/PlantDetails'; import COLORS from '@/styles/colors'; import { Flex } from '@/styles/containers'; -import { H1 } from '@/styles/text'; +import { H1, P1 } from '@/styles/text'; import { UserPlant } from '@/types/schema'; import { useAuth } from '@/utils/AuthProvider'; import { useProfile } from '@/utils/ProfileProvider'; @@ -79,9 +78,6 @@ export default function Home() { await insertUserPlants(userId!, details); router.push('/view-plants'); } - const handlePlantSelection = (index: number) => { - setCurrentIndex(index); - }; return ( <> @@ -90,17 +86,9 @@ export default function Home() {

Add Plant Details

- {/* + {currentIndex} / {plantsToAdd.length} - */} - move(-1)} - onNext={() => move(1)} - onSelectPlant={handlePlantSelection} - /> +
Date: Fri, 13 Dec 2024 01:05:32 -0800 Subject: [PATCH 12/15] fix button placement --- app/add-details/page.tsx | 26 +++++++++++--------------- app/add-details/styles.ts | 10 +++++++++- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index c8661ad..c7894d5 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -10,7 +10,7 @@ import { H1, P1 } from '@/styles/text'; import { UserPlant } from '@/types/schema'; import { useAuth } from '@/utils/AuthProvider'; import { useProfile } from '@/utils/ProfileProvider'; -import { FooterButton, MoveButton } from './styles'; +import { ButtonDiv, FooterButton, MoveButton } from './styles'; export default function Home() { const { profileData, profileReady, plantsToAdd } = useProfile(); @@ -48,14 +48,10 @@ export default function Home() { } } - function disableNext() { - // disable next if planting type is "SELECT" or undefined - return !( - details[currentIndex - 1].planting_type - // requires refactor of details to ensure that planting_type is PlantingTypeEnum - // && details[currentIndex - 1].planting_type !== 'SELECT' - ); - } + // disable next if planting type not selected (undefined) + const disableNext = + currentIndex <= plantsToAdd.length && + !details[currentIndex - 1].planting_type; function updateInput(field: string, value: string) { const updatedDetails = [...details]; @@ -98,8 +94,8 @@ export default function Home() { onPlantingTypeChange={type => updateInput('planting_type', type)} />
- - + + {currentIndex > 1 && ( move(1)} - $primaryColor={disableNext() ? COLORS.midgray : COLORS.shrub} + $primaryColor={disableNext ? COLORS.midgray : COLORS.shrub} $secondaryColor="white" > Next - - + + )} {currentIndex === plantsToAdd.length + 1 && ( diff --git a/app/add-details/styles.ts b/app/add-details/styles.ts index 487f8df..328a451 100644 --- a/app/add-details/styles.ts +++ b/app/add-details/styles.ts @@ -11,7 +11,6 @@ export const MoveButton = styled(SmallRoundedButton)` font-style: normal; font-weight: 400; line-height: normal; - margin-left: 24px; `; export const FooterButton = styled.div` display: flex; @@ -24,3 +23,12 @@ export const FooterButton = styled.div` transform: translateX(-50%); padding: 24px; `; + +export const ButtonDiv = styled.div` + display: flex; + width: 100%; + justify-content: space-between; + &:has(:only-child) { + justify-content: flex-end; + } +`; From 7d8ef475496bb49cfe135aed94a8b31723190b2e Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Fri, 13 Dec 2024 01:09:48 -0800 Subject: [PATCH 13/15] add loading styles to view plants --- app/view-plants/page.tsx | 237 +++++++++++++++++++++------------------ 1 file changed, 130 insertions(+), 107 deletions(-) diff --git a/app/view-plants/page.tsx b/app/view-plants/page.tsx index 31c58e0..694d528 100644 --- a/app/view-plants/page.tsx +++ b/app/view-plants/page.tsx @@ -12,7 +12,7 @@ import PlantCard from '@/components/PlantCard'; import SearchBar from '@/components/SearchBar'; import COLORS from '@/styles/colors'; import { Box, Flex } from '@/styles/containers'; -import { H1 } from '@/styles/text'; +import { H1, P1 } from '@/styles/text'; import { DropdownOption, OwnedPlant, @@ -63,7 +63,7 @@ const growingSeasonOptions: DropdownOption[] = [ export default function Page() { const router = useRouter(); const { hasPlot, profileData, profileReady, setPlantsToAdd } = useProfile(); - const { userId } = useAuth(); + const { userId, loading: authLoading } = useAuth(); const [viewingOption, setViewingOption] = useState<'myPlants' | 'all'>( hasPlot ? 'myPlants' : 'all', @@ -84,6 +84,8 @@ export default function Page() { const [ownedPlants, setOwnedPlants] = useState([]); const userState = profileData?.us_state ?? null; + const profileAndAuthReady = profileReady && !authLoading; + // Fetch All Plants useEffect(() => { // Only fetch plants when profile is ready and we have a state @@ -101,7 +103,7 @@ export default function Page() { // Fetch User Plants for My Garden tab useEffect(() => { // Only fetch user plants if we have a valid userId - if (userId) { + if (!authLoading && userId) { const fetchUserPlants = async () => { const fetchedUserPlants = await getCurrentUserPlantsByUserId(userId); @@ -118,7 +120,7 @@ export default function Page() { }; fetchUserPlants(); } - }, [userId]); + }, [userId, authLoading]); const clearFilters = () => { setSelectedGrowingSeason([]); @@ -183,29 +185,138 @@ export default function Page() { setSelectedPlants([]); setInAddMode(false); } - // Not logged in - if (!userId) { + + function MainBody() { + // assume auth and profile are both ready + // Not logged in + if (!userId) { + return ( + + Login to view all plants + + + ); + } + + // Not onboarded + if (!profileData) { + return ( + + Complete your profile view all plants + + + ); + } + + // Onboarded and Logged in: Normal Screen return ( -
-

Login to view all plants

- -
+ <> + + + setViewingOption('myPlants')} + > + My Plants + + setViewingOption('all')} + > + All + + + {/* Select/Cancel toggles Add Mode; appears in All plants only*/} + {viewingOption === 'all' && + (inAddMode ? ( + + Cancel + + ) : ( + setInAddMode(true)} + > + Select + + ))} + + + {viewingOption === 'myPlants' ? ( + + ) : ( + + )} + ); } - // Not onboarded - if (profileReady && !profileData) { + function MyPlantsDisplay() { return (
-

Complete Your Profile

-

Please complete your onboarding to access view plants

- + {ownedPlants.length === 0 ? ( + <>Add Plants To Your Garden + ) : filteredUserPlantList.length === 0 ? ( +

No plants match your current filters.

+ ) : ( + + {filteredUserPlantList.map(ownedPlant => ( + handleUserPlantCardClick(ownedPlant)} + // aspectRatio="168 / 200" + /> + ))} + + )}
); } + function AllPlantsDisplay() { + return ( + <> + {filteredPlantList.length === 0 ? ( +
+

No plants match your current filters.

+
+ ) : ( + + {filteredPlantList.map(plant => ( + handlePlantCardClick(plant)} + // aspectRatio="168 / 200" + /> + ))} + + )} + {inAddMode && ( + + {selectedPlants.length ? 'Add to My Garden' : 'Select Plants'} + + )} + + ); + } + const plantPluralityString = selectedPlants.length > 1 ? 'Plants' : 'Plant'; return ( @@ -250,96 +361,8 @@ export default function Page() { ) : null} - - - setViewingOption('myPlants')} - > - My Plants - - setViewingOption('all')} - > - All - - - {/* Select/Cancel toggles Add Mode; appears in All plants only*/} - {viewingOption === 'all' && - (inAddMode ? ( - - Cancel - - ) : ( - setInAddMode(true)} - > - Select - - ))} - - {viewingOption === 'myPlants' && ( -
- {ownedPlants.length === 0 ? ( - <>Add Plants To Your Garden - ) : filteredUserPlantList.length === 0 ? ( -

No plants match your current filters.

- ) : ( - - {filteredUserPlantList.map(ownedPlant => ( - handleUserPlantCardClick(ownedPlant)} - // aspectRatio="168 / 200" - /> - ))} - - )} -
- )} - {viewingOption === 'all' && ( - <> - {plants.length === 0 ? ( - <>Loading... - ) : filteredPlantList.length === 0 ? ( -
-

No plants match your current filters.

-
- ) : ( - - {filteredPlantList.map(plant => ( - handlePlantCardClick(plant)} - // aspectRatio="168 / 200" - /> - ))} - - )} - {inAddMode && ( - - {selectedPlants.length ? 'Add to My Garden' : 'Select Plants'} - - )} - - )} + {/* Plant Cards and Body */} + {!profileAndAuthReady ? <>Loading : }
); From 7cf753e7cb5e83ecbef33b0d246b51bd8ed6cc5f Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Fri, 13 Dec 2024 01:19:42 -0800 Subject: [PATCH 14/15] remove unused NavigationBar component --- components/NavigationBar/index.tsx | 152 ----------------------------- 1 file changed, 152 deletions(-) delete mode 100644 components/NavigationBar/index.tsx diff --git a/components/NavigationBar/index.tsx b/components/NavigationBar/index.tsx deleted file mode 100644 index 20c4227..0000000 --- a/components/NavigationBar/index.tsx +++ /dev/null @@ -1,152 +0,0 @@ -// NavigationBar.tsx -import React, { useState } from 'react'; -import styled from 'styled-components'; -import COLORS from '@/styles/colors'; -import { Plant } from '@/types/schema'; - -interface NavigationBarProps { - currentIndex: number; - totalCount: number; - plantsToAdd: Plant[]; - onPrev: () => void; - onNext: () => void; - onSelectPlant: (index: number) => void; -} - -const NavigationBarContainer = styled.div` - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 24px; -`; - -const NavigationButton = styled.button` - background-color: white; - color: ${COLORS.midgray}; - border: 1px solid ${COLORS.midgray}; - border-radius: 5px; - font-size: 14px; - cursor: pointer; - height: 30px; - width: 29px; - - &:first-child { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - border-right: none; - } - - &:last-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-left: none; - } - - &:not(:first-child) { - border-left: none; - } - - &:disabled { - cursor: not-allowed; - } -`; - -const DropdownToggle = styled.button` - height: 30px; - background-color: white; - color: ${COLORS.midgray}; - border: 1px solid #888; - font-size: 14px; - cursor: pointer; - width: 85px; -`; - -const DropdownContainer = styled.div` - position: relative; - display: flex; - justify-content: center; -`; - -const DropdownMenu = styled.div` - position: absolute; - top: 100%; - left: 50%; - transform: translateX(-50%); - background-color: white; - border: 1px solid ${COLORS.midgray}; - border-radius: 4px; - padding: 8px 0; - z-index: 1; - min-width: 200px; -`; -const DropdownItem = styled.button` - display: block; - width: 100%; - padding: 8px 16px; - text-align: left; - background-color: transparent; - border: none; - cursor: pointer; - - &:hover { - background-color: ${COLORS.lightgray}; - } -`; - -const NavigationBar: React.FC = ({ - currentIndex, - totalCount, - plantsToAdd, - onPrev, - onNext, - onSelectPlant, -}) => { - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - - const toggleDropdown = () => { - setIsDropdownOpen(prevState => !prevState); - }; - - const handlePlantSelection = (index: number) => { - onSelectPlant(index); - setIsDropdownOpen(false); - }; - - return ( - - - {'<'} - - - - {currentIndex}/{plantsToAdd.length} - - {isDropdownOpen && ( - - {plantsToAdd.map((plant, index) => ( - handlePlantSelection(index + 1)} - > - {plant.plant_name} - - ))} - - )} - - - {'>'} - - - ); -}; - -export default NavigationBar; From 01150ad35abf6048cc325afd6c8fed04d3cc2ac8 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Fri, 13 Dec 2024 01:54:40 -0800 Subject: [PATCH 15/15] use async handleSubmit in add-detaisl --- app/add-details/page.tsx | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/app/add-details/page.tsx b/app/add-details/page.tsx index c7894d5..4e306d8 100644 --- a/app/add-details/page.tsx +++ b/app/add-details/page.tsx @@ -61,19 +61,16 @@ export default function Home() { }; setDetails(updatedDetails); } - // const handleSubmit = async () => { - // try { - // await insertUserPlants(userId!, details); - // router.push('/view-plants'); - // } catch (error) { - // console.error('Error inserting user plants:', error); - // // Optionally, add user-facing error handling - // } - // }; - async function updateDB() { - await insertUserPlants(userId!, details); - router.push('/view-plants'); - } + const handleSubmit = async () => { + // TODO: elegantly handle not logged in case (e.g. when someonee clicks "Back") + // instead of doing userId! + try { + await insertUserPlants(userId!, details); + router.push('/view-plants'); + } catch (error) { + console.error('Error inserting user plants:', error); + } + }; return ( <> @@ -88,8 +85,8 @@ export default function Home() { updateInput('date_added', date)} onPlantingTypeChange={type => updateInput('planting_type', type)} /> @@ -124,7 +121,7 @@ export default function Home() { -