Skip to content

Commit

Permalink
[feat] Implement Plant Seasonality Filters (#17)
Browse files Browse the repository at this point in the history
Co-authored-by: Catherine Tan <[email protected]>
Co-authored-by: rachaelch3n <[email protected]>
Co-authored-by: Catherine Tan <[email protected]>
Co-authored-by: Sashank Balusu <[email protected]>
  • Loading branch information
5 people authored Oct 26, 2024
1 parent f5c5120 commit 0d236b9
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 34 deletions.
14 changes: 14 additions & 0 deletions api/supabase/queries/plantSeasonality.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Plant } from '@/types/schema';
import supabase from '../createClient';

export async function getPlantSeasonality(
input_state: string,
): Promise<Plant[]> {
const { data, error } = await supabase
.from('plants')
.select('*')
.eq('us_state', input_state);
if (error)
throw new Error(`Error fetching plant seasonality: ${error.message}`);
return data;
}
56 changes: 32 additions & 24 deletions app/seasonal-planting-guide/page.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,65 @@
'use client';

import React from 'react';
// import FilterDropdown from '@/components/FilterDropdown';
import React, { useState } from 'react';
import FilterDropdown from '@/components/FilterDropdown';
import { PlantList } from '@/components/PlantList';

// interface SeasonalPlantingGuideProps {}

const SeasonalPlantingGuide = () => {
// hide this for now, will be used in the next PR for dropdown filters
const growingSeasonOptions = ['Spring', 'Summer', 'Fall', 'Winter'];
const harvestSeasonOptions = ['Spring', 'Summer', 'Fall', 'Winter'];
const plantingTypeOptions = [
'Start Seeds Indoors',
'Start Seeds Outdoors',
'Plant Seedlings/Transplant Outdoors',
];

// const growingSeasonOptions = ['Spring', 'Summer', 'Fall', 'Winter'];
// const harvestSeasonOptions = ['Spring', 'Summer', 'Fall', 'Winter'];
// const plantingTypeOptions = [
// 'Start Seeds Indoors',
// 'Start Seeds Outdoors',
// 'Plant Seedlings/Transplant Outdoors',
// ];
const [selectedGrowingSeason, setSelectedGrowingSeason] =
useState<string>('');
const [selectedHarvestSeason, setSelectedHarvestSeason] =
useState<string>('');
const [selectedPlantingType, setSelectedPlantingType] = useState<string>('');

// const [growingSeason, setGrowingSeason] = useState<string>('');
// const [harvestSeason, setHarvestSeason] = useState<string>('');
// const [plantingType, setPlantingType] = useState<string>('');
const clearFilters = () => {
setSelectedGrowingSeason('');
setSelectedHarvestSeason('');
setSelectedPlantingType('');
};

return (
//hide filter dropdowns for now, will be done in another PR
<div>
{/* <FilterDropdown
<FilterDropdown
name="growingSeason"
id="growingSeason"
setStateAction={setGrowingSeason}
value={selectedGrowingSeason}
setStateAction={setSelectedGrowingSeason}
options={growingSeasonOptions}
placeholder="Growing Season"
/>

<FilterDropdown
name="harvestSeason"
id="harvestSeason"
setStateAction={setHarvestSeason}
value={selectedHarvestSeason}
setStateAction={setSelectedHarvestSeason}
options={harvestSeasonOptions}
placeholder="Harvest Season"
/>

<FilterDropdown
name="plantingType"
id="plantingType"
setStateAction={setPlantingType}
value={selectedPlantingType}
setStateAction={setSelectedPlantingType}
options={plantingTypeOptions}
placeholder="Planting Type"
/> */}
/>

<button onClick={clearFilters}>Clear filters</button>

<PlantList
// growing_season={growingSeason}
// harvest_season={harvestSeason}
// planting_type={plantingType}
growingSeasonFilterValue={selectedGrowingSeason}
harvestSeasonFilterValue={selectedHarvestSeason}
plantingTypeFilterValue={selectedPlantingType}
/>
</div>
);
Expand Down
51 changes: 51 additions & 0 deletions components/FilterDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState } from 'react';

interface FilterDropdownProps {
name: string;
id: string;
value: string;
setStateAction: React.Dispatch<React.SetStateAction<string>>;
options: string[];
placeholder: string;
}

export default function FilterDropdown({
name,
id,
value,
setStateAction,
options,
placeholder,
}: FilterDropdownProps) {
const [isOpen, setIsOpen] = useState(false);

const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setStateAction(event.target.value);
setIsOpen(false);
};

const handleToggle = () => {
setIsOpen(!isOpen);
};

return (
<select
name={name}
id={id}
onChange={handleChange}
onClick={handleToggle}
onBlur={() => setIsOpen(false)}
value={value}
>
{/*Default placeholder text*/}
<option value="" disabled hidden>
{placeholder}
</option>
{options.map((option, index) => (
<option key={index} value={option}>
{option}
</option>
))}
</select>
);
}
129 changes: 125 additions & 4 deletions components/PlantList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
import React, { useEffect, useState } from 'react';
import { getAllPlants } from '@/api/supabase/queries/plants';
import { Plant } from '@/types/schema';
import { processPlantMonth } from '@/utils/helpers';

export const PlantList = () => {
interface PlantListProps {
harvestSeasonFilterValue: string;
plantingTypeFilterValue: string;
growingSeasonFilterValue: string;
}

export const PlantList = ({
harvestSeasonFilterValue,
plantingTypeFilterValue,
growingSeasonFilterValue,
}: PlantListProps) => {
const [plants, setPlants] = useState<Plant[]>([]);
const growingSeasonToIndex = new Map<string, number[]>([
['Spring', [2, 3, 4]],
['Summer', [5, 6, 7]],
['Fall', [8, 9, 10]],
['Winter', [11, 0, 1]],
]);

const monthToIndex = new Map<string, number>([
['JANUARY', 0],
['FEBRUARY', 1],
['MARCH', 2],
['APRIL', 3],
['MAY', 4],
['JUNE', 5],
['JULY', 6],
['AUGUST', 7],
['SEPTEMBER', 8],
['OCTOBER', 9],
['NOVEMBER', 10],
['DECEMBER', 11],
]);

useEffect(() => {
const fetchPlantSeasonality = async () => {
// gets plants in Tennessee by default
const plantList = await getAllPlants();
const us_state = 'TENNESSEE';
const filteredPlantList = plantList.filter(
Expand All @@ -18,11 +51,99 @@ export const PlantList = () => {
fetchPlantSeasonality();
}, []);

// Check if growingSeason matches the plant's growing season
const checkGrowingSeason = (plant: Plant) => {
// Automatically returns true if selected growing season is ''
if (!growingSeasonFilterValue) {
return true;
}

// list of valid indexes for the growing season
// indexes are the months of the year
const validIndexes = growingSeasonToIndex.get(growingSeasonFilterValue);

const isInRange = (start: number, end: number, validIndexes: number[]) => {
// Checks if the start and end months are within the valid range
if (start <= end) {
return validIndexes.some(index => index >= start && index <= end);
} else {
// Handle wrap-around case (e.g. NOVEMBER to FEBRUARY)
return validIndexes.some(index => index >= start || index <= end);
}
};

// Handle late/early month logic
// Set late/early month to just the month using processPlantMonth
const indoorsStart = processPlantMonth(plant.indoors_start);
const indoorsEnd = processPlantMonth(plant.indoors_end);
const outdoorsStart = processPlantMonth(plant.outdoors_start);
const outdoorsEnd = processPlantMonth(plant.outdoors_end);

// Checks if either indoor_start to indoor_end or outdoor_start to outdoor_end
// is within the valid range
// exclamation marks to assert values are not undefined
return (
isInRange(
monthToIndex.get(indoorsStart)!,
monthToIndex.get(indoorsEnd)!,
validIndexes!,
) ||
isInRange(
monthToIndex.get(outdoorsStart)!,
monthToIndex.get(outdoorsEnd)!,
validIndexes!,
)
);
};

// Checks if harvestSeason matches the plant's harvest_season
const checkHarvestSeason = (plant: Plant) => {
// Automatically returns true if selected harvestSeason is ''
return (
!harvestSeasonFilterValue ||
plant.harvest_season === harvestSeasonFilterValue.toLocaleUpperCase()
);
};

// Checks if plantingType matches the plant's planting type
const checkPlantingType = (plant: Plant) => {
// Automatically returns true if selected plantingType is ''
if (!plantingTypeFilterValue) {
return true;
}

// Checking if corresponding start field in table is not null
// according to plantingType selected
if (plantingTypeFilterValue === 'Start Seeds Indoors') {
return plant.indoors_start !== null;
} else if (plantingTypeFilterValue === 'Start Seeds Outdoors') {
return plant.outdoors_start !== null;
} else if (
plantingTypeFilterValue === 'Plant Seedlings/Transplant Outdoors'
) {
return plant.transplant_start !== null;
}
};

const filterPlantList = (plant: Plant) => {
// Filters the plant list based on the selected filters
// Only returns true if plant passes all checks
return (
checkGrowingSeason(plant) &&
checkHarvestSeason(plant) &&
checkPlantingType(plant)
);
};

return (
<div>
{plants.map((plant, key) => (
<div key={key}>{plant.plant_name}</div>
))}
{plants
.filter(filterPlantList)
.sort((a, b) => a.plant_name.localeCompare(b.plant_name))
.map((plant, key) => (
//this should display PlantCalendarRows instead of this temporary div
<div key={key}>{plant.plant_name}</div>
))}
</div>
);
};
12 changes: 6 additions & 6 deletions types/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ export interface Plant {
harvest_season: SeasonEnum;
water_frequency: string;
weeding_frequency: string;
plant_seed_indoors_start: string;
plant_seed_indoors_end: string;
plant_seed_outdoors_start: string;
plant_seed_outdoors_end: string;
plant_transplant_start: string;
plant_transplant_end: 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: boolean;
Expand Down
15 changes: 15 additions & 0 deletions utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function processPlantMonth(month: string) {
// If field is not null and starts with 'LATE' or 'EARLY,
// get substring after 'LATE_ or 'EARLY_'
if (!month) {
return month;
}

if (month.startsWith('LATE')) {
return month.substring(5);
} else if (month.startsWith('EARLY')) {
return month.substring(6);
} else {
return month;
}
}

0 comments on commit 0d236b9

Please sign in to comment.