From 8d463fba10fc4919f55537760574a6ac6a0fc8a7 Mon Sep 17 00:00:00 2001 From: Jeff Bliss Date: Wed, 13 Nov 2024 14:22:29 -0500 Subject: [PATCH] more updates for selfservice --- src/components/CommunityPane.jsx | 131 +++++++++++---------- src/components/DropDownSelector.jsx | 79 ++++++++++--- src/components/Section.jsx | 43 +++++-- src/pages/SelfServicePage.jsx | 175 +++++++++++----------------- src/util/api.js | 10 +- 5 files changed, 236 insertions(+), 202 deletions(-) diff --git a/src/components/CommunityPane.jsx b/src/components/CommunityPane.jsx index 9a12901..9751dad 100644 --- a/src/components/CommunityPane.jsx +++ b/src/components/CommunityPane.jsx @@ -1,98 +1,101 @@ - import { Typography } from "@mui/material"; - import HeaderBox from "./HeaderBox"; import ScoreSection from './ScoreSection'; import Pane from './Pane'; import Section from './Section'; - import { Box, Stack } from '@mui/material'; - import theme from '../theme'; - -const getSectionData = community => [ +const getSectionData = (community, isSelectable, availableOptions, onSelectionChange) => [ { header: 'State', - cards: [ community.state ] + cards: community.state, + availableSelections: isSelectable ? availableOptions?.state || [] : [], }, { header: 'Activities', - cards: community.activities + cards: community.activities, + availableSelections: isSelectable ? availableOptions?.activities || [] : [], }, { header: 'Sectors', - cards: community.sectors + cards: community.sectors, + availableSelections: isSelectable ? availableOptions?.sectors || [] : [], }, { header: 'Hazards', - cards: community.hazards + cards: community.hazards, + availableSelections: isSelectable ? availableOptions?.hazards || [] : [], }, { header: 'Size', - cards: [ community.size ] + cards: community.size, + availableSelections: isSelectable ? availableOptions?.size || [] : [], } -] -.map((section, index) => { +].map((section, index) => { section.type = 'community' - section.key=`section${index}` + section.key = `section${index}` + section.isSelectable = isSelectable + section.onSelectionChange = onSelectionChange return section }); +export default function CommunityPane({ + community, + isSelectable = false, + availableOptions = {}, + onSelectionChange = () => {} + }) { + const sectionData = getSectionData(community, isSelectable, availableOptions, onSelectionChange); -export default function CommunityPane({ community }) { - const sectionData = getSectionData(community); return ( - - - - { community.name } - - { /* filler box to match height of "Matched Practitioners" heading */ } - - - - { - sectionData.map((section, index) => { - return Section(section) - }) - } - + + + {community.name} + + {/* filler box to match height of "Matched Practitioners" heading */} + + + -
Total
-
{ community.totalCategories }
-
-
-
+ {sectionData.map((section, index) => { + return Section(section) + })} + +
Total
+
{community.totalCategories}
+
+ + ) } - diff --git a/src/components/DropDownSelector.jsx b/src/components/DropDownSelector.jsx index fc6c3f2..22fa504 100644 --- a/src/components/DropDownSelector.jsx +++ b/src/components/DropDownSelector.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; +import theme from '../theme'; -const DropDownSelector = (props) => { - const { availableSelections, selections, setSelections, option } = props; +const DropDownSelector = ({ availableSelections, selections, setSelections, option }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const handleAdd = (itemToAdd) => { @@ -17,19 +17,31 @@ const DropDownSelector = (props) => { return (
-
{option}
- - {/* Selected States */} + {/* Selected Items */}
{selections.map((item) => (
- {item} + {item} @@ -37,25 +49,60 @@ const DropDownSelector = (props) => { ))}
- {/* Add State Button & Dropdown */} + {/* Add Button & Dropdown */}
{isDropdownOpen && ( -
+
{availableSelections .filter(item => !selections.includes(item)) .map((item) => ( @@ -67,4 +114,4 @@ const DropDownSelector = (props) => { ); }; -export default DropDownSelector; \ No newline at end of file +export default DropDownSelector; diff --git a/src/components/Section.jsx b/src/components/Section.jsx index a3b43b1..0b27455 100644 --- a/src/components/Section.jsx +++ b/src/components/Section.jsx @@ -1,18 +1,39 @@ - import { Typography, Box, Stack } from "@mui/material" - import Cell from "./Cell" +import DropDownSelector from "./DropDownSelector" +import theme from '../theme' -export default function Section ({ header='', type, cards, key }) { +export default function Section({ header='', type, cards, key, isSelectable, onSelectionChange, availableSelections = [] }) { const cells = cards.map((label, index) => Cell({ label, type, key: `${key}_row${index}` })) + return ( - - - { header } + + {/* Section Header */} + + {header} + + + {/* Selector if isSelectable */} + {isSelectable && ( + + onSelectionChange(header.toLowerCase(), newSelections)} + option={header} + /> + + )} + + {/* Content */} + + {cells} + - - { cells } - - ) -} +} \ No newline at end of file diff --git a/src/pages/SelfServicePage.jsx b/src/pages/SelfServicePage.jsx index 87f3b3b..660a688 100644 --- a/src/pages/SelfServicePage.jsx +++ b/src/pages/SelfServicePage.jsx @@ -1,11 +1,8 @@ // React -import { useState, useEffect, useContext } from 'react'; - -// router -import { useParams } from 'react-router-dom'; +import { useState, useEffect } from 'react'; // API -import { fetchCommunity, fetchPractitionersForCommunity, fetchOptionsFromAirtable } from '../util/api'; +import { fetchOptionsFromAirtable } from '../util/api'; // components import { CssBaseline, Stack, Container, Typography, Box } from '@mui/material'; @@ -24,7 +21,6 @@ import DropDownSelector from "../components/DropDownSelector.jsx"; export default function SelfServicePage() { - const [selectedOptions, setSelectedOptions] = useState({ state: [], activities: [], @@ -41,9 +37,9 @@ export default function SelfServicePage() { size: [], }); - const [ practitioners, setPractitioners ] = useState([]); - const [ poppedPractitioner, setPoppedPractitioner ] = useState(null); - const [ hoverRow, setHoverRow ] = useState(null); + const [practitioners, setPractitioners] = useState([]); + const [poppedPractitioner, setPoppedPractitioner] = useState(null); + const [hoverRow, setHoverRow] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); @@ -54,6 +50,7 @@ export default function SelfServicePage() { sectors: selectedOptions.sectors, hazards: selectedOptions.hazards, size: selectedOptions.size, + totalCategories: Object.values(selectedOptions).reduce((sum, arr) => sum + arr.length, 0) } useEffect(() => { @@ -69,6 +66,13 @@ export default function SelfServicePage() { }); }, []); + const handleSelectionChange = (category, newSelections) => { + setSelectedOptions(prev => ({ + ...prev, + [category]: newSelections + })); + }; + if (error) { return
{error}
; } @@ -77,109 +81,60 @@ export default function SelfServicePage() { return
Loading options...
; } - if (availableOptions) { - - const practitionerPanes = practitioners.map((pract, index) => { - return - }) - - return ( - - - - - - - - setSelectedOptions({ ...selectedOptions, state: selections }) } - option = "State" - /> - setSelectedOptions({ ...selectedOptions, activities: selections }) } - option = "Activity" - /> - setSelectedOptions({ ...selectedOptions, sectors: selections }) } - option = "Sector" - /> - setSelectedOptions({ ...selectedOptions, hazards: selections }) } - option = "Hazard" - /> - setSelectedOptions({ ...selectedOptions, size: selections }) } - option = "Size" - /> - - { /* Practitioners */ } - - - + + + + + + + + {/* Practitioners */} + + + - Matched Practitioners - - - { practitionerPanes } - + >Matched
Practitioners + + + {PractitionerPane} - - - - - ) - } else { - return ( - - ) - } - + + + + + + ); } diff --git a/src/util/api.js b/src/util/api.js index 1eebdce..28996a9 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -14,7 +14,15 @@ const base = Airtable.base(__AIRTABLE_BASE__) const normalizeRec = (rec, fieldMap) => { const result = {} for (const [normKey, airKey] of Object.entries(fieldMap)) { - result[normKey] = rec[airKey] || '' + // For communities, ensure every field is an array + if (fieldMap === communityFieldMap) { + // If the field exists but isn't an array, wrap it + result[normKey] = rec[airKey] ? + (Array.isArray(rec[airKey]) ? rec[airKey] : [rec[airKey]]) : + [] // If field doesn't exist, return empty array + } else { + result[normKey] = rec[airKey] || '' + } } return result }