feat(dashboard): Rework route modals (#6459)

**What**
- Reworks how RouteModals are setup.

**Why**
- With the current implementation it was possible to create a race-condition in the logic that handled displaying a prompt if the user tried to close a modal, while a child form was dirty. The race condition would cause a new prompt to spawn each time the user clicked the screen, making it impossible to dismiss the prompt. This only occurred in a few specific cases.

**How**
- Creates two new components: RouteFocusModal and RouteDrawer. The component shares logic for handling their own open/closed state, and now accept a form prop, that allows the modals to keep track of whether their child form is dirty. This change ensures that race conditions cannot occur, and that the prompt always only renders once when needed.
This commit is contained in:
Kasper Fabricius Kristensen
2024-02-22 10:03:40 +01:00
committed by GitHub
parent add861d205
commit 72a17d6cd7
88 changed files with 1750 additions and 1799 deletions

View File

@@ -0,0 +1,56 @@
import { Country } from "@medusajs/medusa"
import { Tooltip } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { PlaceholderCell } from "../../common/placeholder-cell"
type CountriesCellProps = {
countries?: Country[] | null
}
export const CountriesCell = ({ countries }: CountriesCellProps) => {
const { t } = useTranslation()
if (!countries || countries.length === 0) {
return <PlaceholderCell />
}
const displayValue = countries
.slice(0, 2)
.map((c) => c.display_name)
.join(", ")
const additionalCountries = countries.slice(2).map((c) => c.display_name)
return (
<div className="flex size-full items-center gap-x-1">
<span>{displayValue}</span>
{additionalCountries.length > 0 && (
<Tooltip
content={
<ul>
{additionalCountries.map((c) => (
<li key={c}>{c}</li>
))}
</ul>
}
>
<span>
{t("general.plusCountMore", {
count: additionalCountries.length,
})}
</span>
</Tooltip>
)}
</div>
)
}
export const CountriesHeader = () => {
const { t } = useTranslation()
return (
<div className="flex size-full items-center">
<span>{t("fields.countries")}</span>
</div>
)
}

View File

@@ -0,0 +1 @@
export * from "./countries-cell"

View File

@@ -0,0 +1,58 @@
import { FulfillmentProvider } from "@medusajs/medusa"
import { Tooltip } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { PlaceholderCell } from "../../common/placeholder-cell"
type FulfillmentProvidersCellProps = {
fulfillmentProviders?: FulfillmentProvider[] | null
}
export const FulfillmentProvidersCell = ({
fulfillmentProviders,
}: FulfillmentProvidersCellProps) => {
const { t } = useTranslation()
if (!fulfillmentProviders || fulfillmentProviders.length === 0) {
return <PlaceholderCell />
}
const displayValue = fulfillmentProviders
.slice(0, 2)
.map((p) => p.id)
.join(", ")
const additionalProviders = fulfillmentProviders.slice(2).map((c) => c.id)
return (
<div className="flex size-full items-center gap-x-1">
<span>{displayValue}</span>
{additionalProviders.length > 0 && (
<Tooltip
content={
<ul>
{additionalProviders.map((c) => (
<li key={c}>{c}</li>
))}
</ul>
}
>
<span>
{t("general.plusCountMore", {
count: additionalProviders.length,
})}
</span>
</Tooltip>
)}
</div>
)
}
export const FulfillmentProvidersHeader = () => {
const { t } = useTranslation()
return (
<div className="flex size-full items-center">
<span>{t("fields.fulfillmentProviders")}</span>
</div>
)
}

View File

@@ -0,0 +1 @@
export * from "./fulfillment-providers-cell"

View File

@@ -0,0 +1 @@
export * from "./payment-providers-cell"

View File

@@ -0,0 +1,58 @@
import { PaymentProvider } from "@medusajs/medusa"
import { Tooltip } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { PlaceholderCell } from "../../common/placeholder-cell"
type PaymentProvidersCellProps = {
paymentProviders?: PaymentProvider[] | null
}
export const PaymentProvidersCell = ({
paymentProviders,
}: PaymentProvidersCellProps) => {
const { t } = useTranslation()
if (!paymentProviders || paymentProviders.length === 0) {
return <PlaceholderCell />
}
const displayValue = paymentProviders
.slice(0, 2)
.map((p) => p.id)
.join(", ")
const additionalProviders = paymentProviders.slice(2).map((c) => c.id)
return (
<div className="flex size-full items-center gap-x-1">
<span>{displayValue}</span>
{additionalProviders.length > 0 && (
<Tooltip
content={
<ul>
{additionalProviders.map((c) => (
<li key={c}>{c}</li>
))}
</ul>
}
>
<span>
{t("general.plusCountMore", {
count: additionalProviders.length,
})}
</span>
</Tooltip>
)}
</div>
)
}
export const PaymentProvidersHeader = () => {
const { t } = useTranslation()
return (
<div className="flex size-full items-center">
<span>{t("fields.paymentProviders")}</span>
</div>
)
}

View File

@@ -0,0 +1 @@
export * from "./region-cell"

View File

@@ -0,0 +1,23 @@
import { useTranslation } from "react-i18next"
type RegionCellProps = {
name: string
}
export const RegionCell = ({ name }: RegionCellProps) => {
return (
<div className="flex h-full w-full items-center overflow-hidden">
<span className="truncate">{name}</span>
</div>
)
}
export const RegionHeader = () => {
const { t } = useTranslation()
return (
<div className="flex h-full w-full items-center">
<span className="truncate">{t("fields.name")}</span>
</div>
)
}