diff --git a/.changeset/plenty-coins-smell.md b/.changeset/plenty-coins-smell.md new file mode 100644 index 0000000000..8862e0742a --- /dev/null +++ b/.changeset/plenty-coins-smell.md @@ -0,0 +1,5 @@ +--- +"@medusajs/admin-ui": patch +--- + +fix(admin-ui): Fixes an issue where regions were not scrollable in tax settings view, also adds a IntersectionObserver to ensure that we load all regions as the user scrolls. The current implementation was capped at loading 20 regions. diff --git a/packages/admin-ui/ui/src/domain/settings/taxes/index.tsx b/packages/admin-ui/ui/src/domain/settings/taxes/index.tsx index 484f402c0a..ab2084b6f8 100644 --- a/packages/admin-ui/ui/src/domain/settings/taxes/index.tsx +++ b/packages/admin-ui/ui/src/domain/settings/taxes/index.tsx @@ -1,23 +1,76 @@ +import { Region } from "@medusajs/medusa" import { useAdminRegions } from "medusa-react" -import { useCallback, useEffect, useState } from "react" -import { useNavigate, useParams } from "react-router-dom" +import { useCallback, useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" +import { useNavigate, useParams } from "react-router-dom" import BackButton from "../../../components/atoms/back-button" -import Spinner from "../../../components/atoms/spinner" import GearIcon from "../../../components/fundamentals/icons/gear-icon" import BodyCard from "../../../components/organisms/body-card" import RadioGroup from "../../../components/organisms/radio-group" import TwoSplitPane from "../../../components/templates/two-split-pane" import TaxDetails from "./details" +const LIMIT = 20 + const Taxes = () => { const params = useParams() const regId: string | undefined = params["*"] + const [offset, setOffset] = useState(0) + const observerRef = useRef(null) + const navigate = useNavigate() const { t } = useTranslation() - const { regions, isLoading } = useAdminRegions() + const [regionOpts, setRegionOpts] = useState([]) + + const { regions, count, isLoading } = useAdminRegions({ + offset, + limit: LIMIT, + }) + + useEffect(() => { + if (regions && !isLoading) { + setRegionOpts((prev) => { + const exisitngIds = prev.map((r) => r.id) + const newOpts = regions.filter((r) => !exisitngIds.includes(r.id)) + + return [...prev, ...newOpts] + }) + } + }, [regions, isLoading]) + + useEffect(() => { + const current = observerRef.current + + const observer = new IntersectionObserver( + (entries) => { + // If the ref is in view and there's more data to fetch + if ( + entries[0].isIntersecting && + regionOpts && + regionOpts.length < (count || 0) + ) { + setOffset(regionOpts.length) + } + }, + { + root: null, // relative to the viewport + threshold: 1.0, // trigger when 100% of the ref is visible + } + ) + + if (current) { + observer.observe(current) + } + + // Clean up the observer on component unmount + return () => { + if (current) { + observer.unobserve(current) + } + } + }, [count, regionOpts]) const [selectedRegion, setSelectedRegion] = useState( regId @@ -71,35 +124,40 @@ const Taxes = () => { onClick: () => navigate("/a/settings/regions"), }, ]} + className="no-scrollbar overflow-y-auto" > - {isLoading || !regions ? ( -
- -
- ) : ( +
- {regions.map((r) => { + {regionOpts.map((r, index) => { return ( - c.display_name) - .join(", ")}` +
+ > + c.display_name) + .join(", ")}` + : undefined + } + value={r.id} + key={r.id} + /> +
) })}
- )} +