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:
committed by
GitHub
parent
07205e4249
commit
75c5d5ad9e
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
@@ -624,6 +624,9 @@ export const RouteMap: RouteObject[] = [
|
||||
children: [
|
||||
{
|
||||
path: "/settings",
|
||||
handle: {
|
||||
crumb: () => "Settings",
|
||||
},
|
||||
element: <SettingsLayout />,
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 }}>
|
||||
|
||||
Reference in New Issue
Block a user