fix(dashboard): Minor cleanup and improve text legibility (#7045)
**What** - Cleans up some artifacts from the V1 -> V2 migrations. - Removes the MedusaProvider from the root in favor of a plain QueryClient. - Applies font styles to make the text in admin resemble designs in Figma more closely.
This commit is contained in:
1 parent
27387b7cf1
commit
51acd1da5b
17 files changed
+96
-320
No files matched your search
@@ -1,24 +1,18 @@
|
|||||||
import { Toaster } from "@medusajs/ui"
|
import { Toaster } from "@medusajs/ui"
|
||||||
import { MedusaProvider } from "medusa-react"
|
import { QueryClientProvider } from "@tanstack/react-query"
|
||||||
|
|
||||||
|
import { queryClient } from "./lib/medusa"
|
||||||
import { RouterProvider } from "./providers/router-provider"
|
import { RouterProvider } from "./providers/router-provider"
|
||||||
import { ThemeProvider } from "./providers/theme-provider"
|
import { ThemeProvider } from "./providers/theme-provider"
|
||||||
|
|
||||||
import { MEDUSA_BACKEND_URL, queryClient } from "./lib/medusa"
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<MedusaProvider
|
<QueryClientProvider client={queryClient}>
|
||||||
baseUrl={MEDUSA_BACKEND_URL}
|
|
||||||
queryClientProviderProps={{
|
|
||||||
client: queryClient,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<RouterProvider />
|
<RouterProvider />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</MedusaProvider>
|
</QueryClientProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./protected-route"
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
import { Spinner } from "@medusajs/icons"
|
||||||
|
import { Navigate, Outlet, useLocation } from "react-router-dom"
|
||||||
|
import { useMe } from "../../../hooks/api/users"
|
||||||
|
import { SearchProvider } from "../../../providers/search-provider"
|
||||||
|
import { SidebarProvider } from "../../../providers/sidebar-provider"
|
||||||
|
|
||||||
|
export const ProtectedRoute = () => {
|
||||||
|
const { user, isLoading } = useMe()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center">
|
||||||
|
<Spinner className="text-ui-fg-interactive animate-spin" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return <Navigate to="/login" state={{ from: location }} replace />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<SearchProvider>
|
||||||
|
<Outlet />
|
||||||
|
</SearchProvider>
|
||||||
|
</SidebarProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
+3
@@ -5,6 +5,9 @@ import { useAdminGetSession } from "medusa-react"
|
|||||||
import { SearchProvider } from "../../../providers/search-provider"
|
import { SearchProvider } from "../../../providers/search-provider"
|
||||||
import { SidebarProvider } from "../../../providers/sidebar-provider"
|
import { SidebarProvider } from "../../../providers/sidebar-provider"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated - Delete once all V1 domains have been migrated to V2.
|
||||||
|
*/
|
||||||
export const ProtectedRoute = () => {
|
export const ProtectedRoute = () => {
|
||||||
const { user, isLoading } = useAdminGetSession()
|
const { user, isLoading } = useAdminGetSession()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./main-layout"
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
import {
|
|
||||||
Buildings,
|
|
||||||
ChevronDownMini,
|
|
||||||
CurrencyDollar,
|
|
||||||
MinusMini,
|
|
||||||
ReceiptPercent,
|
|
||||||
ShoppingCart,
|
|
||||||
SquaresPlus,
|
|
||||||
Tag,
|
|
||||||
Users,
|
|
||||||
} from "@medusajs/icons"
|
|
||||||
import { Avatar, Text } from "@medusajs/ui"
|
|
||||||
import * as Collapsible from "@radix-ui/react-collapsible"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
import { Skeleton } from "../../common/skeleton"
|
|
||||||
import { NavItem, NavItemProps } from "../../layout/nav-item"
|
|
||||||
import { Shell } from "../../layout/shell"
|
|
||||||
|
|
||||||
import extensions from "medusa-admin:routes/links"
|
|
||||||
import { useStore } from "../../../hooks/api/store"
|
|
||||||
|
|
||||||
export const MainLayout = () => {
|
|
||||||
return (
|
|
||||||
<Shell>
|
|
||||||
<MainSidebar />
|
|
||||||
</Shell>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const MainSidebar = () => {
|
|
||||||
return (
|
|
||||||
<aside className="flex flex-1 flex-col justify-between overflow-y-auto">
|
|
||||||
<div className="flex flex-1 flex-col">
|
|
||||||
<div className="bg-ui-bg-subtle sticky top-0">
|
|
||||||
<Header />
|
|
||||||
<div className="px-3">
|
|
||||||
<div className="border-ui-border-strong h-px w-full border-b border-dashed" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<CoreRouteSection />
|
|
||||||
<ExtensionRouteSection />
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Header = () => {
|
|
||||||
const { store, isError, error } = useStore()
|
|
||||||
|
|
||||||
const name = store?.name
|
|
||||||
const fallback = store?.name?.slice(0, 1).toUpperCase()
|
|
||||||
|
|
||||||
if (isError) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full px-3 py-2">
|
|
||||||
<div className="flex items-center p-1 md:pr-2">
|
|
||||||
<div className="flex items-center gap-x-3">
|
|
||||||
{fallback ? (
|
|
||||||
<Avatar variant="squared" fallback={fallback} />
|
|
||||||
) : (
|
|
||||||
<Skeleton className="h-8 w-8 rounded-md" />
|
|
||||||
)}
|
|
||||||
{name ? (
|
|
||||||
<Text size="small" weight="plus" leading="compact">
|
|
||||||
{store.name}
|
|
||||||
</Text>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="h-[9px] w-[120px]" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const useCoreRoutes = (): Omit<NavItemProps, "pathname">[] => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
icon: <ShoppingCart />,
|
|
||||||
label: t("orders.domain"),
|
|
||||||
to: "/orders",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: t("draftOrders.domain"),
|
|
||||||
to: "/draft-orders",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <Tag />,
|
|
||||||
label: t("products.domain"),
|
|
||||||
to: "/products",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: t("collections.domain"),
|
|
||||||
to: "/collections",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("categories.domain"),
|
|
||||||
to: "/categories",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("giftCards.domain"),
|
|
||||||
to: "/gift-cards",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <Buildings />,
|
|
||||||
label: t("inventory.domain"),
|
|
||||||
to: "/inventory",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: t("reservations.domain"),
|
|
||||||
to: "/reservations",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <Users />,
|
|
||||||
label: t("customers.domain"),
|
|
||||||
to: "/customers",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: t("customerGroups.domain"),
|
|
||||||
to: "/customer-groups",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <ReceiptPercent />,
|
|
||||||
label: t("promotions.domain"),
|
|
||||||
to: "/promotions",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <CurrencyDollar />,
|
|
||||||
label: t("pricing.domain"),
|
|
||||||
to: "/pricing",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const CoreRouteSection = () => {
|
|
||||||
const coreRoutes = useCoreRoutes()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<nav className="flex flex-col gap-y-1 py-2">
|
|
||||||
{coreRoutes.map((route) => {
|
|
||||||
return <NavItem key={route.to} {...route} />
|
|
||||||
})}
|
|
||||||
</nav>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ExtensionRouteSection = () => {
|
|
||||||
if (!extensions.links || extensions.links.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="px-3">
|
|
||||||
<div className="border-ui-border-strong h-px w-full border-b border-dashed" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-y-1 py-2">
|
|
||||||
<Collapsible.Root defaultOpen>
|
|
||||||
<div className="px-4">
|
|
||||||
<Collapsible.Trigger asChild className="group/trigger">
|
|
||||||
<button className="text-ui-fg-subtle flex w-full items-center justify-between px-2">
|
|
||||||
<Text size="xsmall" weight="plus" leading="compact">
|
|
||||||
Extensions
|
|
||||||
</Text>
|
|
||||||
<div className="text-ui-fg-muted">
|
|
||||||
<ChevronDownMini className="group-data-[state=open]/trigger:hidden" />
|
|
||||||
<MinusMini className="group-data-[state=closed]/trigger:hidden" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</Collapsible.Trigger>
|
|
||||||
</div>
|
|
||||||
<Collapsible.Content>
|
|
||||||
<div className="flex flex-col gap-y-1 py-1 pb-4">
|
|
||||||
{extensions.links.map((link) => {
|
|
||||||
return (
|
|
||||||
<NavItem
|
|
||||||
key={link.path}
|
|
||||||
to={link.path}
|
|
||||||
label={link.label}
|
|
||||||
icon={link.icon ? <link.icon /> : <SquaresPlus />}
|
|
||||||
type="extension"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</Collapsible.Content>
|
|
||||||
</Collapsible.Root>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -11,12 +11,12 @@ import {
|
|||||||
} from "@medusajs/icons"
|
} from "@medusajs/icons"
|
||||||
import { Avatar, Text } from "@medusajs/ui"
|
import { Avatar, Text } from "@medusajs/ui"
|
||||||
import * as Collapsible from "@radix-ui/react-collapsible"
|
import * as Collapsible from "@radix-ui/react-collapsible"
|
||||||
import { useAdminStore } from "medusa-react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
|
import { useStore } from "../../../hooks/api/store"
|
||||||
import { Skeleton } from "../../common/skeleton"
|
import { Skeleton } from "../../common/skeleton"
|
||||||
import { NavItem, NavItemProps } from "../nav-item"
|
import { NavItem, NavItemProps } from "../../layout/nav-item"
|
||||||
import { Shell } from "../shell"
|
import { Shell } from "../../layout/shell"
|
||||||
|
|
||||||
import extensions from "medusa-admin:routes/links"
|
import extensions from "medusa-admin:routes/links"
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ const MainSidebar = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { store, isError, error } = useAdminStore()
|
const { store, isError, error } = useStore()
|
||||||
|
|
||||||
const name = store?.name
|
const name = store?.name
|
||||||
const fallback = store?.name?.slice(0, 1).toUpperCase()
|
const fallback = store?.name?.slice(0, 1).toUpperCase()
|
||||||
@@ -135,8 +135,8 @@ const useCoreRoutes = (): Omit<NavItemProps, "pathname">[] => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <ReceiptPercent />,
|
icon: <ReceiptPercent />,
|
||||||
label: t("discounts.domain"),
|
label: t("promotions.domain"),
|
||||||
to: "/discounts",
|
to: "/promotions",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <CurrencyDollar />,
|
icon: <CurrencyDollar />,
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ const Breadcrumbs = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* {!isLast && <TriangleRightMini className="-mt-0.5 mx-2" />} */}
|
|
||||||
{!isLast && <span className="mx-2 -mt-0.5">›</span>}
|
{!isLast && <span className="mx-2 -mt-0.5">›</span>}
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
@@ -140,7 +139,7 @@ const UserBadge = () => {
|
|||||||
<button
|
<button
|
||||||
disabled={!user}
|
disabled={!user}
|
||||||
className={clx(
|
className={clx(
|
||||||
"shadow-borders-base flex max-w-[192px] select-none items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 pl-1 pr-2.5"
|
"shadow-borders-base flex max-w-[192px] select-none items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 pl-1 pr-2.5 outline-none"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{fallback ? (
|
{fallback ? (
|
||||||
@@ -384,7 +383,7 @@ const MobileSidebarContainer = ({ children }: PropsWithChildren) => {
|
|||||||
<Dialog.Root open={mobile} onOpenChange={() => toggle("mobile")}>
|
<Dialog.Root open={mobile} onOpenChange={() => toggle("mobile")}>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Overlay className="bg-ui-bg-overlay fixed inset-0" />
|
<Dialog.Overlay className="bg-ui-bg-overlay fixed inset-0" />
|
||||||
<Dialog.Content className="bg-ui-bg-subtle fixed inset-y-0 left-0 h-screen w-[220px] border-r">
|
<Dialog.Content className="bg-ui-bg-subtle fixed inset-y-0 left-0 h-screen w-full max-w-[240px] border-r">
|
||||||
{children}
|
{children}
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</Dialog.Portal>
|
</Dialog.Portal>
|
||||||
|
|||||||
@@ -28,7 +28,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@apply bg-ui-bg-subtle text-ui-fg-base;
|
@apply bg-ui-bg-subtle text-ui-fg-base antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
type Options = {
|
|
||||||
leading?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const debounce = (
|
|
||||||
func: (...args: any[]) => void,
|
|
||||||
delay: number,
|
|
||||||
{ leading }: Options = {}
|
|
||||||
): ((...args: any[]) => void) => {
|
|
||||||
let timerId: NodeJS.Timeout | undefined
|
|
||||||
|
|
||||||
return (...args) => {
|
|
||||||
if (!timerId && leading) {
|
|
||||||
func(...args)
|
|
||||||
}
|
|
||||||
clearTimeout(timerId)
|
|
||||||
|
|
||||||
timerId = setTimeout(() => func(...args), delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export function upperCaseFirst(str: string): string {
|
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { createContext } from "react";
|
import { createContext } from "react"
|
||||||
import { Feature } from "./types";
|
import { Feature } from "./types"
|
||||||
|
|
||||||
type FeatureContextValue = {
|
type FeatureContextValue = {
|
||||||
isFeatureEnabled: (feature: Feature) => boolean;
|
isFeatureEnabled: (feature: Feature) => boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export const FeatureContext = createContext<FeatureContextValue | null>(null);
|
export const FeatureContext = createContext<FeatureContextValue | null>(null)
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
import { useAdminStore } from "medusa-react";
|
import { useAdminStore } from "medusa-react"
|
||||||
import { PropsWithChildren, useEffect, useState } from "react";
|
import { PropsWithChildren, useEffect, useState } from "react"
|
||||||
import { FeatureContext } from "./feature-context";
|
import { FeatureContext } from "./feature-context"
|
||||||
import { Feature } from "./types";
|
import { Feature } from "./types"
|
||||||
|
|
||||||
export const FeatureProvider = ({ children }: PropsWithChildren) => {
|
export const FeatureProvider = ({ children }: PropsWithChildren) => {
|
||||||
const { store, isLoading } = useAdminStore();
|
const { store, isLoading } = useAdminStore()
|
||||||
const [features, setFeatures] = useState<Feature[]>([]);
|
const [features, setFeatures] = useState<Feature[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!store || isLoading) {
|
if (!store || isLoading) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const flags = store.feature_flags
|
const flags = store.feature_flags
|
||||||
.filter((f) => f.value === true)
|
.filter((f) => f.value === true)
|
||||||
.map((f) => f.key);
|
.map((f) => f.key)
|
||||||
const modules = store.modules.map((m) => m.module);
|
const modules = store.modules.map((m) => m.module)
|
||||||
const enabled = flags.concat(modules);
|
const enabled = flags.concat(modules)
|
||||||
|
|
||||||
setFeatures(enabled as Feature[]);
|
setFeatures(enabled as Feature[])
|
||||||
}, [store, isLoading]);
|
}, [store, isLoading])
|
||||||
|
|
||||||
function isFeatureEnabled(feature: Feature) {
|
function isFeatureEnabled(feature: Feature) {
|
||||||
return features.includes(feature);
|
return features.includes(feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FeatureContext.Provider value={{ isFeatureEnabled }}>
|
<FeatureContext.Provider value={{ isFeatureEnabled }}>
|
||||||
{children}
|
{children}
|
||||||
</FeatureContext.Provider>
|
</FeatureContext.Provider>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
@@ -5,12 +5,12 @@ const featureFlags = [
|
|||||||
"publishable_api_keys",
|
"publishable_api_keys",
|
||||||
"sales_channels",
|
"sales_channels",
|
||||||
"tax_inclusive_pricing",
|
"tax_inclusive_pricing",
|
||||||
] as const;
|
] as const
|
||||||
|
|
||||||
type FeatureFlag = (typeof featureFlags)[number];
|
type FeatureFlag = (typeof featureFlags)[number]
|
||||||
|
|
||||||
const modules = ["inventory"] as const;
|
const modules = ["inventory"] as const
|
||||||
|
|
||||||
type Module = (typeof modules)[number];
|
type Module = (typeof modules)[number]
|
||||||
|
|
||||||
export type Feature = FeatureFlag | Module;
|
export type Feature = FeatureFlag | Module
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
import { AdminCustomersRes } from "@medusajs/client-types"
|
||||||
|
import {
|
||||||
|
AdminCollectionsRes,
|
||||||
|
AdminProductsRes,
|
||||||
|
AdminPromotionRes,
|
||||||
|
AdminRegionsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
import {
|
import {
|
||||||
AdminApiKeyResponse,
|
AdminApiKeyResponse,
|
||||||
AdminCustomerGroupResponse,
|
AdminCustomerGroupResponse,
|
||||||
@@ -5,49 +12,13 @@ import {
|
|||||||
SalesChannelDTO,
|
SalesChannelDTO,
|
||||||
UserDTO,
|
UserDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import { Outlet, RouteObject } from "react-router-dom"
|
||||||
AdminCollectionsRes,
|
|
||||||
AdminProductsRes,
|
|
||||||
AdminPromotionRes,
|
|
||||||
AdminRegionsRes,
|
|
||||||
} from "@medusajs/medusa"
|
|
||||||
import { Navigate, Outlet, RouteObject, useLocation } from "react-router-dom"
|
|
||||||
|
|
||||||
import { AdminCustomersRes } from "@medusajs/client-types"
|
import { ProtectedRoute } from "../../components/authentication/protected-route"
|
||||||
import { ErrorBoundary } from "../../components/error/error-boundary"
|
import { ErrorBoundary } from "../../components/error/error-boundary"
|
||||||
import { InventoryItemRes } from "../../types/api-responses"
|
import { MainLayout } from "../../components/layout/main-layout"
|
||||||
import { MainLayout } from "../../components/layout-v2/main-layout"
|
|
||||||
import { PriceListRes } from "../../types/api-responses"
|
|
||||||
import { SearchProvider } from "../search-provider"
|
|
||||||
import { SettingsLayout } from "../../components/layout/settings-layout"
|
import { SettingsLayout } from "../../components/layout/settings-layout"
|
||||||
import { SidebarProvider } from "../sidebar-provider"
|
import { InventoryItemRes, PriceListRes } from "../../types/api-responses"
|
||||||
import { Spinner } from "@medusajs/icons"
|
|
||||||
import { useMe } from "../../hooks/api/users"
|
|
||||||
|
|
||||||
export const ProtectedRoute = () => {
|
|
||||||
const { user, isLoading } = useMe()
|
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return (
|
|
||||||
<div className="flex min-h-screen items-center justify-center">
|
|
||||||
<Spinner className="text-ui-fg-interactive animate-spin" />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return <Navigate to="/login" state={{ from: location }} replace />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarProvider>
|
|
||||||
<SearchProvider>
|
|
||||||
<Outlet />
|
|
||||||
</SearchProvider>
|
|
||||||
</SidebarProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experimental V2 routes.
|
* Experimental V2 routes.
|
||||||
@@ -473,9 +444,6 @@ export const v2Routes: RouteObject[] = [
|
|||||||
{
|
{
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
element: <SettingsLayout />,
|
element: <SettingsLayout />,
|
||||||
handle: {
|
|
||||||
crumb: () => "Settings",
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
index: true,
|
index: true,
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { PropsWithChildren, useState } from "react"
|
import { PropsWithChildren, useEffect, useState } from "react"
|
||||||
|
import { useLocation } from "react-router-dom"
|
||||||
import { SidebarContext } from "./sidebar-context"
|
import { SidebarContext } from "./sidebar-context"
|
||||||
|
|
||||||
export const SidebarProvider = ({ children }: PropsWithChildren) => {
|
export const SidebarProvider = ({ children }: PropsWithChildren) => {
|
||||||
const [desktop, setDesktop] = useState(true)
|
const [desktop, setDesktop] = useState(true)
|
||||||
const [mobile, setMobile] = useState(false)
|
const [mobile, setMobile] = useState(false)
|
||||||
|
|
||||||
|
const { pathname } = useLocation()
|
||||||
|
|
||||||
const toggle = (view: "desktop" | "mobile") => {
|
const toggle = (view: "desktop" | "mobile") => {
|
||||||
if (view === "desktop") {
|
if (view === "desktop") {
|
||||||
setDesktop(!desktop)
|
setDesktop(!desktop)
|
||||||
@@ -13,6 +16,13 @@ export const SidebarProvider = ({ children }: PropsWithChildren) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close the mobile sidebar on route change
|
||||||
|
// this is to prevent the sidebar from staying open
|
||||||
|
// when navigating to a new page
|
||||||
|
useEffect(() => {
|
||||||
|
setMobile(false)
|
||||||
|
}, [pathname])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarContext.Provider value={{ desktop, mobile, toggle }}>
|
<SidebarContext.Provider value={{ desktop, mobile, toggle }}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// / <reference types="vite/client" />
|
// / <reference types="vite/client" />
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly MEDUSA_ADMIN_BACKEND_URL: string
|
readonly VITE_MEDUSA_ADMIN_BACKEND_URL: string
|
||||||
readonly VITE_MEDUSA_V2: "true" | "false"
|
readonly VITE_MEDUSA_V2: "true" | "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user