diff --git a/app/plant-page/my-garden/[userPlantId]/page.tsx b/app/plant-page/my-garden/[userPlantId]/page.tsx index e26c845..d02f676 100644 --- a/app/plant-page/my-garden/[userPlantId]/page.tsx +++ b/app/plant-page/my-garden/[userPlantId]/page.tsx @@ -114,7 +114,6 @@ export default function UserPlantPage() {

Planting Timeline

- {/*add SeasonalColorKey here */} [] >([]); - const [selectedUsState, setSelectedUsState] = useState(''); + const [selectedUsState, setSelectedUsState] = + useState | null>(null); const [searchTerm, setSearchTerm] = useState(''); const clearFilters = () => { @@ -50,7 +54,10 @@ export default function SeasonalPlantingGuide() { useEffect(() => { if (profileReady && profileData) { - setSelectedUsState(profileData.us_state); + setSelectedUsState({ + label: toTitleCase(profileData.us_state), + value: profileData.us_state, + }); } }, [profileData, profileReady]); @@ -65,14 +72,17 @@ export default function SeasonalPlantingGuide() { + {/* vertical bar to separate state and other filters */} + + - - + + Clear Filters + {!selectedUsState ? (

Choose Your State

) : ( - + (''); const [selectedPlants, setSelectedPlants] = useState([]); const [ownedPlants, setOwnedPlants] = useState([]); + const [isCardKeyOpen, setIsCardKeyOpen] = useState(false); + const cardKeyRef = useRef(null); + const infoButtonRef = useRef(null); const userState = profileData?.us_state ?? null; const profileAndAuthReady = profileReady && !authLoading; @@ -378,12 +383,52 @@ export default function Page() { const plantPluralityString = selectedPlants.length > 1 ? 'Plants' : 'Plant'; + // close plant card key when clicking outside, even on info button + const handleClickOutside = (event: MouseEvent) => { + if ( + cardKeyRef.current && + !cardKeyRef.current.contains(event.target as Node) && + infoButtonRef.current && + !infoButtonRef.current.contains(event.target as Node) + ) { + setIsCardKeyOpen(false); + } + }; + + // handle clicking outside PlantCardKey to close it if open + useEffect(() => { + if (isCardKeyOpen) { + document.addEventListener('mousedown', handleClickOutside); + } else { + document.removeEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isCardKeyOpen]); + return ( -
+
-

- View Plants -

+ +

+ View Plants +

+
+ setIsCardKeyOpen(!isCardKeyOpen)} + ref={infoButtonRef} + > + + + {isCardKeyOpen && ( +
+ +
+ )} +
+
-// >(({ children, ...props }, ref) => { -// return ( -// -// ); -// }); -// Button.displayName = 'Button'; - -interface SmallRoundedButtonProps { - $primaryColor?: string; - $secondaryColor: string; -} - -export const SmallRoundedButton = styled.button` - font-family: inherit; - padding: 10px 20px; - border-radius: 15px; - box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.05); - border: 0.5px solid ${({ $secondaryColor }) => $secondaryColor}; - background-color: ${({ $primaryColor }) => - $primaryColor ? $primaryColor : 'white'}; - color: ${({ $primaryColor, $secondaryColor }) => - $primaryColor ? 'white' : $secondaryColor}; - font-size: 16px; - cursor: pointer; - transition: - background-color 0.3s ease, - border-color 0.3s ease; - - &:hover { - background-color: ${({ $primaryColor, $secondaryColor }) => - $primaryColor ? $primaryColor : $secondaryColor}; - color: ${({ $primaryColor, $secondaryColor }) => - $primaryColor ? $secondaryColor : 'white'}; - border-color: ${({ $secondaryColor }) => $secondaryColor}; - } -`; diff --git a/components/Buttons.tsx b/components/Buttons.tsx index 46f37fc..d13ecbb 100644 --- a/components/Buttons.tsx +++ b/components/Buttons.tsx @@ -78,7 +78,8 @@ export const SmallButton = styled(P3).attrs({ as: 'button' })` ${ButtonStyles} // Unique to Small Button border-radius: 20px; - min-width: 60px; height: 24px; - padding: 4px 10px; + min-width: 60px; + flex-shrink: 0; // to prevent Clear Filters from collapsing on overflow + padding: 4px 8px; `; diff --git a/components/FilterDropdownMultiple/index.tsx b/components/FilterDropdownMultiple/index.tsx index e0077c3..38acadb 100644 --- a/components/FilterDropdownMultiple/index.tsx +++ b/components/FilterDropdownMultiple/index.tsx @@ -1,6 +1,14 @@ import React from 'react'; +import Select, { + components, + GroupBase, + MultiValue, + MultiValueProps, + OptionProps, +} from 'react-select'; +import { P3 } from '@/styles/text'; import { DropdownOption } from '@/types/schema'; -import { StyledMultiSelect } from './styles'; +import { customSelectStyles } from './styles'; interface FilterDropdownProps { value: DropdownOption[]; @@ -17,16 +25,81 @@ export default function FilterDropdownMultiple({ placeholder, disabled = false, }: FilterDropdownProps) { + const handleChange = (selectedOptions: MultiValue>) => { + setStateAction(selectedOptions as DropdownOption[]); + }; + + // overrides the default MultiValue to display custom text + // displays first selected value followed by + n if more than 1 selected + // CustomMultiValue appears for each selected option, so if more than 1 is selected, + // the rest of the selected options are not shown, instead the + n is shown as part of the first option + const CustomMultiValue = ({ + ...props + }: MultiValueProps< + DropdownOption, + true, + GroupBase> + >) => { + const { selectProps, data, index } = props; + if (Array.isArray(selectProps.value)) { + const isFirst = index === 0; + // find number of remaining selected options + const additionalCount = selectProps.value.length - 1; + + return ( + + {/* display label of first selected option */} + {isFirst ? ( + <> + {data.label} + {/* display additional count only if more than one option is selected*/} + {additionalCount > 0 && ` +${additionalCount}`} + + ) : // don't display anything if not the first selected option + null} + + ); + } + + // nothing is selected yet + return null; + }; + + // overrides the default Options to display a checkbox that ticks when option selected + const CustomOption = ( + props: OptionProps, true, GroupBase>>, + ) => { + return ( + + null} //no-op + style={{ marginRight: 8 }} // spacing between checkbox and text + /> + {props.label} + + ); + }; + return ( - +
+ (small)} + isSearchable={false} + hideSelectedOptions={false} + menuPosition="fixed" + instanceId="dropdown-single" + /> +
); } diff --git a/components/FilterDropdownSingle/styles.ts b/components/FilterDropdownSingle/styles.ts index 725f68b..2ca3b25 100644 --- a/components/FilterDropdownSingle/styles.ts +++ b/components/FilterDropdownSingle/styles.ts @@ -1,5 +1,7 @@ +import { StylesConfig } from 'react-select'; import styled from 'styled-components'; import COLORS from '@/styles/colors'; +import { DropdownOption } from '@/types/schema'; export const FilterDropdownInput = styled.select<{ $hasValue: boolean }>` border-radius: 60px; @@ -13,3 +15,70 @@ export const FilterDropdownInput = styled.select<{ $hasValue: boolean }>` background-color: ${COLORS.lightgray}; } `; + +// custom styles for react-select component +// Option type is DropdownOption +export const customSelectStyles = ( + $isSmall: boolean, +): StylesConfig, true> => ({ + // container + control: (baseStyles, state) => ({ + ...baseStyles, + borderRadius: '56px', + height: '30px', + border: `0.5px solid ${state.hasValue ? COLORS.shrub : COLORS.midgray}`, + backgroundColor: state.isDisabled + ? COLORS.lightgray + : state.hasValue + ? COLORS.shrub + : '#fff', + padding: '8px 14px', + minWidth: $isSmall ? '93px' : '150px', + width: 'max-content', + }), + // placeholder text + placeholder: baseStyles => ({ + ...baseStyles, + padding: '0px', + margin: 'auto', + // style as a P3 with fontWeight 400 + color: COLORS.midgray, + fontSize: '0.75rem', + fontWeight: 400, + }), + // hide vertical bar between arrow and text + indicatorSeparator: baseStyles => ({ + ...baseStyles, + display: 'none', + }), + // 'x' to clear selected option(s) + clearIndicator: baseStyles => ({ + ...baseStyles, + padding: '0px', + }), + // dropdown arrow + dropdownIndicator: (baseStyles, state) => ({ + ...baseStyles, + padding: '0px', + marginLeft: '-4px', // move the dropdown indicator to the left, cant override text styles + color: state.hasValue ? '#fff' : COLORS.midgray, + }), + // selected option display text + singleValue: (baseStyles, state) => ({ + ...baseStyles, + border: '0px', + padding: '0px', + margin: '0px', + paddingLeft: '0px', + // style as a P3 with fontWeight 400 + color: state.hasValue ? `#fff` : `${COLORS.black}`, // replace with `#fff`? + fontSize: '0.75rem', + fontWeight: 400, + }), + option: baseStyles => ({ + ...baseStyles, + // style as a P3 with fontWeight 400 + fontSize: '0.75rem', + fontWeight: 400, + }), +}); diff --git a/components/Header/index.tsx b/components/Header/index.tsx index 57b9073..6ccc6a2 100644 --- a/components/Header/index.tsx +++ b/components/Header/index.tsx @@ -1,7 +1,9 @@ import React from 'react'; import Link from 'next/link'; import CONFIG from '@/lib/configs'; +import COLORS from '@/styles/colors'; import { Flex } from '@/styles/containers'; +import { P3 } from '@/styles/text'; import { useAuth } from '@/utils/AuthProvider'; import { useProfile } from '@/utils/ProfileProvider'; import Icon from '../Icon'; @@ -32,14 +34,22 @@ export default function Header({ toggleNavColumn }: HeaderProps) { } // Not onboarded - return Complete Onboarding; + return ( + + Complete Onboarding + + ); } // Not logged-in user return ( - Login - Sign Up + + Login + + + Sign Up + ); }; diff --git a/components/MonthHeader/index.tsx b/components/MonthHeader/index.tsx deleted file mode 100644 index 566fc20..0000000 --- a/components/MonthHeader/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { P3 } from '@/styles/text'; -import { MonthsContainer } from './styles'; - -const months = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', -]; - -export default function MonthHeader() { - return ( - - {months.map((month, index) => ( - {month} - ))} - - ); -} diff --git a/components/MonthHeader/styles.ts b/components/MonthHeader/styles.ts deleted file mode 100644 index d89cb7c..0000000 --- a/components/MonthHeader/styles.ts +++ /dev/null @@ -1,9 +0,0 @@ -import styled from 'styled-components'; - -export const MonthsContainer = styled.div` - display: grid; - grid-template-columns: repeat(12, 1fr); - /* gap: 0.75rem; */ - width: 100%; - justify-items: center; -`; diff --git a/components/PlantCalendarList/index.tsx b/components/PlantCalendarList/index.tsx index 1394794..2a19859 100644 --- a/components/PlantCalendarList/index.tsx +++ b/components/PlantCalendarList/index.tsx @@ -14,7 +14,6 @@ import { checkSearchTerm, checkUsState, } from '@/utils/helpers'; -import MonthHeader from '../MonthHeader'; import PlantCalendarRow from '../PlantCalendarRow'; import * as Styles from './styles'; @@ -22,10 +21,35 @@ interface PlantListProps { harvestSeasonFilterValue: DropdownOption[]; plantingTypeFilterValue: DropdownOption[]; growingSeasonFilterValue: DropdownOption[]; - usStateFilterValue: string; + usStateFilterValue: DropdownOption | null; searchTerm: string; } +const months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', +]; + +export const MonthHeader = () => { + return ( + + {months.map((month, index) => ( + {month} + ))} + + ); +}; + export const PlantCalendarList = ({ harvestSeasonFilterValue, plantingTypeFilterValue, @@ -64,36 +88,43 @@ export const PlantCalendarList = ({ ]); return ( - - - - - - - - - - - {filteredPlantList.map(plant => ( - - - {plant.plant_name} - + + + {/* set widths of each columns*/} + + + + + + + - + - ))} - - + + + {filteredPlantList.map(plant => ( + + + {plant.plant_name} + + + + + + ))} + + + ); }; diff --git a/components/PlantCalendarList/styles.ts b/components/PlantCalendarList/styles.ts index 718fc9b..1d4d6e8 100644 --- a/components/PlantCalendarList/styles.ts +++ b/components/PlantCalendarList/styles.ts @@ -1,6 +1,12 @@ import styled from 'styled-components'; import { P3 } from '@/styles/text'; +// Container for the table to handle overflow +export const TableContainer = styled.div` + width: 100%; + overflow-x: auto; // Allow horizontal scrolling +`; + // Styled Table export const StyledTable = styled.table` width: 100%; @@ -22,7 +28,17 @@ export const StickyTd = styled(P3).attrs({ // Scrollable container for PlantCalendarRow export const ScrollableTd = styled.td` - overflow-x: scroll; + // overflow-x: scroll; + width: inherit; padding-bottom: 8px; // maybe replace with 4px above and below to center padding? `; + +// month header at the top of PlantCalendarList +export const MonthsContainer = styled.div` + display: grid; + grid-template-columns: repeat(12, 1fr); + /* gap: 0.75rem; */ + width: 100%; + justify-items: center; +`; diff --git a/components/PlantCalendarRow/index.tsx b/components/PlantCalendarRow/index.tsx index 23d2988..ab09614 100644 --- a/components/PlantCalendarRow/index.tsx +++ b/components/PlantCalendarRow/index.tsx @@ -1,7 +1,7 @@ import React, { memo, useMemo } from 'react'; import COLORS from '@/styles/colors'; import { fillCalendarGridArrayRowWithColor } from '@/utils/helpers'; -import MonthHeader from '../MonthHeader'; +import { MonthHeader } from '../PlantCalendarList'; import SeasonColorKey from '../SeasonColorKey'; import { CalendarCell, CalendarGrid } from './styles'; diff --git a/components/PlantCard/index.tsx b/components/PlantCard/index.tsx index 28bb290..72fdc94 100644 --- a/components/PlantCard/index.tsx +++ b/components/PlantCard/index.tsx @@ -1,7 +1,7 @@ import React, { memo } from 'react'; import { P1 } from '@/styles/text'; import { Plant } from '@/types/schema'; -import { mapMonthToSeason, useTitleCase } from '@/utils/helpers'; +import { mapMonthToSeason, toTitleCase } from '@/utils/helpers'; import DifficultyLevelBar from '../DifficultyLevelBar'; import Icon from '../Icon'; import { @@ -48,15 +48,13 @@ const PlantCard = memo(function PlantCard({ - {useTitleCase( - mapMonthToSeason(plant.outdoors_start) || 'Unknown', - )} + {toTitleCase(mapMonthToSeason(plant.outdoors_start) || 'Unknown')} - {useTitleCase(plant.harvest_season)} + {toTitleCase(plant.harvest_season)} diff --git a/components/PlantCardKey/index.tsx b/components/PlantCardKey/index.tsx new file mode 100644 index 0000000..0a955f2 --- /dev/null +++ b/components/PlantCardKey/index.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { IconType } from '@/lib/icons'; +import COLORS from '@/styles/colors'; +import { Flex } from '@/styles/containers'; +import { P3 } from '@/styles/text'; +import { DifficultyLevelEnum } from '@/types/schema'; +import { toTitleCase } from '@/utils/helpers'; +import DifficultyLevelBar from '../DifficultyLevelBar'; +import Icon from '../Icon'; +import { + DifficultyLevelsContainer, + HorizontalLine, + IconKeyContainer, + PlantCardKeyContainer, + Title, +} from './styles'; + +const DifficultyBarAndLabel = ({ + difficultyLevel, +}: { + difficultyLevel: DifficultyLevelEnum; +}) => { + return ( + + + {toTitleCase(difficultyLevel)} + + ); +}; + +const IconAndLabel = ({ + iconName, + label, +}: { + iconName: IconType; + label: string; +}) => { + return ( + + + {label} + + ); +}; + +export default function PlantCardKey() { + return ( + + + <P3 $color="white" $fontWeight={400}> + Plant Card Key + </P3> + + + + Planting Difficulty Level + + + + + + + + + + Icon Key + + + + + + + + + ); +} diff --git a/components/PlantCardKey/styles.ts b/components/PlantCardKey/styles.ts new file mode 100644 index 0000000..be4d428 --- /dev/null +++ b/components/PlantCardKey/styles.ts @@ -0,0 +1,65 @@ +import styled from 'styled-components'; +import COLORS from '@/styles/colors'; + +export const PlantCardKeyContainer = styled.div` + display: flex; + flex-direction: column; + border-radius: 12px; + box-shadow: 2px 0px 8px 0px rgba(0, 0, 0, 0.1); + background-color: white; + border: none; + margin-top: 16px; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); // center PlantCardKey with button above it + z-index: 1000; + min-width: 168px; + min-height: 200px; + width: max-content; + + // creates triangle pointer above plant card key + &::before { + content: ''; + width: 0; + height: 0; + left: 50%; + top: -10px; + transform: translateX(-50%); + position: absolute; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-bottom: 10px solid ${COLORS.shrub}; + } +`; + +export const Title = styled.div` + display: flex; + justify-content: center; + align-items: center; + background-color: ${COLORS.shrub}; + border-radius: 12px 12px 0px 0px; + color: white; + padding: 8px 0px; + border: none; +`; + +export const DifficultyLevelsContainer = styled.div` + display: flex; + flex-direction: row; + gap: 20px; + justify-content: space-between; +`; + +export const HorizontalLine = styled.div` + margin-top: 4px; + width: 100%; + height: 1px; + background-color: ${COLORS.lightgray}; +`; + +export const IconKeyContainer = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; diff --git a/components/SearchBar/index.tsx b/components/SearchBar/index.tsx index 69c46c4..a43b3b4 100644 --- a/components/SearchBar/index.tsx +++ b/components/SearchBar/index.tsx @@ -17,8 +17,6 @@ export default function SearchBar({ setSearchTerm(e.target.value)} diff --git a/components/SearchBar/styles.ts b/components/SearchBar/styles.ts index 67528a3..cb75f33 100644 --- a/components/SearchBar/styles.ts +++ b/components/SearchBar/styles.ts @@ -19,11 +19,12 @@ export const IconWrapper = styled.div` pointer-events: none; /* Prevent the icon from blocking input clicks */ `; -export const SearchBarInput = styled(P3)` +export const SearchBarInput = styled(P3).attrs({ as: 'input', type: 'search' })` padding: 8px 8px 8px 32px; border: none; border-radius: 16px; background-color: #f7f7f7; width: 100%; color: ${COLORS.midgray}; + font-family: inherit; `; diff --git a/components/YourPlantDetails/index.tsx b/components/YourPlantDetails/index.tsx index 53f45f9..3254f34 100644 --- a/components/YourPlantDetails/index.tsx +++ b/components/YourPlantDetails/index.tsx @@ -5,7 +5,7 @@ import COLORS from '@/styles/colors'; import { Flex } from '@/styles/containers'; import { P1, P3 } from '@/styles/text'; import { PlantingTypeEnum } from '@/types/schema'; -import { formatTimestamp, useTitleCase } from '@/utils/helpers'; +import { formatTimestamp, toTitleCase } from '@/utils/helpers'; import Icon from '../Icon'; import { Container, Header } from './style'; @@ -39,7 +39,7 @@ export default function YourPlantDetails({ {DetailRow('calendar', `Date Planted: ${formatTimestamp(datePlanted)}`)} - {DetailRow('plantHand', `Planting Type: ${useTitleCase(plantingType)}`)} + {DetailRow('plantHand', `Planting Type: ${toTitleCase(plantingType)}`)} {recentHarvestDate && DetailRow( 'plant', diff --git a/package.json b/package.json index 416419e..5333343 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "next": "^15.0.4", "react": "^18", "react-dom": "^18", - "react-multi-select-component": "^4.3.4", + "react-select": "^5.8.3", "styled-components": "^6.1.13", "supabase": "^1.200.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce04748..51a755f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,9 @@ importers: react-dom: specifier: ^18 version: 18.3.1(react@18.3.1) - react-multi-select-component: - specifier: ^4.3.4 - version: 4.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: + specifier: ^5.8.3 + version: 5.8.3(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) styled-components: specifier: ^6.1.13 version: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -80,6 +80,10 @@ packages: resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} @@ -93,6 +97,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.25.9': resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} @@ -108,15 +116,56 @@ packages: '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.13.5': + resolution: {integrity: sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@emotion/is-prop-valid@1.2.2': resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==} '@emotion/memoize@0.8.1': resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.13.5': + resolution: {integrity: sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + '@emotion/unitless@0.8.1': resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -135,6 +184,15 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@floating-ui/core@1.6.8': + resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} + + '@floating-ui/dom@1.6.12': + resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} + + '@floating-ui/utils@0.2.8': + resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + '@humanwhocodes/config-array@0.13.0': resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} @@ -405,6 +463,9 @@ packages: '@types/node@20.17.6': resolution: {integrity: sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/phoenix@1.6.5': resolution: {integrity: sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==} @@ -414,6 +475,9 @@ packages: '@types/react-dom@18.3.1': resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} + '@types/react-transition-group@4.4.11': + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} @@ -578,6 +642,10 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -645,6 +713,13 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + cross-spawn@7.0.5: resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} engines: {node: '>= 8'} @@ -722,6 +797,9 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} @@ -742,6 +820,9 @@ packages: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-abstract@1.23.5: resolution: {integrity: sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==} engines: {node: '>= 0.4'} @@ -926,6 +1007,9 @@ packages: find-parent-dir@0.3.1: resolution: {integrity: sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A==} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1048,6 +1132,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + https-proxy-agent@7.0.5: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} @@ -1088,6 +1175,9 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -1231,6 +1321,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1259,6 +1352,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1273,6 +1369,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1421,6 +1520,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1500,11 +1603,17 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-multi-select-component@4.3.4: - resolution: {integrity: sha512-Ui/bzCbROF4WfKq3OKWyQJHmy/bd1mW7CQM+L83TfiltuVvHElhKEyPM3JzO9urIcWplBUKv+kyxqmEnd9jPcA==} + react-select@5.8.3: + resolution: {integrity: sha512-lVswnIq8/iTj1db7XCG74M/3fbGB6ZaluCzvwPGT5ZOjCdL/k0CLWhEK0vCBLuU5bHTEf6Gj8jtSvi+3v+tO1w==} peerDependencies: - react: ^16 || ^17 || ^18 - react-dom: ^16 || ^17 || ^18 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} @@ -1518,6 +1627,9 @@ packages: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regexp.prototype.flags@1.5.3: resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} engines: {node: '>= 0.4'} @@ -1618,6 +1730,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -1692,6 +1808,9 @@ packages: babel-plugin-macros: optional: true + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + stylis@4.3.2: resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} @@ -1779,6 +1898,15 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-isomorphic-layout-effect@1.1.2: + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -1844,6 +1972,10 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yarnhook@0.6.2: resolution: {integrity: sha512-+vrULrVsgYbLd0ie1Ba087xYK4rzB6m2TQNgVen3pXZsm/FRl6GeYzsXttJ4Q9I/MmcK142wMaSFUPlBFASYHQ==} hasBin: true @@ -1868,6 +2000,13 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-string-parser@7.25.9': {} '@babel/helper-validator-identifier@7.25.9': {} @@ -1876,6 +2015,10 @@ snapshots: dependencies: '@babel/types': 7.26.0 + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.25.9': dependencies: '@babel/code-frame': 7.26.2 @@ -1904,14 +2047,78 @@ snapshots: tslib: 2.8.1 optional: true + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.25.9 + '@babel/runtime': 7.26.0 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.13.5': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + '@emotion/is-prop-valid@1.2.2': dependencies: '@emotion/memoize': 0.8.1 '@emotion/memoize@0.8.1': {} + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.13.5(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.13.5 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/unitless@0.10.0': {} + '@emotion/unitless@0.8.1': {} + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -1935,6 +2142,17 @@ snapshots: '@eslint/js@8.57.1': {} + '@floating-ui/core@1.6.8': + dependencies: + '@floating-ui/utils': 0.2.8 + + '@floating-ui/dom@1.6.12': + dependencies: + '@floating-ui/core': 1.6.8 + '@floating-ui/utils': 0.2.8 + + '@floating-ui/utils@0.2.8': {} + '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -2169,6 +2387,8 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/parse-json@4.0.2': {} + '@types/phoenix@1.6.5': {} '@types/prop-types@15.7.13': {} @@ -2177,6 +2397,10 @@ snapshots: dependencies: '@types/react': 18.3.12 + '@types/react-transition-group@4.4.11': + dependencies: + '@types/react': 18.3.12 + '@types/react@18.3.12': dependencies: '@types/prop-types': 15.7.13 @@ -2388,6 +2612,12 @@ snapshots: axobject-query@4.1.0: {} + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.26.0 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + balanced-match@1.0.2: {} bin-links@5.0.0: @@ -2460,6 +2690,16 @@ snapshots: concat-map@0.0.1: {} + convert-source-map@1.9.0: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + cross-spawn@7.0.5: dependencies: path-key: 3.1.1 @@ -2535,6 +2775,11 @@ snapshots: dependencies: esutils: 2.0.3 + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.26.0 + csstype: 3.1.3 + dotenv@16.4.5: {} eastasianwidth@0.2.0: {} @@ -2552,6 +2797,10 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.1 + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + es-abstract@1.23.5: dependencies: array-buffer-byte-length: 1.0.1 @@ -2685,7 +2934,7 @@ snapshots: debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 @@ -2698,7 +2947,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -2720,7 +2969,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -2896,6 +3145,8 @@ snapshots: find-parent-dir@0.3.1: {} + find-root@1.1.0: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3039,6 +3290,10 @@ snapshots: dependencies: function-bind: 1.1.2 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 @@ -3077,6 +3332,8 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-arrayish@0.2.1: {} + is-arrayish@0.3.2: optional: true @@ -3209,6 +3466,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -3239,6 +3498,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -3251,6 +3512,8 @@ snapshots: lru-cache@10.4.3: {} + memoize-one@6.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -3399,6 +3662,13 @@ snapshots: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -3463,8 +3733,29 @@ snapshots: react-is@16.13.1: {} - react-multi-select-component@4.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-select@5.8.3(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/cache': 11.13.5 + '@emotion/react': 11.13.5(@types/react@18.3.12)(react@18.3.1) + '@floating-ui/dom': 1.6.12 + '@types/react-transition-group': 4.4.11 + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.12)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - supports-color + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: + '@babel/runtime': 7.26.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -3484,6 +3775,8 @@ snapshots: globalthis: 1.0.4 which-builtin-type: 1.1.4 + regenerator-runtime@0.14.1: {} + regexp.prototype.flags@1.5.3: dependencies: call-bind: 1.0.7 @@ -3613,6 +3906,8 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.5.7: {} + streamsearch@1.1.0: {} string-width@4.2.3: @@ -3705,6 +4000,8 @@ snapshots: client-only: 0.0.1 react: 18.3.1 + stylis@4.2.0: {} + stylis@4.3.2: {} supabase@1.219.2: @@ -3809,6 +4106,12 @@ snapshots: dependencies: punycode: 2.3.1 + use-isomorphic-layout-effect@1.1.2(@types/react@18.3.12)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + web-streams-polyfill@3.3.3: {} webidl-conversions@3.0.1: {} @@ -3885,6 +4188,8 @@ snapshots: yallist@5.0.0: {} + yaml@1.10.2: {} + yarnhook@0.6.2: dependencies: execa: 4.1.0 diff --git a/utils/helpers.ts b/utils/helpers.ts index b7203a2..cefc898 100644 --- a/utils/helpers.ts +++ b/utils/helpers.ts @@ -81,28 +81,17 @@ export function checkGrowingSeason( // Handle late/early month logic // Set late/early month to just the month using processPlantMonth - const indoorsStart = - plant.indoors_start && processPlantMonth(plant.indoors_start); - const indoorsEnd = plant.indoors_end && processPlantMonth(plant.indoors_end); const outdoorsStart = plant.outdoors_start && processPlantMonth(plant.outdoors_start); const outdoorsEnd = plant.outdoors_end && processPlantMonth(plant.outdoors_end); - // Checks if either indoor_start to indoor_end or outdoor_start to outdoor_end - // is within the valid range of months + // Checks if outdoor_start to outdoor_end is within the valid range of months // 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!, - ) + return isInRange( + monthToIndex.get(outdoorsStart!)!, + monthToIndex.get(outdoorsEnd!)!, + validIndexes!, ); } @@ -223,13 +212,21 @@ export function checkDifficulty( return false; } -export function checkUsState(usStateFilterValue: string, plant: Plant) { +export function checkUsState( + usStateFilterValue: DropdownOption | null, + plant: Plant, +) { // Automatically returns true if no selected usState + if (!usStateFilterValue) { + return true; + } + // Check if plant's us_state matches usStateFilterValue - return usStateFilterValue === '' || plant.us_state === usStateFilterValue; + const selectedState = usStateFilterValue.value; + return plant.us_state === selectedState; } -export function useTitleCase(text: string) { +export function toTitleCase(text: string) { return text.charAt(0) + text.slice(1).toLowerCase(); }