Compare commits

..

1 Commits

Author SHA1 Message Date
3d248d1197 Strip AI tooling and working docs for dev push 2026-04-23 14:24:48 +10:00
4 changed files with 46 additions and 94 deletions

View File

@@ -19,7 +19,7 @@
"test": "vitest run --passWithNoTests", "test": "vitest run --passWithNoTests",
"test:watch": "vitest", "test:watch": "vitest",
"chromatic": "chromatic --exit-zero-on-changes --build-script-name=build:storybook", "chromatic": "chromatic --exit-zero-on-changes --build-script-name=build:storybook",
"demo:dev": "vite -c vite.demo.config.ts --mode arrangement", "demo:dev": "vite -c vite.demo.config.ts",
"demo:build": "vite build -c vite.demo.config.ts", "demo:build": "vite build -c vite.demo.config.ts",
"demo:publish": "npm run demo:build -- --mode arrangement && ./scripts/deploy-demo.sh arrangement", "demo:publish": "npm run demo:build -- --mode arrangement && ./scripts/deploy-demo.sh arrangement",
"prepare": "husky" "prepare": "husky"

View File

@@ -6,7 +6,6 @@ import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
import StarRoundedIcon from '@mui/icons-material/StarRounded'; import StarRoundedIcon from '@mui/icons-material/StarRounded';
import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined'; import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined';
import type { SxProps, Theme } from '@mui/material/styles'; import type { SxProps, Theme } from '@mui/material/styles';
import { Button } from '../../atoms/Button';
import { IconButton } from '../../atoms/IconButton'; import { IconButton } from '../../atoms/IconButton';
import { Typography } from '../../atoms/Typography'; import { Typography } from '../../atoms/Typography';
import { ProviderCard } from '../ProviderCard'; import { ProviderCard } from '../ProviderCard';
@@ -199,9 +198,6 @@ export const MapProviderDrawer = React.forwardRef<HTMLDivElement, MapProviderDra
px: 2, px: 2,
py: 0.5, py: 0.5,
gap: 1, gap: 1,
bgcolor: 'var(--fa-color-surface-subtle)',
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
}} }}
> >
{cluster && !provider && ( {cluster && !provider && (
@@ -225,27 +221,23 @@ export const MapProviderDrawer = React.forwardRef<HTMLDivElement, MapProviderDra
</IconButton> </IconButton>
</Box> </Box>
{/* Single-provider content — card is display-only; a CTA button {/* Single-provider content — entire card clickable. Card runs
below handles navigation to the provider's packages. */} edge-to-edge with all corners squared; the drawer Paper provides
the top radius. */}
{provider && ( {provider && (
<Box> <ProviderCard
<ProviderCard name={provider.name}
name={provider.name} location={provider.location}
location={provider.location} verified={provider.verified}
verified={provider.verified} imageUrl={provider.imageUrl}
imageUrl={provider.imageUrl} logoUrl={provider.logoUrl}
logoUrl={provider.logoUrl} rating={provider.rating}
rating={provider.rating} reviewCount={provider.reviewCount}
reviewCount={provider.reviewCount} startingPrice={provider.startingPrice}
startingPrice={provider.startingPrice} onClick={() => onSelectProvider(provider.id)}
sx={{ borderRadius: 0, boxShadow: 'none', border: 'none' }} aria-label={`${provider.name}, ${provider.location}. Tap to view packages.`}
/> sx={{ borderRadius: 0, boxShadow: 'none', border: 'none' }}
<Box sx={{ px: 2, pb: 2, pt: 1 }}> />
<Button variant="contained" fullWidth onClick={() => onSelectProvider(provider.id)}>
View Packages
</Button>
</Box>
</Box>
)} )}
{/* Cluster list content — tap a row to drill in */} {/* Cluster list content — tap a row to drill in */}

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { createRoot, type Root } from 'react-dom/client'; import { createRoot, type Root } from 'react-dom/client';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import type { SxProps, Theme } from '@mui/material/styles'; import type { SxProps, Theme } from '@mui/material/styles';
import { ThemeProvider, useTheme } from '@mui/material/styles';
import { import {
APIProvider, APIProvider,
Map as GoogleMap, Map as GoogleMap,
@@ -140,33 +139,20 @@ const MapRefCapture: React.FC<{
const MarkerLayer: React.FC<{ const MarkerLayer: React.FC<{
providers: ProviderData[]; providers: ProviderData[];
hiddenIds: Set<string>; hiddenIds: Set<string>;
theme: Theme;
externalisePopups: boolean;
onPinClick: (id: string) => void; onPinClick: (id: string) => void;
onSelectProvider: (id: string) => void;
onClusterClick: (providers: ProviderData[], position: google.maps.LatLngLiteral) => void; onClusterClick: (providers: ProviderData[], position: google.maps.LatLngLiteral) => void;
}> = ({ }> = ({ providers, hiddenIds, onPinClick, onClusterClick }) => {
providers,
hiddenIds,
theme,
externalisePopups,
onPinClick,
onSelectProvider,
onClusterClick,
}) => {
const map = useMap(); const map = useMap();
const markerLibrary = useMapsLibrary('marker'); const markerLibrary = useMapsLibrary('marker');
// Stash callbacks in a ref so the effect below doesn't re-run (and rebuild // Stash callbacks in a ref so the effect below doesn't re-run (and rebuild
// every marker) when the parent passes fresh arrow-function references. // every marker) when the parent passes fresh arrow-function references.
const onPinClickRef = React.useRef(onPinClick); const onPinClickRef = React.useRef(onPinClick);
const onSelectProviderRef = React.useRef(onSelectProvider);
const onClusterClickRef = React.useRef(onClusterClick); const onClusterClickRef = React.useRef(onClusterClick);
React.useEffect(() => { React.useEffect(() => {
onPinClickRef.current = onPinClick; onPinClickRef.current = onPinClick;
onSelectProviderRef.current = onSelectProvider;
onClusterClickRef.current = onClusterClick; onClusterClickRef.current = onClusterClick;
}, [onPinClick, onSelectProvider, onClusterClick]); }, [onPinClick, onClusterClick]);
React.useEffect(() => { React.useEffect(() => {
if (!map || !markerLibrary) return; if (!map || !markerLibrary) return;
@@ -179,38 +165,20 @@ const MarkerLayer: React.FC<{
.map((p) => { .map((p) => {
const el = document.createElement('div'); const el = document.createElement('div');
const root = createRoot(el); const root = createRoot(el);
// MapPin's own onClick stays for keyboard a11y (Enter/Space via its
if (p.verified) { // onKeyDown). stopPropagation guards against the DOM click bubbling
root.render( // to the Map's onClick and closing the popup the same frame it opens.
<ThemeProvider theme={theme}> root.render(
<MapPopup <MapPin
name={p.name} name={p.name}
imageUrl={p.imageUrl} price={p.startingPrice}
price={p.startingPrice} verified={p.verified}
location={p.location} onClick={(e) => {
rating={p.rating} e.stopPropagation();
verified onPinClickRef.current(p.id);
onClick={() => }}
externalisePopups />,
? onPinClickRef.current(p.id) );
: onSelectProviderRef.current(p.id)
}
/>
</ThemeProvider>,
);
} else {
root.render(
<MapPin
name={p.name}
price={p.startingPrice}
verified={p.verified}
onClick={(e) => {
e.stopPropagation();
onPinClickRef.current(p.id);
}}
/>,
);
}
roots.push(root); roots.push(root);
const marker = new markerLibrary.AdvancedMarkerElement({ const marker = new markerLibrary.AdvancedMarkerElement({
@@ -218,12 +186,13 @@ const MarkerLayer: React.FC<{
content: el, content: el,
gmpClickable: true, gmpClickable: true,
}); });
if (!p.verified) { // Also listen at the Google Maps level + stop the GMaps event so
marker.addListener('click', (event: google.maps.MapMouseEvent) => { // Map's onClick can't fire when a pin is clicked via mouse. Safe to
event.stop(); // fire twice with keyboard — handlePinClick is idempotent.
onPinClickRef.current(p.id); marker.addListener('click', (event: google.maps.MapMouseEvent) => {
}); event.stop();
} onPinClickRef.current(p.id);
});
markerToProvider.set(marker, p); markerToProvider.set(marker, p);
return marker; return marker;
}); });
@@ -350,7 +319,6 @@ export const ProviderMap = React.forwardRef<ProviderMapHandle, ProviderMapProps>
}, },
ref, ref,
) => { ) => {
const muiTheme = useTheme();
const [activeProviderId, setActiveProviderId] = React.useState<string | null>(null); const [activeProviderId, setActiveProviderId] = React.useState<string | null>(null);
const [activeCluster, setActiveCluster] = React.useState<ActiveCluster | null>(null); const [activeCluster, setActiveCluster] = React.useState<ActiveCluster | null>(null);
const [exiting, setExiting] = React.useState(false); const [exiting, setExiting] = React.useState(false);
@@ -386,18 +354,14 @@ export const ProviderMap = React.forwardRef<ProviderMapHandle, ProviderMapProps>
); );
// Pins hidden from the map (because their popup is showing instead). // Pins hidden from the map (because their popup is showing instead).
// Verified providers are excluded — their marker IS the MapPopup.
const hiddenIds = React.useMemo(() => { const hiddenIds = React.useMemo(() => {
const s = new Set<string>(); const s = new Set<string>();
if (effectiveProviderId) { if (effectiveProviderId) s.add(effectiveProviderId);
const p = withCoords.find((prov) => prov.id === effectiveProviderId);
if (p && !p.verified) s.add(effectiveProviderId);
}
if (activeCluster) { if (activeCluster) {
activeCluster.providers.forEach((p) => s.add(p.id)); activeCluster.providers.forEach((p) => s.add(p.id));
} }
return s; return s;
}, [effectiveProviderId, activeCluster, withCoords]); }, [effectiveProviderId, activeCluster]);
const handlePinClick = React.useCallback( const handlePinClick = React.useCallback(
(id: string) => { (id: string) => {
@@ -533,17 +497,13 @@ export const ProviderMap = React.forwardRef<ProviderMapHandle, ProviderMapProps>
<MarkerLayer <MarkerLayer
providers={withCoords} providers={withCoords}
hiddenIds={hiddenIds} hiddenIds={hiddenIds}
theme={muiTheme}
externalisePopups={externalisePopups}
onPinClick={handlePinClick} onPinClick={handlePinClick}
onSelectProvider={onSelectProvider}
onClusterClick={handleClusterClick} onClusterClick={handleClusterClick}
/> />
{/* Click-to-reveal popup for unverified providers. Verified {/* Internal popups — skipped when caller externalises them (e.g.
providers are always rendered as MapPopup inside MarkerLayer, mobile drawer). Active state still flows via onActiveChange. */}
so they don't need this path. */} {!externalisePopups && activeProvider && (
{!externalisePopups && activeProvider && !activeProvider.verified && (
<AdvancedMarker position={activeProvider.coords!} zIndex={1000}> <AdvancedMarker position={activeProvider.coords!} zIndex={1000}>
<MapPopup <MapPopup
name={activeProvider.name} name={activeProvider.name}

View File

@@ -807,7 +807,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 4, gap: 2,
pb: 3, pb: 3,
pt: 2, pt: 2,
px: { xs: 2, md: 3 }, px: { xs: 2, md: 3 },