-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(dashboard): render main layout as home page for v2 #6823
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./main-layout" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
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 { useV2Store } from "../../../lib/api-v2" | ||
|
||
import { Skeleton } from "../../common/skeleton" | ||
import { NavItem, NavItemProps } from "../../layout/nav-item" | ||
import { Shell } from "../../layout/shell" | ||
|
||
import extensions from "medusa-admin:routes/links" | ||
|
||
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 } = useV2Store({}) | ||
|
||
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("discounts.domain"), | ||
to: "/discounts", | ||
}, | ||
{ | ||
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> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,11 @@ export const useV2Store = ({ initialData }: { initialData?: any }) => { | |
{ initialData } | ||
) | ||
|
||
const store = data.stores[0] | ||
const store = data?.stores[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: not really related to your PR, but make sense to manually set the error in the case where isLoading is false and the stores array is empty, to have the same effect as if the current useAdminStore endpoint fails. Otherwise, you might end up with a false positive, where loading is completed and no error is thrown, but at the same time store is undefined, causing the admin to break. |
||
|
||
if (!isLoading && !isError && typeof store === "undefined") { | ||
throw new Error("Store does not exist") | ||
} | ||
Comment on lines
+13
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kasperkristensen is this what you mean? |
||
|
||
return { store, isLoading, isError, error } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { OrderList as Component } from "./order-list" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// TODO: This is just a placeholder to render the actual "home" page | ||
// after logging in. Replace this with the actual order components when ready | ||
export const OrderList = () => { | ||
return <div className="flex w-full flex-col gap-y-2"></div> | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only thing that changes here is the v2Store call. Lmk if this is the desired approach.