198 lines
6.4 KiB
JavaScript
198 lines
6.4 KiB
JavaScript
import { useContext, useMemo, useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { StoreContext } from '../../context/StoreContext';
|
|
import '../../styles/components/StoreTabs.css';
|
|
|
|
const DEFAULT_LOCATION_NAME = "Default Location";
|
|
|
|
function getStoreKey(store) {
|
|
return String(store?.household_store_id ?? store?.store_id ?? store?.id ?? "");
|
|
}
|
|
|
|
function getStoreName(store) {
|
|
return store?.name || store?.display_name || "Store";
|
|
}
|
|
|
|
function getLocationName(store) {
|
|
if (!store) return "Location";
|
|
const locationName = store.location_name || "";
|
|
if (!locationName || locationName === DEFAULT_LOCATION_NAME) {
|
|
return "Default";
|
|
}
|
|
return locationName;
|
|
}
|
|
|
|
function chooseStoreLocation(locations) {
|
|
if (!locations || locations.length === 0) return null;
|
|
return (
|
|
locations.find((store) => store.location_name === "Default Location" || store.display_name === store.name) ||
|
|
locations.find((store) => store.is_default) ||
|
|
locations[0]
|
|
);
|
|
}
|
|
|
|
function buildStoreOptions(stores) {
|
|
const grouped = new Map();
|
|
|
|
for (const store of stores) {
|
|
const key = getStoreKey(store);
|
|
if (!grouped.has(key)) {
|
|
grouped.set(key, {
|
|
key,
|
|
name: getStoreName(store),
|
|
locations: [],
|
|
});
|
|
}
|
|
grouped.get(key).locations.push(store);
|
|
}
|
|
|
|
return Array.from(grouped.values()).map((group) => ({
|
|
...group,
|
|
store: chooseStoreLocation(group.locations),
|
|
}));
|
|
}
|
|
|
|
export default function StoreTabs() {
|
|
const { stores, activeStore, setActiveStore, loading } = useContext(StoreContext);
|
|
const navigate = useNavigate();
|
|
const [activePicker, setActivePicker] = useState(null);
|
|
const storeOptions = useMemo(() => buildStoreOptions(stores || []), [stores]);
|
|
const activeStoreKey = getStoreKey(activeStore);
|
|
const selectedStoreOption =
|
|
storeOptions.find((option) => option.key === activeStoreKey) || storeOptions[0];
|
|
const selectedLocations = selectedStoreOption?.locations || [];
|
|
const selectedLocation =
|
|
selectedLocations.find((location) => String(location.id) === String(activeStore?.id)) ||
|
|
selectedStoreOption?.store;
|
|
const canPickStore = storeOptions.length > 1 && !loading;
|
|
const canPickLocation = selectedLocations.length > 1 && !loading;
|
|
|
|
if (!stores || stores.length === 0 || storeOptions.length === 0) {
|
|
return (
|
|
<div className="store-tabs">
|
|
<div className="store-tabs-empty">
|
|
No stores available for this household
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const handleStoreTriggerClick = () => {
|
|
if (!canPickStore) return;
|
|
setActivePicker("store");
|
|
};
|
|
|
|
const handleLocationTriggerClick = () => {
|
|
if (!canPickLocation) return;
|
|
setActivePicker("location");
|
|
};
|
|
|
|
const handleStoreSelect = (store) => {
|
|
setActiveStore(store);
|
|
setActivePicker(null);
|
|
};
|
|
|
|
const handleLocationSelect = (location) => {
|
|
setActiveStore(location);
|
|
setActivePicker(null);
|
|
};
|
|
|
|
const handleManageMap = () => {
|
|
const locationQuery = selectedLocation?.id ? `&locationId=${selectedLocation.id}` : "";
|
|
navigate(`/manage?tab=stores${locationQuery}`);
|
|
};
|
|
|
|
return (
|
|
<div className="store-tabs">
|
|
<div className="store-tabs-container">
|
|
<button
|
|
type="button"
|
|
className={`store-selector-trigger ${canPickStore ? "is-interactive" : ""}`}
|
|
onClick={handleStoreTriggerClick}
|
|
disabled={loading}
|
|
aria-haspopup={canPickStore ? "dialog" : undefined}
|
|
aria-expanded={canPickStore ? activePicker === "store" : undefined}
|
|
aria-label={`Store: ${selectedStoreOption.name}`}
|
|
>
|
|
<span className="store-name">{selectedStoreOption.name}</span>
|
|
{canPickStore ? <span className="store-selector-caret" aria-hidden="true" /> : null}
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
className={`store-selector-trigger location-selector-trigger ${canPickLocation ? "is-interactive" : ""}`}
|
|
onClick={handleLocationTriggerClick}
|
|
disabled={loading}
|
|
aria-haspopup={canPickLocation ? "dialog" : undefined}
|
|
aria-expanded={canPickLocation ? activePicker === "location" : undefined}
|
|
aria-label={`Location: ${getLocationName(selectedLocation)}`}
|
|
>
|
|
<span className="store-name">{getLocationName(selectedLocation)}</span>
|
|
{canPickLocation ? <span className="store-selector-caret" aria-hidden="true" /> : null}
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
className="store-map-button"
|
|
onClick={handleManageMap}
|
|
disabled={loading || !selectedLocation}
|
|
>
|
|
Manage Map
|
|
</button>
|
|
</div>
|
|
|
|
{activePicker === "store" ? (
|
|
<div className="store-selector-modal-overlay" onClick={() => setActivePicker(null)}>
|
|
<div
|
|
className="store-selector-modal"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label="Select store"
|
|
onClick={(event) => event.stopPropagation()}
|
|
>
|
|
<h2>Stores</h2>
|
|
<div className="store-selector-list">
|
|
{storeOptions.map((option) => (
|
|
<button
|
|
key={option.key}
|
|
type="button"
|
|
className={`store-selector-option ${option.key === selectedStoreOption.key ? "active" : ""}`}
|
|
onClick={() => handleStoreSelect(option.store)}
|
|
>
|
|
{option.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
|
|
{activePicker === "location" ? (
|
|
<div className="store-selector-modal-overlay" onClick={() => setActivePicker(null)}>
|
|
<div
|
|
className="store-selector-modal"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label="Select location"
|
|
onClick={(event) => event.stopPropagation()}
|
|
>
|
|
<h2>Locations</h2>
|
|
<div className="store-selector-list">
|
|
{selectedLocations.map((location) => (
|
|
<button
|
|
key={location.id}
|
|
type="button"
|
|
className={`store-selector-option ${String(location.id) === String(selectedLocation?.id) ? "active" : ""}`}
|
|
onClick={() => handleLocationSelect(location)}
|
|
>
|
|
{getLocationName(location)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|