feat(dashboard,js-sdk,types): Update app layout, and add user sdk methods (#8182)

**What**
- Updates app layout (sidebar and topbar)
- Adds "System" option to theme toggle (we now default to system)
- Adds sdk methods for user endpoints (RESOLVES CC-67)
This commit is contained in:
Kasper Fabricius Kristensen
2024-07-19 13:18:48 +02:00
committed by GitHub
parent 07205e4249
commit 75c5d5ad9e
31 changed files with 1346 additions and 2400 deletions

View File

@@ -29,6 +29,7 @@ export const useShortcuts = ({
debounce: number
}) => {
const [keys, setKeys] = useState<string[]>([])
const navigate = useNavigate()
// eslint-disable-next-line react-hooks/exhaustive-deps
const removeKeys = useCallback(
@@ -42,6 +43,15 @@ export const useShortcuts = ({
if (shortcut && shortcut.callback) {
shortcut.callback()
setKeys([])
return
}
if (shortcut && shortcut.to) {
navigate(shortcut.to)
setKeys([])
return
}
}, debounce / 2),
[]
@@ -105,170 +115,186 @@ export const useGlobalShortcuts = () => {
keys: {
Mac: ["G", "O"],
},
label: t("app.keyboardShortcuts.goToOrders"),
label: t("app.keyboardShortcuts.navigation.goToOrders"),
type: "pageShortcut",
callback: () => navigate("/orders"),
to: "/orders",
},
{
keys: {
Mac: ["G", "P"],
},
label: t("app.keyboardShortcuts.goToProducts"),
label: t("app.keyboardShortcuts.navigation.goToProducts"),
type: "pageShortcut",
callback: () => navigate("/products"),
},
{
keys: {
Mac: ["G", "P", "C"],
},
label: t("app.keyboardShortcuts.goToCollections"),
type: "pageShortcut",
callback: () => navigate("/collections"),
},
{
keys: {
Mac: ["G", "P", "A"],
},
label: t("app.keyboardShortcuts.goToCategories"),
type: "pageShortcut",
callback: () => navigate("/categories"),
to: "/products",
},
{
keys: {
Mac: ["G", "C"],
},
label: t("app.keyboardShortcuts.goToCustomers"),
label: t("app.keyboardShortcuts.navigation.goToCollections"),
type: "pageShortcut",
callback: () => navigate("/customers"),
to: "/collections",
},
{
keys: {
Mac: ["G", "C", "G"],
Mac: ["G", "A"],
},
label: t("app.keyboardShortcuts.goToCustomerGroups"),
label: t("app.keyboardShortcuts.navigation.goToCategories"),
type: "pageShortcut",
callback: () => navigate("/customer-groups"),
to: "/categories",
},
{
keys: {
Mac: ["G", "U"],
},
label: t("app.keyboardShortcuts.navigation.goToCustomers"),
type: "pageShortcut",
to: "/customers",
},
{
keys: {
Mac: ["G", "G"],
},
label: t("app.keyboardShortcuts.navigation.goToCustomerGroups"),
type: "pageShortcut",
to: "/customer-groups",
},
{
keys: {
Mac: ["G", "I"],
},
label: t("app.keyboardShortcuts.goToInventory"),
label: t("app.keyboardShortcuts.navigation.goToInventory"),
type: "pageShortcut",
callback: () => navigate("/inventory"),
},
{
keys: {
Mac: ["G", "I", "R"],
},
label: t("app.keyboardShortcuts.goToReservations"),
type: "pageShortcut",
callback: () => navigate("/reservations"),
},
{
keys: {
Mac: ["G", "L"],
},
label: t("app.keyboardShortcuts.goToPriceLists"),
type: "pageShortcut",
callback: () => navigate("/price-lists"),
to: "/inventory",
},
{
keys: {
Mac: ["G", "R"],
},
label: t("app.keyboardShortcuts.goToPromotions"),
label: t("app.keyboardShortcuts.navigation.goToReservations"),
type: "pageShortcut",
callback: () => navigate("/promotions"),
to: "/reservations",
},
{
keys: {
Mac: ["G", "R", "C"],
Mac: ["G", "L"],
},
label: t("app.keyboardShortcuts.goToCampaigns"),
label: t("app.keyboardShortcuts.navigation.goToPriceLists"),
type: "pageShortcut",
callback: () => navigate("/campaigns"),
},
//
{
keys: {
Mac: ["G", "S", "S"],
},
label: t("app.keyboardShortcuts.goToStore"),
type: "settingShortcut",
callback: () => navigate("/settings/store"),
to: "/price-lists",
},
{
keys: {
Mac: ["G", "S", "U"],
Mac: ["G", "M"],
},
label: t("app.keyboardShortcuts.goToUsers"),
type: "settingShortcut",
callback: () => navigate("/settings/users"),
label: t("app.keyboardShortcuts.navigation.goToPromotions"),
type: "pageShortcut",
to: "/promotions",
},
{
keys: {
Mac: ["G", "S", "R"],
Mac: ["G", "K"],
},
label: t("app.keyboardShortcuts.goToRegions"),
label: t("app.keyboardShortcuts.navigation.goToCampaigns"),
type: "pageShortcut",
to: "/campaigns",
},
// Settings
{
keys: {
Mac: ["G", ","],
},
label: t("app.keyboardShortcuts.settings.goToSettings"),
type: "settingShortcut",
callback: () => navigate("/settings/regions"),
to: "/settings",
},
{
keys: {
Mac: ["G", "S", "T"],
Mac: ["G", ",", "S"],
},
label: t("app.keyboardShortcuts.goToTaxRegions"),
label: t("app.keyboardShortcuts.settings.goToStore"),
type: "settingShortcut",
callback: () => navigate("/settings/tax-regions"),
to: "/settings/store",
},
{
keys: {
Mac: ["G", "S", "A"],
Mac: ["G", ",", "U"],
},
label: t("app.keyboardShortcuts.goToSalesChannels"),
label: t("app.keyboardShortcuts.settings.goToUsers"),
type: "settingShortcut",
callback: () => navigate("/settings/sales-channels"),
to: "/settings/users",
},
{
keys: {
Mac: ["G", "S", "P"],
Mac: ["G", ",", "R"],
},
label: t("app.keyboardShortcuts.goToProductTypes"),
label: t("app.keyboardShortcuts.settings.goToRegions"),
type: "settingShortcut",
callback: () => navigate("/settings/product-types"),
to: "/settings/regions",
},
{
keys: {
Mac: ["G", "S", "L"],
Mac: ["G", ",", "T"],
},
label: t("app.keyboardShortcuts.goToLocations"),
label: t("app.keyboardShortcuts.settings.goToTaxRegions"),
type: "settingShortcut",
callback: () => navigate("/settings/locations"),
to: "/settings/tax-regions",
},
{
keys: {
Mac: ["G", "S", "J"],
Mac: ["G", ",", "A"],
},
label: t("app.keyboardShortcuts.goToPublishableApiKeys"),
label: t("app.keyboardShortcuts.settings.goToSalesChannels"),
type: "settingShortcut",
callback: () => navigate("/settings/publishable-api-keys"),
to: "/settings/sales-channels",
},
{
keys: {
Mac: ["G", "S", "K"],
Mac: ["G", ",", "P"],
},
label: t("app.keyboardShortcuts.goToSecretApiKeys"),
label: t("app.keyboardShortcuts.settings.goToProductTypes"),
type: "settingShortcut",
callback: () => navigate("/settings/secret-api-keys"),
to: "/settings/product-types",
},
{
keys: {
Mac: ["G", "S", "W"],
Mac: ["G", ",", "L"],
},
label: t("app.keyboardShortcuts.goToWorkflows"),
label: t("app.keyboardShortcuts.settings.goToLocations"),
type: "settingShortcut",
callback: () => navigate("/settings/workflows"),
to: "/settings/locations",
},
{
keys: {
Mac: ["G", ",", "J"],
},
label: t("app.keyboardShortcuts.settings.goToPublishableApiKeys"),
type: "settingShortcut",
to: "/settings/publishable-api-keys",
},
{
keys: {
Mac: ["G", ",", "K"],
},
label: t("app.keyboardShortcuts.settings.goToSecretApiKeys"),
type: "settingShortcut",
to: "/settings/secret-api-keys",
},
{
keys: {
Mac: ["G", ",", "W"],
},
label: t("app.keyboardShortcuts.settings.goToWorkflows"),
type: "settingShortcut",
to: "/settings/workflows",
},
{
keys: {
Mac: ["G", ",", "M"],
},
label: t("app.keyboardShortcuts.settings.goToProfile"),
type: "settingShortcut",
to: "/settings/profile",
},
// Commands
{

View File

@@ -15,6 +15,14 @@ export type Shortcut = {
keys: Keys
type: ShortcutType
label: string
callback: () => void
_defaultKeys?: Keys
}
} & (
| {
callback: () => void
to?: never
}
| {
to: string
callback?: never
}
)

View File

@@ -624,6 +624,9 @@ export const RouteMap: RouteObject[] = [
children: [
{
path: "/settings",
handle: {
crumb: () => "Settings",
},
element: <SettingsLayout />,
children: [
{

View File

@@ -1,12 +1,24 @@
import { PropsWithChildren, useEffect, useState } from "react"
import { Search } from "../../components/search"
import { useSidebar } from "../sidebar-provider"
import { SearchContext } from "./search-context"
export const SearchProvider = ({ children }: PropsWithChildren) => {
const [open, setOpen] = useState(false)
const { mobile, toggle } = useSidebar()
const toggleSearch = () => {
setOpen(!open)
const update = !open
/**
* If the mobile sidebar is open, then make sure
* to close it when opening the search
*/
if (update && mobile) {
toggle("mobile")
}
setOpen(update)
}
useEffect(() => {

View File

@@ -1,3 +1,3 @@
export type { Theme } from "./theme-context"
export type { ThemeOption as Theme } from "./theme-context"
export * from "./theme-provider"
export * from "./use-theme"

View File

@@ -1,10 +1,11 @@
import { createContext } from "react"
export type Theme = "light" | "dark"
export type ThemeOption = "light" | "dark" | "system"
export type ThemeValue = "light" | "dark"
type ThemeContextValue = {
theme: Theme
setTheme: (theme: Theme) => void
theme: ThemeOption
setTheme: (theme: ThemeOption) => void
}
export const ThemeContext = createContext<ThemeContextValue | null>(null)

View File

@@ -1,16 +1,44 @@
import { PropsWithChildren, useEffect, useState } from "react"
import { Theme, ThemeContext } from "./theme-context"
import { ThemeContext, ThemeOption, ThemeValue } from "./theme-context"
const THEME_KEY = "medusa_admin_theme"
export const ThemeProvider = ({ children }: PropsWithChildren) => {
const [state, setState] = useState<Theme>(
(localStorage?.getItem(THEME_KEY) as Theme) || "light"
)
function getDefaultValue(): ThemeOption {
const persisted = localStorage?.getItem(THEME_KEY) as ThemeOption
const setTheme = (theme: Theme) => {
if (persisted) {
return persisted
}
return "system"
}
function getThemeValue(selected: ThemeOption): ThemeValue {
if (selected === "system") {
if (window !== undefined) {
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
}
// Default to light theme if we can't detect the system preference
return "light"
}
return selected
}
export const ThemeProvider = ({ children }: PropsWithChildren) => {
const [state, setState] = useState<ThemeOption>(getDefaultValue())
const [value, setValue] = useState<ThemeValue>(getThemeValue(state))
const setTheme = (theme: ThemeOption) => {
localStorage.setItem(THEME_KEY, theme)
const themeValue = getThemeValue(theme)
setState(theme)
setValue(themeValue)
}
useEffect(() => {
@@ -34,8 +62,8 @@ export const ThemeProvider = ({ children }: PropsWithChildren) => {
)
document.head.appendChild(css)
html.classList.remove(state === "light" ? "dark" : "light")
html.classList.add(state)
html.classList.remove(value === "light" ? "dark" : "light")
html.classList.add(value)
/**
* Re-enable transitions after the theme has been set,
@@ -44,7 +72,7 @@ export const ThemeProvider = ({ children }: PropsWithChildren) => {
window.getComputedStyle(css).opacity
document.head.removeChild(css)
}
}, [state])
}, [value])
return (
<ThemeContext.Provider value={{ theme: state, setTheme }}>