From d8c92152276b5618b723d7019ccf85a2af7dc995 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 09:49:13 +0200 Subject: [PATCH 1/7] progress --- packages/admin-next/dashboard/package.json | 2 +- .../layout-v2/main-layout/main-layout.tsx | 4 +- .../src/components/layout/shell/shell.tsx | 44 +++------ .../dashboard/src/hooks/api/auth.tsx | 22 +++++ .../dashboard/src/hooks/api/currencies.tsx | 39 ++++++++ .../dashboard/src/hooks/api/customers.tsx | 77 +++++++++++++++ .../dashboard/src/hooks/api/promotions.tsx | 24 +++++ .../dashboard/src/hooks/api/regions.tsx | 96 +++++++++++++++++++ .../src/hooks/api/sales-channels.tsx | 86 +++++++++++++++++ .../dashboard/src/hooks/api/store.tsx | 48 ++++++++++ .../dashboard/src/hooks/api/users.tsx | 76 +++++++++++++++ .../dashboard/src/lib/client/api-keys.ts | 21 ++++ .../dashboard/src/lib/client/auth.ts | 26 +++++ .../dashboard/src/lib/client/client.ts | 21 ++++ .../dashboard/src/lib/client/common.ts | 40 ++++++++ .../dashboard/src/lib/client/currencies.ts | 18 ++++ .../dashboard/src/lib/client/customers.ts | 38 ++++++++ .../dashboard/src/lib/client/index.ts | 1 + .../dashboard/src/lib/client/promotions.ts | 18 ++++ .../dashboard/src/lib/client/regions.ts | 49 ++++++++++ .../src/lib/client/sales-channels.ts | 52 ++++++++++ .../dashboard/src/lib/client/stores.ts | 30 ++++++ .../dashboard/src/lib/client/users.ts | 29 ++++++ .../dashboard/src/lib/query-key-factory.ts | 45 +++++++++ .../src/providers/router-provider/v2.tsx | 4 +- .../dashboard/src/types/api-payloads.ts | 41 ++++++++ .../dashboard/src/types/api-responses.ts | 71 ++++++++++++++ .../dashboard/src/v2-routes/login/login.tsx | 6 +- .../profile-general-section.tsx | 4 +- .../profile/profile-detail/profile-detail.tsx | 4 +- .../edit-profile-form/edit-profile-form.tsx | 18 +--- .../profile/profile-edit/profile-edit.tsx | 4 +- .../store-currency-section.tsx | 16 ++-- .../store-general-section.tsx | 4 +- .../v2-routes/store/store-detail/loader.ts | 31 ++---- .../store/store-detail/store-detail.tsx | 6 +- .../edit-store-form/edit-store-form.tsx | 10 +- .../v2-routes/store/store-edit/store-edit.tsx | 4 +- .../user-list-table/user-list-table.tsx | 13 +-- 39 files changed, 1033 insertions(+), 109 deletions(-) create mode 100644 packages/admin-next/dashboard/src/hooks/api/auth.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/currencies.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/customers.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/promotions.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/regions.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/store.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/users.tsx create mode 100644 packages/admin-next/dashboard/src/lib/client/api-keys.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/auth.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/client.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/common.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/currencies.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/customers.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/index.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/promotions.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/regions.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/sales-channels.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/stores.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/users.ts create mode 100644 packages/admin-next/dashboard/src/lib/query-key-factory.ts create mode 100644 packages/admin-next/dashboard/src/types/api-payloads.ts create mode 100644 packages/admin-next/dashboard/src/types/api-responses.ts diff --git a/packages/admin-next/dashboard/package.json b/packages/admin-next/dashboard/package.json index faea370361d5a..dd51aec14d43c 100644 --- a/packages/admin-next/dashboard/package.json +++ b/packages/admin-next/dashboard/package.json @@ -23,7 +23,7 @@ "@medusajs/ui": "workspace:^", "@radix-ui/react-collapsible": "1.0.3", "@radix-ui/react-hover-card": "^1.0.7", - "@tanstack/react-query": "4.22.0", + "@tanstack/react-query": "^5.28.14", "@tanstack/react-table": "8.10.7", "@tanstack/react-virtual": "^3.0.4", "@uiw/react-json-view": "^2.0.0-alpha.17", diff --git a/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx b/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx index 9bf63f4eb11b8..0b29836030910 100644 --- a/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx +++ b/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx @@ -12,13 +12,13 @@ import { 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" +import { useStore } from "../../../hooks/api/store" export const MainLayout = () => { return ( @@ -46,7 +46,7 @@ const MainSidebar = () => { } const Header = () => { - const { store, isError, error } = useV2Store({}) + const { store, isError, error } = useStore() const name = store?.name const fallback = store?.name?.slice(0, 1).toUpperCase() diff --git a/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx b/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx index 2c013c5b64317..f41dfcef0c464 100644 --- a/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx +++ b/packages/admin-next/dashboard/src/components/layout/shell/shell.tsx @@ -11,7 +11,6 @@ import { } from "@medusajs/icons" import { Avatar, DropdownMenu, IconButton, Kbd, Text, clx } from "@medusajs/ui" import * as Dialog from "@radix-ui/react-dialog" -import { useAdminDeleteSession, useAdminGetSession } from "medusa-react" import { PropsWithChildren } from "react" import { Link, @@ -24,13 +23,10 @@ import { import { Skeleton } from "../../common/skeleton" -import { queryClient } from "../../../lib/medusa" +import { useMe } from "../../../hooks/api/users" import { useSearch } from "../../../providers/search-provider" import { useSidebar } from "../../../providers/sidebar-provider" import { useTheme } from "../../../providers/theme-provider" -import { useV2Session } from "../../../lib/api-v2" - -const V2_ENABLED = import.meta.env.VITE_MEDUSA_V2 || "false" export const Shell = ({ children }: PropsWithChildren) => { return ( @@ -119,21 +115,7 @@ const Breadcrumbs = () => { } const UserBadge = () => { - const isV2Enabled = V2_ENABLED === "true" - - // Medusa V2 disabled - const v1 = useAdminGetSession({ - enabled: !isV2Enabled, - }) - - // Medusa V2 enabled - const v2 = useV2Session({ - enabled: isV2Enabled, - }) - - // Comment: Only place where we switch between the two modes inline. - // This is to avoid having to rebuild the shell for the app. - const { user, isLoading, isError, error } = !isV2Enabled ? v1 : v2 + const { user, isLoading, isError, error } = useMe() const name = [user?.first_name, user?.last_name].filter(Boolean).join(" ") const displayName = name || user?.email @@ -220,19 +202,19 @@ const ThemeToggle = () => { const Logout = () => { const navigate = useNavigate() - const { mutateAsync: logoutMutation } = useAdminDeleteSession() + // const { mutateAsync: logoutMutation } = useAdminDeleteSession() const handleLayout = async () => { - await logoutMutation(undefined, { - onSuccess: () => { - /** - * When the user logs out, we want to clear the query cache - */ - queryClient.clear() - - navigate("/login") - }, - }) + // await logoutMutation(undefined, { + // onSuccess: () => { + // /** + // * When the user logs out, we want to clear the query cache + // */ + // queryClient.clear() + // navigate("/login") + // }, + // }) + // noop } return ( diff --git a/packages/admin-next/dashboard/src/hooks/api/auth.tsx b/packages/admin-next/dashboard/src/hooks/api/auth.tsx new file mode 100644 index 0000000000000..5c838f34254f7 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/auth.tsx @@ -0,0 +1,22 @@ +import { UseMutationOptions, useMutation } from "@tanstack/react-query" + +import { client } from "../../lib/client" +import { EmailPassReq } from "../../types/api-payloads" +import { EmailPassRes } from "../../types/api-responses" + +export const useEmailPassLogin = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.auth.authenticate.emailPass(payload), + onSuccess: async (data: { token: string }, variables, context) => { + const { token } = data + + // Create a new session with the token + await client.auth.login(token) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/currencies.tsx b/packages/admin-next/dashboard/src/hooks/api/currencies.tsx new file mode 100644 index 0000000000000..c662a9b8e3f74 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/currencies.tsx @@ -0,0 +1,39 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { CurrencyListRes, CurrencyRes } from "../../types/api-responses" + +const CURRENCIES_QUERY_KEY = "currencies" as const +const currenciesQueryKeys = queryKeysFactory(CURRENCIES_QUERY_KEY) + +export const useCurrencies = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.currencies.list(query), + queryKey: currenciesQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCurrency = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: currenciesQueryKeys.detail(id), + queryFn: async () => client.currencies.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/hooks/api/customers.tsx b/packages/admin-next/dashboard/src/hooks/api/customers.tsx new file mode 100644 index 0000000000000..ca7c2750c6777 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/customers.tsx @@ -0,0 +1,77 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { CreateCustomerReq, UpdateCustomerReq } from "../../types/api-payloads" +import { CustomerListRes, CustomerRes } from "../../types/api-responses" + +const CUSTOMERS_QUERY_KEY = "customers" as const +const customersQueryKeys = queryKeysFactory(CUSTOMERS_QUERY_KEY) + +export const useCustomer = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: customersQueryKeys.detail(id), + queryFn: async () => client.customers.retrieve(id, query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCustomers = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.customers.list(query), + queryKey: customersQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateCustomer = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.customers.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: customersQueryKeys.lists() }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateCustomer = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.customers.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: customersQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: customersQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/promotions.tsx b/packages/admin-next/dashboard/src/hooks/api/promotions.tsx new file mode 100644 index 0000000000000..8d8f095f05c4d --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/promotions.tsx @@ -0,0 +1,24 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" + +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { PromotionRes } from "../../types/api-responses" + +const PROMOTIONS_QUERY_KEY = "promotions" as const +const promotionsQueryKeys = queryKeysFactory(PROMOTIONS_QUERY_KEY) + +export const usePromotion = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: promotionsQueryKeys.detail(id), + queryFn: async () => client.promotions.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/hooks/api/regions.tsx b/packages/admin-next/dashboard/src/hooks/api/regions.tsx new file mode 100644 index 0000000000000..f456fa3f9cf3b --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/regions.tsx @@ -0,0 +1,96 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { CreateRegionReq } from "../../types/api-payloads" +import { + RegionDeleteRes, + RegionListRes, + RegionRes, +} from "../../types/api-responses" + +const REGIONS_QUERY_KEY = "regions" as const +const regionsQueryKeys = queryKeysFactory(REGIONS_QUERY_KEY) + +export const useRegion = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: regionsQueryKeys.detail(id), + queryFn: async () => client.regions.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useRegions = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.regions.list(query), + queryKey: regionsQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateRegion = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.regions.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateRegion = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.regions.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: regionsQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteRegion = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.regions.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: regionsQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: regionsQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx b/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx new file mode 100644 index 0000000000000..dae42172a18f0 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx @@ -0,0 +1,86 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" + +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { + CreateSalesChannelReq, + UpdateSalesChannelReq, +} from "../../types/api-payloads" +import { SalesChannelListRes, SalesChannelRes } from "../../types/api-responses" + +const SALES_CHANNELS_QUERY_KEY = "sales-channels" as const +const salesChannelsQueryKeys = queryKeysFactory(SALES_CHANNELS_QUERY_KEY) + +export const useSalesChannel = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: salesChannelsQueryKeys.detail(id), + queryFn: async () => client.salesChannels.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useSalesChannels = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.salesChannels.list(query), + queryKey: salesChannelsQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateSalesChannel = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.salesChannels.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.lists(), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateSalesChannel = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.salesChannels.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.lists(), + }) + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.detail(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/store.tsx b/packages/admin-next/dashboard/src/hooks/api/store.tsx new file mode 100644 index 0000000000000..973ac0a3ee376 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/store.tsx @@ -0,0 +1,48 @@ +import { + MutationOptions, + QueryKey, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" + +import { queryKeysFactory } from "medusa-react" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { UpdateStoreReq } from "../../types/api-payloads" +import { StoreRes } from "../../types/api-responses" + +const STORE_QUERY_KEY = "store" as const +const storeQueryKeys = queryKeysFactory(STORE_QUERY_KEY) + +export const useStore = ( + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.stores.retrieve(), + queryKey: storeQueryKeys.details(), + ...options, + }) + + return { + ...data, + ...rest, + } +} + +export const useUpdateStore = ( + id: string, + options?: MutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.stores.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: storeQueryKeys.details() }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/users.tsx b/packages/admin-next/dashboard/src/hooks/api/users.tsx new file mode 100644 index 0000000000000..328f2935f46c1 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/users.tsx @@ -0,0 +1,76 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { UpdateUserReq } from "../../types/api-payloads" +import { UserListRes, UserRes } from "../../types/api-responses" + +const USERS_QUERY_KEY = "users" as const +const usersQueryKeys = { + ...queryKeysFactory(USERS_QUERY_KEY), + me: () => [USERS_QUERY_KEY, "me"], +} + +export const useMe = ( + options?: UseQueryOptions +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.users.me(), + queryKey: usersQueryKeys.me(), + ...options, + }) + + return { + ...data, + ...rest, + } +} + +export const useUser = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey"> +) => { + +export const useUsers = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.users.list(query), + queryKey: usersQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useUpdateUser = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.users.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: usersQueryKeys.detail(id) }) + queryClient.invalidateQueries({ queryKey: usersQueryKeys.lists() }) + + // We invalidate the me query in case the user updates their own profile + queryClient.invalidateQueries({ queryKey: usersQueryKeys.me() }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/lib/client/api-keys.ts b/packages/admin-next/dashboard/src/lib/client/api-keys.ts new file mode 100644 index 0000000000000..c2f223bbf71d7 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/api-keys.ts @@ -0,0 +1,21 @@ +import { ApiKeyListRes, ApiKeyRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +const retrieveApiKey = async (id: string, query?: Record) => { + return makeRequest>( + `/admin/api-keys/${id}`, + query + ) +} + +const listApiKeys = async (query?: Record) => { + return makeRequest>( + `/admin/api-keys`, + query + ) +} + +export const apiKeys = { + retrieve: retrieveApiKey, + list: listApiKeys, +} diff --git a/packages/admin-next/dashboard/src/lib/client/auth.ts b/packages/admin-next/dashboard/src/lib/client/auth.ts new file mode 100644 index 0000000000000..b94b1bdcd2517 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/auth.ts @@ -0,0 +1,26 @@ +import { EmailPassReq } from "../../types/api-payloads" +import { EmailPassRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +async function emailPass(payload: EmailPassReq) { + return makeRequest("/auth/admin/emailpass", undefined, { + method: "POST", + body: JSON.stringify(payload), + }) +} + +async function login(token: string) { + return makeRequest("/auth/session", undefined, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + }) +} + +export const auth = { + authenticate: { + emailPass, + }, + login, +} diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts new file mode 100644 index 0000000000000..c56ed4e03d398 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -0,0 +1,21 @@ +import { apiKeys } from "./api-keys" +import { auth } from "./auth" +import { currencies } from "./currencies" +import { customers } from "./customers" +import { promotions } from "./promotions" +import { regions } from "./regions" +import { salesChannels } from "./sales-channels" +import { stores } from "./stores" +import { users } from "./users" + +export const client = { + auth: auth, + apiKeys: apiKeys, + customers: customers, + currencies: currencies, + promotions: promotions, + stores: stores, + salesChannels: salesChannels, + users: users, + regions: regions, +} diff --git a/packages/admin-next/dashboard/src/lib/client/common.ts b/packages/admin-next/dashboard/src/lib/client/common.ts new file mode 100644 index 0000000000000..0263560922f59 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/common.ts @@ -0,0 +1,40 @@ +const baseUrl = "http://localhost:9000" + +const commonHeaders: HeadersInit = { + Accept: "application/json", + "Content-Type": "application/json", +} + +function getUrl(path: string, query?: Record) { + const params = query ? new URLSearchParams(query).toString() : null + + return `${baseUrl}${path}${params ? `?${params}` : ""}` +} + +function getOptions(options?: RequestInit): RequestInit { + return { + ...options, + headers: { + ...commonHeaders, + ...options?.headers, + }, + credentials: "include", + } +} + +export async function makeRequest< + R, + Q extends Record | undefined = undefined, +>(path: string, query?: Q, options?: RequestInit): Promise { + const url = getUrl(path, query) + const requestOptions = getOptions(options) + + const response = await fetch(url, requestOptions) + + if (!response.ok) { + const errorData = await response.json() + throw new Error(`API error ${response.status}: ${errorData.message}`) + } + + return response.json() +} diff --git a/packages/admin-next/dashboard/src/lib/client/currencies.ts b/packages/admin-next/dashboard/src/lib/client/currencies.ts new file mode 100644 index 0000000000000..2a0223b78ec08 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/currencies.ts @@ -0,0 +1,18 @@ +import { CurrencyListRes, CurrencyRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +async function retrieveCurrency(id: string) { + return makeRequest(`/admin/currencies/${id}`) +} + +async function listCurrencies(query?: Record) { + return makeRequest>( + "/admin/currencies", + query + ) +} + +export const currencies = { + retrieve: retrieveCurrency, + list: listCurrencies, +} diff --git a/packages/admin-next/dashboard/src/lib/client/customers.ts b/packages/admin-next/dashboard/src/lib/client/customers.ts new file mode 100644 index 0000000000000..f0081dbac13cf --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/customers.ts @@ -0,0 +1,38 @@ +import { CreateCustomerReq, UpdateCustomerReq } from "../../types/api-payloads" +import { CustomerListRes, CustomerRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +async function retrieveCustomer(id: string, query?: Record) { + return makeRequest>( + `/admin/customers/${id}`, + query + ) +} + +async function listCustomers(query?: Record) { + return makeRequest>( + `/admin/customers`, + query + ) +} + +async function createCustomer(payload: CreateCustomerReq) { + return makeRequest(`/admin/customers`, undefined, { + method: "POST", + body: JSON.stringify(payload), + }) +} + +async function updateCustomer(id: string, payload: UpdateCustomerReq) { + return makeRequest(`/admin/customers/${id}`, undefined, { + method: "POST", + body: JSON.stringify(payload), + }) +} + +export const customers = { + retrieve: retrieveCustomer, + list: listCustomers, + create: createCustomer, + update: updateCustomer, +} diff --git a/packages/admin-next/dashboard/src/lib/client/index.ts b/packages/admin-next/dashboard/src/lib/client/index.ts new file mode 100644 index 0000000000000..a5017494f79a8 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/index.ts @@ -0,0 +1 @@ +export * from "./client" diff --git a/packages/admin-next/dashboard/src/lib/client/promotions.ts b/packages/admin-next/dashboard/src/lib/client/promotions.ts new file mode 100644 index 0000000000000..f56b306b5ef5f --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/promotions.ts @@ -0,0 +1,18 @@ +import { AdminGetPromotionsParams } from "@medusajs/medusa" + +import { PromotionRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +const retrievePromotion = async ( + id: string, + query?: AdminGetPromotionsParams +) => { + return makeRequest( + `/admin/promotions/${id}`, + query + ) +} + +export const promotions = { + retrieve: retrievePromotion, +} diff --git a/packages/admin-next/dashboard/src/lib/client/regions.ts b/packages/admin-next/dashboard/src/lib/client/regions.ts new file mode 100644 index 0000000000000..d51736c00071f --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/regions.ts @@ -0,0 +1,49 @@ +import { CreateRegionDTO, UpdateRegionDTO } from "@medusajs/types" +import { + RegionDeleteRes, + RegionListRes, + RegionRes, +} from "../../types/api-responses" +import { makeRequest } from "./common" + +async function retrieveRegion(id: string, query?: Record) { + return makeRequest>( + `/admin/regions/${id}`, + query + ) +} + +async function listRegions(query?: Record) { + return makeRequest>( + `/admin/regions`, + query + ) +} + +async function createRegion(region: CreateRegionDTO) { + return makeRequest(`/admin/regions`, undefined, { + method: "POST", + body: JSON.stringify(region), + }) +} + +async function updateRegion(id: string, region: UpdateRegionDTO) { + return makeRequest(`/admin/regions/${id}`, undefined, { + method: "POST", + body: JSON.stringify(region), + }) +} + +async function deleteRegion(id: string) { + return makeRequest(`/admin/regions/${id}`, undefined, { + method: "DELETE", + }) +} + +export const regions = { + retrieve: retrieveRegion, + list: listRegions, + create: createRegion, + update: updateRegion, + delete: deleteRegion, +} diff --git a/packages/admin-next/dashboard/src/lib/client/sales-channels.ts b/packages/admin-next/dashboard/src/lib/client/sales-channels.ts new file mode 100644 index 0000000000000..c8953695cec07 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/sales-channels.ts @@ -0,0 +1,52 @@ +import { + CreateSalesChannelReq, + UpdateSalesChannelReq, +} from "../../types/api-payloads" +import { SalesChannelListRes, SalesChannelRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +async function retrieveSalesChannel(id: string, query?: Record) { + return makeRequest>( + `/admin/sales-channels/${id}`, + query + ) +} + +async function listSalesChannels(query?: Record) { + return makeRequest>( + `/admin/sales-channels`, + query + ) +} + +async function createSalesChannel(payload: CreateSalesChannelReq) { + return makeRequest(`/admin/sales-channels`, undefined, { + method: "POST", + body: JSON.stringify(payload), + }) +} + +async function updateSalesChannel(id: string, payload: UpdateSalesChannelReq) { + return makeRequest( + `/admin/sales-channels/${id}`, + undefined, + { + method: "POST", + body: JSON.stringify(payload), + } + ) +} + +async function deleteSalesChannel(id: string) { + return makeRequest(`/admin/sales-channels/${id}`, undefined, { + method: "DELETE", + }) +} + +export const salesChannels = { + retrieve: retrieveSalesChannel, + list: listSalesChannels, + create: createSalesChannel, + update: updateSalesChannel, + delete: deleteSalesChannel, +} diff --git a/packages/admin-next/dashboard/src/lib/client/stores.ts b/packages/admin-next/dashboard/src/lib/client/stores.ts new file mode 100644 index 0000000000000..4f789c07e7b1e --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/stores.ts @@ -0,0 +1,30 @@ +import { UpdateStoreReq } from "../../types/api-payloads" +import { StoreListRes, StoreRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +async function retrieveStore(query?: Record): Promise { + const response = await makeRequest>( + "/admin/stores", + query + ) + + const activeStore = response.stores?.[0] + + if (!activeStore) { + throw new Error("No active store found") + } + + return { store: activeStore } +} + +async function updateStore(id: string, payload: UpdateStoreReq) { + return makeRequest(`/admin/stores/${id}`, undefined, { + method: "POST", + body: JSON.stringify(payload), + }) +} + +export const stores = { + retrieve: retrieveStore, + update: updateStore, +} diff --git a/packages/admin-next/dashboard/src/lib/client/users.ts b/packages/admin-next/dashboard/src/lib/client/users.ts new file mode 100644 index 0000000000000..a3ec57ad86f37 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/users.ts @@ -0,0 +1,29 @@ +import { UpdateUserReq } from "../../types/api-payloads" +import { UserListRes, UserRes } from "../../types/api-responses" +import { makeRequest } from "./common" + +async function me() { + return makeRequest("/admin/users/me") +} + +async function retrieveUser(id: string, query?: Record) { + return makeRequest>(`/admin/users/${id}`, query) +} + +async function listUsers(query?: Record) { + return makeRequest(`/admin/users`, undefined, query) +} + +async function updateUser(id: string, payload: UpdateUserReq) { + return makeRequest(`/admin/users/${id}`, undefined, { + method: "POST", + body: JSON.stringify(payload), + }) +} + +export const users = { + me, + retrieve: retrieveUser, + list: listUsers, + update: updateUser, +} diff --git a/packages/admin-next/dashboard/src/lib/query-key-factory.ts b/packages/admin-next/dashboard/src/lib/query-key-factory.ts new file mode 100644 index 0000000000000..df2830166bf52 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/query-key-factory.ts @@ -0,0 +1,45 @@ +import { QueryKey, UseQueryOptions } from "@tanstack/react-query" + +type TQueryKey = { + all: readonly [TKey] + lists: () => readonly [...TQueryKey["all"], "list"] + list: ( + query?: TListQuery + ) => readonly [ + ...ReturnType["lists"]>, + { query: TListQuery | undefined }, + ] + details: () => readonly [...TQueryKey["all"], "detail"] + detail: ( + id: TDetailQuery + ) => readonly [...ReturnType["details"]>, TDetailQuery] +} + +export type UseQueryOptionsWrapper< + // Return type of queryFn + TQueryFn = unknown, + // Type thrown in case the queryFn rejects + E = Error, + // Query key type + TQueryKey extends QueryKey = QueryKey, +> = Omit< + UseQueryOptions, + "queryKey" | "queryFn" +> + +export const queryKeysFactory = < + T, + TListQueryType = any, + TDetailQueryType = string, +>( + globalKey: T +) => { + const queryKeyFactory: TQueryKey = { + all: [globalKey], + lists: () => [...queryKeyFactory.all, "list"], + list: (query?: TListQueryType) => [...queryKeyFactory.lists(), { query }], + details: () => [...queryKeyFactory.all, "detail"], + detail: (id: TDetailQueryType) => [...queryKeyFactory.details(), id], + } + return queryKeyFactory +} diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx index dad60e7e86489..9ccb06e98a987 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx @@ -8,12 +8,12 @@ import { Spinner } from "@medusajs/icons" import { AdminCollectionsRes } from "@medusajs/medusa" import { SalesChannelDTO, UserDTO } from "@medusajs/types" import { ErrorBoundary } from "../../components/error/error-boundary" -import { useV2Session } from "../../lib/api-v2" +import { useMe } from "../../hooks/api/users" import { SearchProvider } from "../search-provider" import { SidebarProvider } from "../sidebar-provider" export const ProtectedRoute = () => { - const { user, isLoading } = useV2Session() + const { user, isLoading } = useMe() const location = useLocation() if (isLoading) { diff --git a/packages/admin-next/dashboard/src/types/api-payloads.ts b/packages/admin-next/dashboard/src/types/api-payloads.ts new file mode 100644 index 0000000000000..45183e28392a1 --- /dev/null +++ b/packages/admin-next/dashboard/src/types/api-payloads.ts @@ -0,0 +1,41 @@ +/** + * Temporary types for API payloads until we export them from `@medusajs/types` + */ + +import { + CreateApiKeyDTO, + CreateCustomerDTO, + CreateRegionDTO, + CreateSalesChannelDTO, + UpdateApiKeyDTO, + UpdateCustomerDTO, + UpdateRegionDTO, + UpdateSalesChannelDTO, + UpdateStoreDTO, + UpdateUserDTO, +} from "@medusajs/types" + +// Auth +export type EmailPassReq = { email: string; password: string } + +// Regions +export type CreateRegionReq = CreateRegionDTO +export type UpdateRegionReq = UpdateRegionDTO + +// Stores +export type UpdateStoreReq = UpdateStoreDTO + +// API Keys +export type CreateApiKeyReq = CreateApiKeyDTO +export type UpdateApiKeyReq = UpdateApiKeyDTO + +// Customers +export type CreateCustomerReq = CreateCustomerDTO +export type UpdateCustomerReq = UpdateCustomerDTO + +// Sales Channels +export type CreateSalesChannelReq = CreateSalesChannelDTO +export type UpdateSalesChannelReq = UpdateSalesChannelDTO + +// Users +export type UpdateUserReq = Omit diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts new file mode 100644 index 0000000000000..fe1dad5297a49 --- /dev/null +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -0,0 +1,71 @@ +/** + * Temporary types for API responses until we export them from `@medusajs/types` + */ + +import { + ApiKeyDTO, + CurrencyDTO, + CustomerDTO, + PromotionDTO, + RegionDTO, + SalesChannelDTO, + StoreDTO, + UserDTO, +} from "@medusajs/types" + +type ListRes = { + count: number + offset: number + limit: number +} + +type DeleteRes = { + id: string + object: string + deleted: true +} + +// Auth +export type EmailPassRes = { token: string } + +// Customers +export type CustomerRes = { customer: CustomerDTO } +export type CustomerListRes = { customers: CustomerDTO[] } & ListRes + +// Promotions +export type PromotionRes = { promotion: PromotionDTO } +export type PromotionListRes = { promotions: PromotionDTO[] } & ListRes +export type PromotionDeleteRes = DeleteRes + +// Users +export type UserRes = { user: UserDTO } +export type UserListRes = { users: UserDTO[] } & ListRes + +// Stores +export type ExtendedStoreDTO = StoreDTO & { + default_currency: CurrencyDTO | null +} + +export type StoreRes = { store: ExtendedStoreDTO } +export type StoreListRes = { stores: ExtendedStoreDTO[] } & ListRes + +// Regions +export type RegionRes = { region: RegionDTO } +export type RegionListRes = { regions: RegionDTO[] } & ListRes +export type RegionDeleteRes = DeleteRes + +// API Keys +export type ApiKeyRes = { api_key: ApiKeyDTO } +export type ApiKeyListRes = { api_keys: ApiKeyDTO[] } & ListRes +export type ApiKeyDeleteRes = DeleteRes + +// Sales Channels +export type SalesChannelRes = { sales_channel: SalesChannelDTO } +export type SalesChannelListRes = { + sales_channels: SalesChannelDTO[] +} & ListRes +export type SalesChannelDeleteRes = DeleteRes + +// Currencies +export type CurrencyRes = { currency: CurrencyDTO } +export type CurrencyListRes = { currencies: CurrencyDTO[] } & ListRes diff --git a/packages/admin-next/dashboard/src/v2-routes/login/login.tsx b/packages/admin-next/dashboard/src/v2-routes/login/login.tsx index 7364d91ec4311..b4739257238f3 100644 --- a/packages/admin-next/dashboard/src/v2-routes/login/login.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/login/login.tsx @@ -7,7 +7,7 @@ import * as z from "zod" import { Form } from "../../components/common/form" import { LogoBox } from "../../components/common/logo-box" -import { useV2LoginAndSetSession } from "../../lib/api-v2" +import { useEmailPassLogin } from "../../hooks/api/auth" import { isAxiosError } from "../../lib/is-axios-error" const LoginSchema = z.object({ @@ -31,7 +31,7 @@ export const Login = () => { }) // TODO: Update when more than emailpass is supported - const { mutateAsync, isLoading } = useV2LoginAndSetSession() + const { mutateAsync, isPending } = useEmailPassLogin() const handleSubmit = form.handleSubmit(async ({ email, password }) => { await mutateAsync( @@ -119,7 +119,7 @@ export const Login = () => { }} /> - diff --git a/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx index 79e5057699157..45bed23b8897a 100644 --- a/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx @@ -1,11 +1,11 @@ +import { UserDTO } from "@medusajs/types" import { Button, Container, Heading, StatusBadge, Text } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { Link } from "react-router-dom" import { languages } from "../../../../../i18n/config" -import { UserDTO } from "@medusajs/types" type ProfileGeneralSectionProps = { - user: Partial> + user: UserDTO } export const ProfileGeneralSection = ({ user }: ProfileGeneralSectionProps) => { diff --git a/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/profile-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/profile-detail.tsx index 2880bcff6e8c5..b1547cf4e1340 100644 --- a/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/profile-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/profile/profile-detail/profile-detail.tsx @@ -1,9 +1,9 @@ import { Outlet, json } from "react-router-dom" +import { useMe } from "../../../hooks/api/users" import { ProfileGeneralSection } from "./components/profile-general-section" -import { useV2Session } from "../../../lib/api-v2" export const ProfileDetail = () => { - const { user, isLoading, isError, error } = useV2Session() + const { user, isLoading, isError, error } = useMe() if (isLoading) { return
Loading...
diff --git a/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx b/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx index 8ff62b4ee2d4e..a636b5322cf6c 100644 --- a/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx @@ -1,18 +1,17 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Button, Input, Select, Switch } from "@medusajs/ui" -import { adminUserKeys, useAdminCustomPost } from "medusa-react" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" import * as zod from "zod" +import { UserDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" +import { useUpdateUser } from "../../../../../hooks/api/users" import { languages } from "../../../../../i18n/config" -import { queryClient } from "../../../../../lib/medusa" -import { UserDTO } from "@medusajs/types" type EditProfileProps = { user: Partial> @@ -48,10 +47,7 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => { a.display_name.localeCompare(b.display_name) ) - const { mutateAsync, isLoading } = useAdminCustomPost( - `/admin/users/${user.id}`, - [...adminUserKeys.lists(), ...adminUserKeys.detail(user.id!)] - ) + const { mutateAsync, isPending } = useUpdateUser(user.id!) const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( @@ -60,12 +56,6 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => { last_name: values.last_name, }, { - onSuccess: () => { - queryClient.invalidateQueries([ - ...adminUserKeys.lists(), - ...adminUserKeys.detail(user.id!), - ]) - }, onError: () => { return }, @@ -196,7 +186,7 @@ export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => { {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/profile-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/profile-edit.tsx index ac7f42225a3c3..c87f734a79bef 100644 --- a/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/profile-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/profile/profile-edit/profile-edit.tsx @@ -1,11 +1,11 @@ import { Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { RouteDrawer } from "../../../components/route-modal" +import { useMe } from "../../../hooks/api/users" import { EditProfileForm } from "./components/edit-profile-form/edit-profile-form" -import { useV2Session } from "../../../lib/api-v2" export const ProfileEdit = () => { - const { user, isLoading, isError, error } = useV2Session() + const { user, isLoading, isError, error } = useMe() const { t } = useTranslation() diff --git a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx index a092e9202dc8a..153fda89d1216 100644 --- a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx @@ -1,5 +1,5 @@ import { Plus, Trash } from "@medusajs/icons" -import { CurrencyDTO, StoreDTO } from "@medusajs/types" +import { CurrencyDTO } from "@medusajs/types" import { Checkbox, CommandBar, @@ -7,20 +7,22 @@ import { Heading, usePrompt, } from "@medusajs/ui" +import { keepPreviousData } from "@tanstack/react-query" import { RowSelectionState, createColumnHelper } from "@tanstack/react-table" import { adminStoreKeys, useAdminCustomPost } from "medusa-react" import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../../components/common/action-menu" import { DataTable } from "../../../../../../components/table/data-table" +import { useCurrencies } from "../../../../../../hooks/api/currencies" +import { useUpdateStore } from "../../../../../../hooks/api/store" import { useDataTable } from "../../../../../../hooks/use-data-table" -import { useV2UpdateStore } from "../../../../../../lib/api-v2" -import { useV2Currencies } from "../../../../../../lib/api-v2/currencies" +import { ExtendedStoreDTO } from "../../../../../../types/api-responses" import { useCurrenciesTableColumns } from "../../../../common/hooks/use-currencies-table-columns" import { useCurrenciesTableQuery } from "../../../../common/hooks/use-currencies-table-query" type StoreCurrencySectionProps = { - store: StoreDTO + store: ExtendedStoreDTO } const PAGE_SIZE = 10 @@ -30,13 +32,13 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => { const { searchParams, raw } = useCurrenciesTableQuery({ pageSize: PAGE_SIZE }) - const { currencies, count, isLoading, isError, error } = useV2Currencies( + const { currencies, count, isLoading, isError, error } = useCurrencies( { code: store.supported_currency_codes, ...searchParams, }, { - keepPreviousData: true, + placeholderData: keepPreviousData, } ) @@ -60,7 +62,7 @@ export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => { }, }) - const { mutateAsync } = useV2UpdateStore(store.id) + const { mutateAsync } = useUpdateStore(store.id) const { t } = useTranslation() const prompt = usePrompt() diff --git a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-general-section/store-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-general-section/store-general-section.tsx index 7cebda57eb464..dcf8821efa8a0 100644 --- a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-general-section/store-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/components/store-general-section/store-general-section.tsx @@ -2,10 +2,10 @@ import { PencilSquare } from "@medusajs/icons" import { Badge, Container, Heading, Text } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../components/common/action-menu" -import { Store } from "../../../../../lib/api-v2/types/store" +import { ExtendedStoreDTO } from "../../../../../types/api-responses" type StoreGeneralSectionProps = { - store: Store + store: ExtendedStoreDTO } export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => { diff --git a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/loader.ts index bc63c693309bb..616e454f5216a 100644 --- a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/loader.ts @@ -1,38 +1,19 @@ -import { Response } from "@medusajs/medusa-js" import { adminStoreKeys } from "medusa-react" -import { redirect } from "react-router-dom" -import { StoreDTO } from "@medusajs/types" -import { FetchQueryOptions } from "@tanstack/react-query" -import { medusa, queryClient } from "../../../lib/medusa" +import { client } from "../../../lib/client" +import { queryClient } from "../../../lib/medusa" +import { StoreRes } from "../../../types/api-responses" const storeDetailQuery = () => ({ queryKey: adminStoreKeys.details(), - queryFn: async () => medusa.admin.custom.get("/stores"), + queryFn: async () => client.stores.retrieve(), }) -const fetchQuery = async ( - query: FetchQueryOptions> -) => { - try { - const res = await queryClient.fetchQuery(query) - // TODO: Reconsider store retrieval - return res - } catch (error) { - const err = error ? JSON.parse(JSON.stringify(error)) : null - - if ((err as Error & { status: number })?.status === 401) { - redirect("/login", 401) - } - } -} - export const storeLoader = async () => { const query = storeDetailQuery() return ( - queryClient.getQueryData>( - query.queryKey - ) ?? (await fetchQuery(query)) + queryClient.getQueryData(query.queryKey) ?? + (await queryClient.fetchQuery(query)) ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/store-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/store-detail.tsx index 919d2967c7058..c028740ebcfef 100644 --- a/packages/admin-next/dashboard/src/v2-routes/store/store-detail/store-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/store/store-detail/store-detail.tsx @@ -1,7 +1,7 @@ import { Outlet, useLoaderData } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" -import { useV2Store } from "../../../lib/api-v2" +import { useStore } from "../../../hooks/api/store" import { StoreCurrencySection } from "./components/store-currency-section/store-currencies-section.tsx" import { StoreGeneralSection } from "./components/store-general-section" import { storeLoader } from "./loader" @@ -9,8 +9,8 @@ import { storeLoader } from "./loader" export const StoreDetail = () => { const initialData = useLoaderData() as Awaited> - const { store, isLoading, isError, error } = useV2Store({ - initialData: initialData, + const { store, isLoading, isError, error } = useStore({ + initialData, }) if (isLoading || !store) { diff --git a/packages/admin-next/dashboard/src/v2-routes/store/store-edit/components/edit-store-form/edit-store-form.tsx b/packages/admin-next/dashboard/src/v2-routes/store/store-edit/components/edit-store-form/edit-store-form.tsx index 4b4ee7246373d..4dc5c1e7364b1 100644 --- a/packages/admin-next/dashboard/src/v2-routes/store/store-edit/components/edit-store-form/edit-store-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/store/store-edit/components/edit-store-form/edit-store-form.tsx @@ -1,5 +1,4 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { StoreDTO } from "@medusajs/types" import { Button, Input } from "@medusajs/ui" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" @@ -10,10 +9,11 @@ import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" -import { useV2UpdateStore } from "../../../../../lib/api-v2" +import { useUpdateStore } from "../../../../../hooks/api/store" +import { ExtendedStoreDTO } from "../../../../../types/api-responses" type EditStoreFormProps = { - store: StoreDTO + store: ExtendedStoreDTO } const EditStoreSchema = z.object({ @@ -34,7 +34,7 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => { resolver: zodResolver(EditStoreSchema), }) - const { mutateAsync, isLoading } = useV2UpdateStore(store.id) + const { mutateAsync, isPending } = useUpdateStore(store.id) const handleSubmit = form.handleSubmit(async (values) => { mutateAsync(values, { @@ -72,7 +72,7 @@ export const EditStoreForm = ({ store }: EditStoreFormProps) => { {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/v2-routes/store/store-edit/store-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/store/store-edit/store-edit.tsx index a1412d944c9de..044cf004fe7a8 100644 --- a/packages/admin-next/dashboard/src/v2-routes/store/store-edit/store-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/store/store-edit/store-edit.tsx @@ -1,12 +1,12 @@ import { Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { RouteDrawer } from "../../../components/route-modal" -import { useV2Store } from "../../../lib/api-v2" +import { useStore } from "../../../hooks/api/store" import { EditStoreForm } from "./components/edit-store-form/edit-store-form" export const StoreEdit = () => { const { t } = useTranslation() - const { store, isLoading, isError, error } = useV2Store() + const { store, isLoading, isError, error } = useStore() if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx index 14d11e3f1c255..6aa6437952ebc 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx @@ -1,6 +1,7 @@ import { PencilSquare } from "@medusajs/icons" -import { User } from "@medusajs/medusa" +import { UserDTO } from "@medusajs/types" import { Button, Container, Heading, Table, clx } from "@medusajs/ui" +import { keepPreviousData } from "@tanstack/react-query" import { PaginationState, createColumnHelper, @@ -8,7 +9,6 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table" -import { useAdminUsers } from "medusa-react" import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { Link, useNavigate } from "react-router-dom" @@ -20,6 +20,7 @@ import { import { OrderBy } from "../../../../../components/filtering/order-by" import { Query } from "../../../../../components/filtering/query" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +import { useUsers } from "../../../../../hooks/api/users" import { useQueryParams } from "../../../../../hooks/use-query-params" const PAGE_SIZE = 50 @@ -39,14 +40,14 @@ export const UserListTable = () => { ) const params = useQueryParams(["q", "order"]) - const { users, count, isLoading, isError, error } = useAdminUsers( + const { users, count, isLoading, isError, error } = useUsers( { limit: PAGE_SIZE, offset: pageIndex * PAGE_SIZE, ...params, }, { - keepPreviousData: true, + placeholderData: keepPreviousData, } ) @@ -174,7 +175,7 @@ export const UserListTable = () => { ) } -const UserActions = ({ user }: { user: Omit }) => { +const UserActions = ({ user }: { user: UserDTO }) => { const { t } = useTranslation() return ( @@ -194,7 +195,7 @@ const UserActions = ({ user }: { user: Omit }) => { ) } -const columnHelper = createColumnHelper>() +const columnHelper = createColumnHelper() const useColumns = () => { const { t } = useTranslation() From b31257c9a603cf8be7d50596b5ac3daac447e4f0 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 15:03:15 +0200 Subject: [PATCH 2/7] add final hooks and fixes --- packages/admin-next/dashboard/package.json | 1 + .../public/locales/en-US/translation.json | 2 +- .../product-table-cells.tsx | 18 +- .../collection-cell/collection-cell.tsx | 4 +- .../product/product-cell/product-cell.tsx | 5 +- .../sales-channels-cell.tsx | 4 +- .../product/variant-cell/variant-cell.tsx | 4 +- .../dashboard/src/hooks/api/invites.tsx | 94 ++++ .../dashboard/src/hooks/api/product-types.tsx | 40 ++ .../dashboard/src/hooks/api/products.tsx | 40 ++ .../src/hooks/api/sales-channels.tsx | 106 ++++- .../dashboard/src/hooks/api/users.tsx | 50 ++- .../columns/use-product-table-columns.tsx | 8 +- .../use-sales-channel-table-columns.tsx | 4 +- .../filters/use-product-table-filters.tsx | 137 +++--- .../query/use-sales-channel-table-query.tsx | 6 +- .../dashboard/src/lib/client/api-keys.ts | 12 +- .../dashboard/src/lib/client/auth.ts | 10 +- .../dashboard/src/lib/client/client.ts | 6 + .../dashboard/src/lib/client/common.ts | 81 +++- .../dashboard/src/lib/client/currencies.ts | 11 +- .../dashboard/src/lib/client/customers.ts | 22 +- .../dashboard/src/lib/client/invites.ts | 38 ++ .../dashboard/src/lib/client/product-types.ts | 15 + .../dashboard/src/lib/client/products.ts | 15 + .../dashboard/src/lib/client/promotions.ts | 4 +- .../dashboard/src/lib/client/regions.ts | 30 +- .../src/lib/client/sales-channels.ts | 54 ++- .../dashboard/src/lib/client/stores.ts | 13 +- .../dashboard/src/lib/client/users.ts | 20 +- .../components/sales-channel-list-table.tsx | 260 ----------- .../src/providers/router-provider/v1.tsx | 80 ---- .../add-products-to-sales-channel-form.tsx | 354 --------------- .../components/index.ts | 1 - .../sales-channel-add-products/index.ts | 1 - .../sales-channel-add-products.tsx | 21 - .../sales-channel-create/index.ts | 1 - .../sales-channel-create.tsx | 10 - .../sales-channel-product-section/index.ts | 1 - .../sales-channel-product-section.tsx | 349 --------------- .../sales-channel-detail/index.ts | 2 - .../sales-channel-detail/loader.ts | 21 - .../sales-channel-detail.tsx | 33 -- .../sales-channel-edit/index.ts | 1 - .../sales-channel-edit/sales-channel-edit.tsx | 31 -- .../sales-channel-list/index.ts | 1 - .../sales-channel-list/sales-channel-list.tsx | 11 - .../components/user-general-section/index.ts | 1 - .../user-general-section.tsx | 96 ---- .../src/routes/users/user-detail/index.ts | 2 - .../src/routes/users/user-detail/loader.ts | 21 - .../routes/users/user-detail/user-detail.tsx | 34 -- .../src/routes/users/user-edit/index.ts | 1 - .../invite-user-form/invite-user-form.tsx | 417 ------------------ .../src/routes/users/user-invite/index.ts | 1 - .../routes/users/user-invite/user-invite.tsx | 10 - .../components/user-list-table/index.ts | 1 - .../user-list-table/user-list-table.tsx | 239 ---------- .../src/routes/users/user-list/index.ts | 1 - .../src/routes/users/user-list/user-list.tsx | 11 - .../dashboard/src/types/api-payloads.ts | 6 + .../dashboard/src/types/api-responses.ts | 27 ++ .../add-products-to-sales-channel-form.tsx | 258 +++-------- .../sales-channel-add-products.tsx | 4 +- .../create-sales-channel-form.tsx | 6 +- .../create-sales-channel-form/index.ts | 0 .../sales-channel-create.tsx | 2 +- .../sales-channel-general-section/index.ts | 0 .../sales-channel-general-section.tsx | 29 +- .../sales-channel-product-section.tsx | 272 +++--------- .../sales-channel-detail/loader.ts | 10 +- .../sales-channel-detail.tsx | 6 +- .../edit-sales-channel-form.tsx | 10 +- .../edit-sales-channel-form/index.ts | 0 .../sales-channel-edit/sales-channel-edit.tsx | 6 +- .../sales-channel-list/components/index.ts | 0 .../components/sales-channel-list-table.tsx | 146 ++++++ .../sales-channel-list/sales-channel-list.tsx | 2 +- .../user-general-section.tsx | 8 +- .../src/v2-routes/users/user-detail/loader.ts | 10 +- .../users/user-detail/user-detail.tsx | 4 +- .../edit-user-form/edit-user-form.tsx | 10 +- .../components/edit-user-form/index.ts | 0 .../src/v2-routes/users/user-edit/index.ts | 2 +- .../users/user-edit/user-edit.tsx} | 4 +- .../invite-user-form/invite-user-form.tsx | 67 +-- .../use-user-table-columns.tsx | 42 ++ .../user-list-table/use-user-table-query.tsx | 24 + .../user-list-table/user-list-table.tsx | 231 ++-------- .../user-list-table/user-row-actions.tsx | 24 + yarn.lock | 125 +++++- 91 files changed, 1268 insertions(+), 2934 deletions(-) create mode 100644 packages/admin-next/dashboard/src/hooks/api/invites.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/product-types.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/products.tsx create mode 100644 packages/admin-next/dashboard/src/lib/client/invites.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/product-types.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/products.ts delete mode 100644 packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/sales-channel-list.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/user-general-section.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-detail/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-detail/loader.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-detail/user-detail.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-edit/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-invite/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/user-list-table.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-list/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/users/user-list/user-list.tsx rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx (95%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-create/components/create-sales-channel-form/index.ts (100%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-detail/components/sales-channel-general-section/index.ts (100%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx (74%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx (92%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-edit/components/edit-sales-channel-form/index.ts (100%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/sales-channels/sales-channel-list/components/index.ts (100%) create mode 100644 packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx rename packages/admin-next/dashboard/src/{modules/user => v2-routes/users}/user-edit/components/edit-user-form/edit-user-form.tsx (91%) rename packages/admin-next/dashboard/src/{modules/user => v2-routes/users}/user-edit/components/edit-user-form/index.ts (100%) rename packages/admin-next/dashboard/src/{modules/user/user-edit/index.tsx => v2-routes/users/user-edit/user-edit.tsx} (84%) create mode 100644 packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-columns.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-query.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-row-actions.tsx diff --git a/packages/admin-next/dashboard/package.json b/packages/admin-next/dashboard/package.json index dd51aec14d43c..85c4f11ae24ed 100644 --- a/packages/admin-next/dashboard/package.json +++ b/packages/admin-next/dashboard/package.json @@ -35,6 +35,7 @@ "i18next-http-backend": "2.4.2", "match-sorter": "^6.3.4", "medusa-react": "workspace:^", + "qs": "^6.12.0", "react": "18.2.0", "react-country-flag": "^3.1.0", "react-dom": "18.2.0", diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index d5eeb6458b787..e19132bd8a99d 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -640,7 +640,7 @@ "inviteUserHint": "Invite a new user to your store.", "sendInvite": "Send invite", "pendingInvites": "Pending Invites", - "revokeInviteWarning": "You are about to revoke the invite for {{email}}. This action cannot be undone.", + "deleteInviteWarning": "You are about to delete the invite for {{email}}. This action cannot be undone.", "resendInvite": "Resend invite", "copyInviteLink": "Copy invite link", "expiredOnDate": "Expired on {{date}}", diff --git a/packages/admin-next/dashboard/src/components/common/product-table-cells/product-table-cells.tsx b/packages/admin-next/dashboard/src/components/common/product-table-cells/product-table-cells.tsx index cd290cf66209d..f29d4d8abdcdc 100644 --- a/packages/admin-next/dashboard/src/components/common/product-table-cells/product-table-cells.tsx +++ b/packages/admin-next/dashboard/src/components/common/product-table-cells/product-table-cells.tsx @@ -1,9 +1,9 @@ +import { SalesChannel } from "@medusajs/medusa" import { - Product, - ProductCollection, - ProductVariant, - SalesChannel, -} from "@medusajs/medusa" + ProductCollectionDTO, + ProductDTO, + ProductVariantDTO, +} from "@medusajs/types" import { StatusBadge, Text } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { Thumbnail } from "../thumbnail" @@ -11,7 +11,7 @@ import { Thumbnail } from "../thumbnail" export const ProductVariantCell = ({ variants, }: { - variants: ProductVariant[] | null + variants: ProductVariantDTO[] | null }) => { const { t } = useTranslation() @@ -35,7 +35,7 @@ export const ProductVariantCell = ({ export const ProductStatusCell = ({ status, }: { - status: Product["status"] + status: ProductDTO["status"] }) => { const { t } = useTranslation() @@ -95,7 +95,7 @@ export const ProductAvailabilityCell = ({ ) } -export const ProductTitleCell = ({ product }: { product: Product }) => { +export const ProductTitleCell = ({ product }: { product: ProductDTO }) => { const thumbnail = product.thumbnail const title = product.title @@ -112,7 +112,7 @@ export const ProductTitleCell = ({ product }: { product: Product }) => { export const ProductCollectionCell = ({ collection, }: { - collection: ProductCollection | null + collection: ProductCollectionDTO | null }) => { if (!collection) { return ( diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/product/collection-cell/collection-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/product/collection-cell/collection-cell.tsx index 3d714b29b11b5..32dd7311109a6 100644 --- a/packages/admin-next/dashboard/src/components/table/table-cells/product/collection-cell/collection-cell.tsx +++ b/packages/admin-next/dashboard/src/components/table/table-cells/product/collection-cell/collection-cell.tsx @@ -1,10 +1,10 @@ -import type { ProductCollection } from "@medusajs/medusa" import { useTranslation } from "react-i18next" +import { ProductCollectionDTO } from "@medusajs/types" import { PlaceholderCell } from "../../common/placeholder-cell" type CollectionCellProps = { - collection?: ProductCollection | null + collection?: ProductCollectionDTO | null } export const CollectionCell = ({ collection }: CollectionCellProps) => { diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/product/product-cell/product-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/product/product-cell/product-cell.tsx index d6e974993b1ca..eba70e2d932f3 100644 --- a/packages/admin-next/dashboard/src/components/table/table-cells/product/product-cell/product-cell.tsx +++ b/packages/admin-next/dashboard/src/components/table/table-cells/product/product-cell/product-cell.tsx @@ -1,11 +1,10 @@ -import type { Product } from "@medusajs/medusa" -import type { PricedProduct } from "@medusajs/medusa/dist/types/pricing" import { useTranslation } from "react-i18next" +import { ProductDTO } from "@medusajs/types" import { Thumbnail } from "../../../../common/thumbnail" type ProductCellProps = { - product: Product | PricedProduct + product: ProductDTO } export const ProductCell = ({ product }: ProductCellProps) => { diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/product/sales-channels-cell/sales-channels-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/product/sales-channels-cell/sales-channels-cell.tsx index af1408c386418..806decac0c428 100644 --- a/packages/admin-next/dashboard/src/components/table/table-cells/product/sales-channels-cell/sales-channels-cell.tsx +++ b/packages/admin-next/dashboard/src/components/table/table-cells/product/sales-channels-cell/sales-channels-cell.tsx @@ -1,11 +1,11 @@ -import type { SalesChannel } from "@medusajs/medusa" import { Tooltip } from "@medusajs/ui" import { useTranslation } from "react-i18next" +import { SalesChannelDTO } from "@medusajs/types" import { PlaceholderCell } from "../../common/placeholder-cell" type SalesChannelsCellProps = { - salesChannels?: SalesChannel[] | null + salesChannels?: SalesChannelDTO[] | null } export const SalesChannelsCell = ({ diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/product/variant-cell/variant-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/product/variant-cell/variant-cell.tsx index dce5d0a80f0ac..3a3b72907c413 100644 --- a/packages/admin-next/dashboard/src/components/table/table-cells/product/variant-cell/variant-cell.tsx +++ b/packages/admin-next/dashboard/src/components/table/table-cells/product/variant-cell/variant-cell.tsx @@ -1,10 +1,10 @@ -import { ProductVariant } from "@medusajs/medusa" import { useTranslation } from "react-i18next" +import { ProductVariantDTO } from "@medusajs/types" import { PlaceholderCell } from "../../common/placeholder-cell" type VariantCellProps = { - variants?: ProductVariant[] | null + variants?: ProductVariantDTO[] | null } export const VariantCell = ({ variants }: VariantCellProps) => { diff --git a/packages/admin-next/dashboard/src/hooks/api/invites.tsx b/packages/admin-next/dashboard/src/hooks/api/invites.tsx new file mode 100644 index 0000000000000..9058b9f55842a --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/invites.tsx @@ -0,0 +1,94 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { CreateInviteReq } from "../../types/api-payloads" +import { + InviteDeleteRes, + InviteListRes, + InviteRes, +} from "../../types/api-responses" + +const INVITES_QUERY_KEY = "invites" as const +const invitesQueryKeys = queryKeysFactory(INVITES_QUERY_KEY) + +export const useInvite = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: invitesQueryKeys.detail(id), + queryFn: async () => client.invites.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useInvites = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.invites.list(query), + queryKey: invitesQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateInvite = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.invites.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: invitesQueryKeys.lists() }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useResendInvite = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.invites.resend(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: invitesQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: invitesQueryKeys.detail(id) }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteInvite = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.invites.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: invitesQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: invitesQueryKeys.detail(id) }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/product-types.tsx b/packages/admin-next/dashboard/src/hooks/api/product-types.tsx new file mode 100644 index 0000000000000..6529f5a34218f --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/product-types.tsx @@ -0,0 +1,40 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { ProductTypeListRes, ProductTypeRes } from "../../types/api-responses" + +const PRODUCT_TYPES_QUERY_KEY = "product_types" as const +const productTypesQueryKeys = queryKeysFactory(PRODUCT_TYPES_QUERY_KEY) + +export const useProductType = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.productTypes.retrieve(id, query), + queryKey: productTypesQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useProductTypes = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.productTypes.list(query), + queryKey: productTypesQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/hooks/api/products.tsx b/packages/admin-next/dashboard/src/hooks/api/products.tsx new file mode 100644 index 0000000000000..635fd5fc62bd1 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/products.tsx @@ -0,0 +1,40 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { ProductListRes, ProductRes } from "../../types/api-responses" + +const PRODUCTS_QUERY_KEY = "products" as const +export const productsQueryKeys = queryKeysFactory(PRODUCTS_QUERY_KEY) + +export const useProduct = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.products.retrieve(id, query), + queryKey: productsQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useProducts = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.products.list(query), + queryKey: productsQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx b/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx index dae42172a18f0..b8c98513a72c8 100644 --- a/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/sales-channels.tsx @@ -10,10 +10,17 @@ import { client } from "../../lib/client" import { queryClient } from "../../lib/medusa" import { queryKeysFactory } from "../../lib/query-key-factory" import { + AddProductsSalesChannelReq, CreateSalesChannelReq, + RemoveProductsSalesChannelReq, UpdateSalesChannelReq, } from "../../types/api-payloads" -import { SalesChannelListRes, SalesChannelRes } from "../../types/api-responses" +import { + SalesChannelDeleteRes, + SalesChannelListRes, + SalesChannelRes, +} from "../../types/api-responses" +import { productsQueryKeys } from "./products" const SALES_CHANNELS_QUERY_KEY = "sales-channels" as const const salesChannelsQueryKeys = queryKeysFactory(SALES_CHANNELS_QUERY_KEY) @@ -84,3 +91,100 @@ export const useUpdateSalesChannel = ( ...options, }) } + +export const useDeleteSalesChannel = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.salesChannels.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.lists(), + }) + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.detail(id), + }) + + // Invalidate all products to ensure they are updated if they were linked to the sales channel + queryClient.invalidateQueries({ + queryKey: productsQueryKeys.all, + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useSalesChannelRemoveProducts = ( + id: string, + options?: UseMutationOptions< + SalesChannelRes, + Error, + RemoveProductsSalesChannelReq + > +) => { + return useMutation({ + mutationFn: (payload) => client.salesChannels.removeProducts(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.lists(), + }) + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.detail(id), + }) + + // Invalidate the products that were removed + for (const product of variables?.product_ids || []) { + queryClient.invalidateQueries({ + queryKey: productsQueryKeys.detail(product), + }) + } + + // Invalidate the products list query + queryClient.invalidateQueries({ + queryKey: productsQueryKeys.lists(), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useSalesChannelAddProducts = ( + id: string, + options?: UseMutationOptions< + SalesChannelRes, + Error, + AddProductsSalesChannelReq + > +) => { + return useMutation({ + mutationFn: (payload) => client.salesChannels.addProducts(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.lists(), + }) + queryClient.invalidateQueries({ + queryKey: salesChannelsQueryKeys.detail(id), + }) + + // Invalidate the products that were removed + for (const product of variables?.product_ids || []) { + queryClient.invalidateQueries({ + queryKey: productsQueryKeys.detail(product), + }) + } + + // Invalidate the products list query + queryClient.invalidateQueries({ + queryKey: productsQueryKeys.lists(), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/users.tsx b/packages/admin-next/dashboard/src/hooks/api/users.tsx index 328f2935f46c1..6d3f33ebb0663 100644 --- a/packages/admin-next/dashboard/src/hooks/api/users.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/users.tsx @@ -1,15 +1,15 @@ import { - QueryKey, - UseMutationOptions, - UseQueryOptions, - useMutation, - useQuery, + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, } from "@tanstack/react-query" import { client } from "../../lib/client" import { queryClient } from "../../lib/medusa" import { queryKeysFactory } from "../../lib/query-key-factory" import { UpdateUserReq } from "../../types/api-payloads" -import { UserListRes, UserRes } from "../../types/api-responses" +import { UserDeleteRes, UserListRes, UserRes } from "../../types/api-responses" const USERS_QUERY_KEY = "users" as const const usersQueryKeys = { @@ -33,12 +33,21 @@ export const useMe = ( } export const useUser = ( - id: string, - query?: Record, - options?: Omit< - UseQueryOptions, - "queryFn" | "queryKey"> + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > ) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.users.retrieve(id, query), + queryKey: usersQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} export const useUsers = ( query?: Record, @@ -74,3 +83,22 @@ export const useUpdateUser = ( ...options, }) } + +export const useDeleteUser = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.users.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: usersQueryKeys.detail(id) }) + queryClient.invalidateQueries({ queryKey: usersQueryKeys.lists() }) + + // We invalidate the me query in case the user updates their own profile + queryClient.invalidateQueries({ queryKey: usersQueryKeys.me() }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/table/columns/use-product-table-columns.tsx b/packages/admin-next/dashboard/src/hooks/table/columns/use-product-table-columns.tsx index f8e990320961a..115da546e4d4e 100644 --- a/packages/admin-next/dashboard/src/hooks/table/columns/use-product-table-columns.tsx +++ b/packages/admin-next/dashboard/src/hooks/table/columns/use-product-table-columns.tsx @@ -1,5 +1,4 @@ -import { Product } from "@medusajs/medusa" -import { ColumnDef, createColumnHelper } from "@tanstack/react-table" +import { createColumnHelper } from "@tanstack/react-table" import { useMemo } from "react" import { @@ -22,8 +21,9 @@ import { VariantCell, VariantHeader, } from "../../../components/table/table-cells/product/variant-cell" +import { ExtendedProductDTO } from "../../../types/api-responses" -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() export const useProductTableColumns = () => { return useMemo( @@ -55,5 +55,5 @@ export const useProductTableColumns = () => { }), ], [] - ) as ColumnDef[] + ) } diff --git a/packages/admin-next/dashboard/src/hooks/table/columns/use-sales-channel-table-columns.tsx b/packages/admin-next/dashboard/src/hooks/table/columns/use-sales-channel-table-columns.tsx index 9dac4356c10e4..38ec913a79156 100644 --- a/packages/admin-next/dashboard/src/hooks/table/columns/use-sales-channel-table-columns.tsx +++ b/packages/admin-next/dashboard/src/hooks/table/columns/use-sales-channel-table-columns.tsx @@ -1,6 +1,6 @@ -import { SalesChannel } from "@medusajs/medusa" import { createColumnHelper } from "@tanstack/react-table" +import { SalesChannelDTO } from "@medusajs/types" import { useMemo } from "react" import { DescriptionCell, @@ -11,7 +11,7 @@ import { NameHeader, } from "../../../components/table/table-cells/sales-channel/name-cell" -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() export const useSalesChannelTableColumns = () => { return useMemo( diff --git a/packages/admin-next/dashboard/src/hooks/table/filters/use-product-table-filters.tsx b/packages/admin-next/dashboard/src/hooks/table/filters/use-product-table-filters.tsx index df9b58cb36b06..a89c4b7ca8943 100644 --- a/packages/admin-next/dashboard/src/hooks/table/filters/use-product-table-filters.tsx +++ b/packages/admin-next/dashboard/src/hooks/table/filters/use-product-table-filters.tsx @@ -1,12 +1,7 @@ -import { - useAdminCollections, - useAdminProductCategories, - useAdminProductTags, - useAdminProductTypes, - useAdminSalesChannels, -} from "medusa-react" import { useTranslation } from "react-i18next" import { Filter } from "../../../components/table/data-table" +import { useProductTypes } from "../../api/product-types" +import { useSalesChannels } from "../../api/sales-channels" const excludeableFields = ["sales_channel_id", "collections"] as const @@ -15,19 +10,19 @@ export const useProductTableFilters = ( ) => { const { t } = useTranslation() - const { product_types } = useAdminProductTypes({ + const { product_types } = useProductTypes({ limit: 1000, offset: 0, }) - const { product_tags } = useAdminProductTags({ - limit: 1000, - offset: 0, - }) + // const { product_tags } = useAdminProductTags({ + // limit: 1000, + // offset: 0, + // }) const isSalesChannelExcluded = exclude?.includes("sales_channel_id") - const { sales_channels } = useAdminSalesChannels( + const { sales_channels } = useSalesChannels( { limit: 1000, fields: "id,name", @@ -38,24 +33,24 @@ export const useProductTableFilters = ( } ) - const { product_categories } = useAdminProductCategories({ - limit: 1000, - offset: 0, - fields: "id,name", - expand: "", - }) + // const { product_categories } = useAdminProductCategories({ + // limit: 1000, + // offset: 0, + // fields: "id,name", + // expand: "", + // }) const isCollectionExcluded = exclude?.includes("collections") - const { collections } = useAdminCollections( - { - limit: 1000, - offset: 0, - }, - { - enabled: !isCollectionExcluded, - } - ) + // const { collections } = useAdminCollections( + // { + // limit: 1000, + // offset: 0, + // }, + // { + // enabled: !isCollectionExcluded, + // } + // ) let filters: Filter[] = [] @@ -74,20 +69,20 @@ export const useProductTableFilters = ( filters = [...filters, typeFilter] } - if (product_tags) { - const tagFilter: Filter = { - key: "tags", - label: t("fields.tag"), - type: "select", - multiple: true, - options: product_tags.map((t) => ({ - label: t.value, - value: t.id, - })), - } - - filters = [...filters, tagFilter] - } + // if (product_tags) { + // const tagFilter: Filter = { + // key: "tags", + // label: t("fields.tag"), + // type: "select", + // multiple: true, + // options: product_tags.map((t) => ({ + // label: t.value, + // value: t.id, + // })), + // } + + // filters = [...filters, tagFilter] + // } if (sales_channels) { const salesChannelFilter: Filter = { @@ -104,35 +99,35 @@ export const useProductTableFilters = ( filters = [...filters, salesChannelFilter] } - if (product_categories) { - const categoryFilter: Filter = { - key: "category_id", - label: t("fields.category"), - type: "select", - multiple: true, - options: product_categories.map((c) => ({ - label: c.name, - value: c.id, - })), - } - - filters = [...filters, categoryFilter] - } - - if (collections) { - const collectionFilter: Filter = { - key: "collection_id", - label: t("fields.collection"), - type: "select", - multiple: true, - options: collections.map((c) => ({ - label: c.title, - value: c.id, - })), - } - - filters = [...filters, collectionFilter] - } + // if (product_categories) { + // const categoryFilter: Filter = { + // key: "category_id", + // label: t("fields.category"), + // type: "select", + // multiple: true, + // options: product_categories.map((c) => ({ + // label: c.name, + // value: c.id, + // })), + // } + + // filters = [...filters, categoryFilter] + // } + + // if (collections) { + // const collectionFilter: Filter = { + // key: "collection_id", + // label: t("fields.collection"), + // type: "select", + // multiple: true, + // options: collections.map((c) => ({ + // label: c.title, + // value: c.id, + // })), + // } + + // filters = [...filters, collectionFilter] + // } const giftCardFilter: Filter = { key: "is_giftcard", diff --git a/packages/admin-next/dashboard/src/hooks/table/query/use-sales-channel-table-query.tsx b/packages/admin-next/dashboard/src/hooks/table/query/use-sales-channel-table-query.tsx index fa2193260579e..222b4199ccd8e 100644 --- a/packages/admin-next/dashboard/src/hooks/table/query/use-sales-channel-table-query.tsx +++ b/packages/admin-next/dashboard/src/hooks/table/query/use-sales-channel-table-query.tsx @@ -21,9 +21,9 @@ export const useSalesChannelTableQuery = ({ limit: pageSize, offset: offset ? Number(offset) : 0, order, - created_at: created_at ? JSON.parse(created_at) : undefined, - updated_at: updated_at ? JSON.parse(updated_at) : undefined, - q, + // created_at: created_at ? JSON.parse(created_at) : undefined, + // updated_at: updated_at ? JSON.parse(updated_at) : undefined, + // q, // Re-enable when params are fixed } return { diff --git a/packages/admin-next/dashboard/src/lib/client/api-keys.ts b/packages/admin-next/dashboard/src/lib/client/api-keys.ts index c2f223bbf71d7..0f43fa001db99 100644 --- a/packages/admin-next/dashboard/src/lib/client/api-keys.ts +++ b/packages/admin-next/dashboard/src/lib/client/api-keys.ts @@ -1,18 +1,12 @@ import { ApiKeyListRes, ApiKeyRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { getRequest } from "./common" const retrieveApiKey = async (id: string, query?: Record) => { - return makeRequest>( - `/admin/api-keys/${id}`, - query - ) + return getRequest(`/admin/api-keys/${id}`, query) } const listApiKeys = async (query?: Record) => { - return makeRequest>( - `/admin/api-keys`, - query - ) + return getRequest(`/admin/api-keys`, query) } export const apiKeys = { diff --git a/packages/admin-next/dashboard/src/lib/client/auth.ts b/packages/admin-next/dashboard/src/lib/client/auth.ts index b94b1bdcd2517..9387f9672d1e2 100644 --- a/packages/admin-next/dashboard/src/lib/client/auth.ts +++ b/packages/admin-next/dashboard/src/lib/client/auth.ts @@ -1,17 +1,13 @@ import { EmailPassReq } from "../../types/api-payloads" import { EmailPassRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { postRequest } from "./common" async function emailPass(payload: EmailPassReq) { - return makeRequest("/auth/admin/emailpass", undefined, { - method: "POST", - body: JSON.stringify(payload), - }) + return postRequest("/auth/admin/emailpass", payload) } async function login(token: string) { - return makeRequest("/auth/session", undefined, { - method: "POST", + return postRequest("/auth/session", undefined, { headers: { Authorization: `Bearer ${token}`, }, diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index c56ed4e03d398..9263134603f20 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -2,6 +2,9 @@ import { apiKeys } from "./api-keys" import { auth } from "./auth" import { currencies } from "./currencies" import { customers } from "./customers" +import { invites } from "./invites" +import { productTypes } from "./product-types" +import { products } from "./products" import { promotions } from "./promotions" import { regions } from "./regions" import { salesChannels } from "./sales-channels" @@ -18,4 +21,7 @@ export const client = { salesChannels: salesChannels, users: users, regions: regions, + invites: invites, + products: products, + productTypes: productTypes, } diff --git a/packages/admin-next/dashboard/src/lib/client/common.ts b/packages/admin-next/dashboard/src/lib/client/common.ts index 0263560922f59..eeb124d159dd6 100644 --- a/packages/admin-next/dashboard/src/lib/client/common.ts +++ b/packages/admin-next/dashboard/src/lib/client/common.ts @@ -1,3 +1,5 @@ +import { stringify } from "qs" + const baseUrl = "http://localhost:9000" const commonHeaders: HeadersInit = { @@ -6,35 +8,100 @@ const commonHeaders: HeadersInit = { } function getUrl(path: string, query?: Record) { - const params = query ? new URLSearchParams(query).toString() : null + const params = query ? stringify(query) : null return `${baseUrl}${path}${params ? `?${params}` : ""}` } -function getOptions(options?: RequestInit): RequestInit { +function getBody(payload?: Record) { + return payload ? JSON.stringify(payload) : undefined +} + +function getOptions( + options?: Omit, + payload?: Record +): RequestInit { + const body = getBody(payload) + return { ...options, headers: { ...commonHeaders, ...options?.headers, }, + body, credentials: "include", } } -export async function makeRequest< - R, - Q extends Record | undefined = undefined, ->(path: string, query?: Q, options?: RequestInit): Promise { +async function makeRequest< + TRes, + TPayload extends Record | undefined, + TQuery extends Record | undefined = undefined, +>( + path: string, + payload?: TPayload, + query?: TQuery, + options?: Omit +): Promise { const url = getUrl(path, query) - const requestOptions = getOptions(options) + const requestOptions = getOptions(options, payload) const response = await fetch(url, requestOptions) if (!response.ok) { const errorData = await response.json() + // Temp: Add a better error type throw new Error(`API error ${response.status}: ${errorData.message}`) } return response.json() } + +export async function getRequest< + TRes, + TQuery extends Record | undefined = {}, +>( + path: string, + query?: TQuery, + options?: Omit +): Promise { + return makeRequest>( + path, + undefined, + query, + { + ...options, + method: "GET", + } + ) +} + +export async function postRequest< + TRes, + TPayload extends Record | undefined = {}, +>( + path: string, + payload?: TPayload, + options?: Omit +): Promise { + return makeRequest, undefined>( + path, + payload, + undefined, + { + ...options, + method: "POST", + } + ) +} + +export async function deleteRequest( + path: string, + options?: Omit +): Promise { + return makeRequest(path, undefined, undefined, { + ...options, + method: "DELETE", + }) +} diff --git a/packages/admin-next/dashboard/src/lib/client/currencies.ts b/packages/admin-next/dashboard/src/lib/client/currencies.ts index 2a0223b78ec08..0ccb46a0e836f 100644 --- a/packages/admin-next/dashboard/src/lib/client/currencies.ts +++ b/packages/admin-next/dashboard/src/lib/client/currencies.ts @@ -1,15 +1,12 @@ import { CurrencyListRes, CurrencyRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { getRequest } from "./common" -async function retrieveCurrency(id: string) { - return makeRequest(`/admin/currencies/${id}`) +async function retrieveCurrency(id: string, query?: Record) { + return getRequest(`/admin/currencies/${id}`, query) } async function listCurrencies(query?: Record) { - return makeRequest>( - "/admin/currencies", - query - ) + return getRequest("/admin/currencies", query) } export const currencies = { diff --git a/packages/admin-next/dashboard/src/lib/client/customers.ts b/packages/admin-next/dashboard/src/lib/client/customers.ts index f0081dbac13cf..027fb0f94d4d0 100644 --- a/packages/admin-next/dashboard/src/lib/client/customers.ts +++ b/packages/admin-next/dashboard/src/lib/client/customers.ts @@ -1,33 +1,21 @@ import { CreateCustomerReq, UpdateCustomerReq } from "../../types/api-payloads" import { CustomerListRes, CustomerRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { getRequest, postRequest } from "./common" async function retrieveCustomer(id: string, query?: Record) { - return makeRequest>( - `/admin/customers/${id}`, - query - ) + return getRequest(`/admin/customers/${id}`, query) } async function listCustomers(query?: Record) { - return makeRequest>( - `/admin/customers`, - query - ) + return getRequest(`/admin/customers`, query) } async function createCustomer(payload: CreateCustomerReq) { - return makeRequest(`/admin/customers`, undefined, { - method: "POST", - body: JSON.stringify(payload), - }) + return postRequest(`/admin/customers`, payload) } async function updateCustomer(id: string, payload: UpdateCustomerReq) { - return makeRequest(`/admin/customers/${id}`, undefined, { - method: "POST", - body: JSON.stringify(payload), - }) + return postRequest(`/admin/customers/${id}`, payload) } export const customers = { diff --git a/packages/admin-next/dashboard/src/lib/client/invites.ts b/packages/admin-next/dashboard/src/lib/client/invites.ts new file mode 100644 index 0000000000000..80f3553180d8b --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/invites.ts @@ -0,0 +1,38 @@ +import { CreateInviteReq } from "../../types/api-payloads" +import { + InviteDeleteRes, + InviteListRes, + InviteRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" + +async function retrieveInvite(id: string, query?: Record) { + return getRequest>( + `/admin/invites/${id}`, + query + ) +} + +async function listInvites(query?: Record) { + return getRequest>(`/admin/invites`, query) +} + +async function createInvite(payload: CreateInviteReq) { + return postRequest(`/admin/invites`, payload) +} + +async function resendInvite(id: string) { + return postRequest(`/admin/invites/${id}/resend`) +} + +async function deleteInvite(id: string) { + return deleteRequest(`/admin/invites/${id}`) +} + +export const invites = { + retrieve: retrieveInvite, + list: listInvites, + create: createInvite, + resend: resendInvite, + delete: deleteInvite, +} diff --git a/packages/admin-next/dashboard/src/lib/client/product-types.ts b/packages/admin-next/dashboard/src/lib/client/product-types.ts new file mode 100644 index 0000000000000..2eaaddb96295e --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/product-types.ts @@ -0,0 +1,15 @@ +import { ProductTypeListRes, ProductTypeRes } from "../../types/api-responses" +import { getRequest } from "./common" + +async function listProductTypes(query?: Record) { + return getRequest(`/admin/product-types`, query) +} + +async function retrieveProductType(id: string, query?: Record) { + return getRequest(`/admin/product-types/${id}`, query) +} + +export const productTypes = { + list: listProductTypes, + retrieve: retrieveProductType, +} diff --git a/packages/admin-next/dashboard/src/lib/client/products.ts b/packages/admin-next/dashboard/src/lib/client/products.ts new file mode 100644 index 0000000000000..43af2d9bd02d3 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/products.ts @@ -0,0 +1,15 @@ +import { ProductListRes, ProductRes } from "../../types/api-responses" +import { getRequest } from "./common" + +async function retrieveProduct(id: string, query?: Record) { + return getRequest(`/admin/products/${id}`, query) +} + +async function listProducts(query?: Record) { + return getRequest(`/admin/products`, query) +} + +export const products = { + retrieve: retrieveProduct, + list: listProducts, +} diff --git a/packages/admin-next/dashboard/src/lib/client/promotions.ts b/packages/admin-next/dashboard/src/lib/client/promotions.ts index f56b306b5ef5f..574bb819a9c8f 100644 --- a/packages/admin-next/dashboard/src/lib/client/promotions.ts +++ b/packages/admin-next/dashboard/src/lib/client/promotions.ts @@ -1,13 +1,13 @@ import { AdminGetPromotionsParams } from "@medusajs/medusa" import { PromotionRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { getRequest } from "./common" const retrievePromotion = async ( id: string, query?: AdminGetPromotionsParams ) => { - return makeRequest( + return getRequest( `/admin/promotions/${id}`, query ) diff --git a/packages/admin-next/dashboard/src/lib/client/regions.ts b/packages/admin-next/dashboard/src/lib/client/regions.ts index d51736c00071f..7a64d0b72648a 100644 --- a/packages/admin-next/dashboard/src/lib/client/regions.ts +++ b/packages/admin-next/dashboard/src/lib/client/regions.ts @@ -4,40 +4,26 @@ import { RegionListRes, RegionRes, } from "../../types/api-responses" -import { makeRequest } from "./common" +import { deleteRequest, getRequest, postRequest } from "./common" async function retrieveRegion(id: string, query?: Record) { - return makeRequest>( - `/admin/regions/${id}`, - query - ) + return getRequest(`/admin/regions/${id}`, query) } async function listRegions(query?: Record) { - return makeRequest>( - `/admin/regions`, - query - ) + return getRequest(`/admin/regions`, query) } -async function createRegion(region: CreateRegionDTO) { - return makeRequest(`/admin/regions`, undefined, { - method: "POST", - body: JSON.stringify(region), - }) +async function createRegion(payload: CreateRegionDTO) { + return postRequest(`/admin/regions`, payload) } -async function updateRegion(id: string, region: UpdateRegionDTO) { - return makeRequest(`/admin/regions/${id}`, undefined, { - method: "POST", - body: JSON.stringify(region), - }) +async function updateRegion(id: string, payload: UpdateRegionDTO) { + return postRequest(`/admin/regions/${id}`, payload) } async function deleteRegion(id: string) { - return makeRequest(`/admin/regions/${id}`, undefined, { - method: "DELETE", - }) + return deleteRequest(`/admin/regions/${id}`) } export const regions = { diff --git a/packages/admin-next/dashboard/src/lib/client/sales-channels.ts b/packages/admin-next/dashboard/src/lib/client/sales-channels.ts index c8953695cec07..82ba3fdd8d906 100644 --- a/packages/admin-next/dashboard/src/lib/client/sales-channels.ts +++ b/packages/admin-next/dashboard/src/lib/client/sales-channels.ts @@ -1,46 +1,60 @@ import { + AddProductsSalesChannelReq, CreateSalesChannelReq, + RemoveProductsSalesChannelReq, UpdateSalesChannelReq, } from "../../types/api-payloads" -import { SalesChannelListRes, SalesChannelRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { + SalesChannelDeleteRes, + SalesChannelListRes, + SalesChannelRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" async function retrieveSalesChannel(id: string, query?: Record) { - return makeRequest>( + return getRequest>( `/admin/sales-channels/${id}`, query ) } async function listSalesChannels(query?: Record) { - return makeRequest>( + return getRequest>( `/admin/sales-channels`, query ) } async function createSalesChannel(payload: CreateSalesChannelReq) { - return makeRequest(`/admin/sales-channels`, undefined, { - method: "POST", - body: JSON.stringify(payload), - }) + return postRequest(`/admin/sales-channels`, payload) } async function updateSalesChannel(id: string, payload: UpdateSalesChannelReq) { - return makeRequest( - `/admin/sales-channels/${id}`, - undefined, - { - method: "POST", - body: JSON.stringify(payload), - } - ) + return postRequest(`/admin/sales-channels/${id}`, payload) } async function deleteSalesChannel(id: string) { - return makeRequest(`/admin/sales-channels/${id}`, undefined, { - method: "DELETE", - }) + return deleteRequest(`/admin/sales-channels/${id}`) +} + +async function batchRemoveProducts( + id: string, + payload: RemoveProductsSalesChannelReq +) { + return postRequest( + `/admin/sales-channels/${id}/products/batch/remove`, + payload + ) +} + +async function batchAddProducts( + id: string, + payload: AddProductsSalesChannelReq +) { + return postRequest( + `/admin/sales-channels/${id}/products/batch/add`, + payload + ) } export const salesChannels = { @@ -49,4 +63,6 @@ export const salesChannels = { create: createSalesChannel, update: updateSalesChannel, delete: deleteSalesChannel, + removeProducts: batchRemoveProducts, + addProducts: batchAddProducts, } diff --git a/packages/admin-next/dashboard/src/lib/client/stores.ts b/packages/admin-next/dashboard/src/lib/client/stores.ts index 4f789c07e7b1e..987558e03d732 100644 --- a/packages/admin-next/dashboard/src/lib/client/stores.ts +++ b/packages/admin-next/dashboard/src/lib/client/stores.ts @@ -1,16 +1,14 @@ import { UpdateStoreReq } from "../../types/api-payloads" import { StoreListRes, StoreRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { getRequest, postRequest } from "./common" async function retrieveStore(query?: Record): Promise { - const response = await makeRequest>( - "/admin/stores", - query - ) + const response = await getRequest("/admin/stores", query) const activeStore = response.stores?.[0] if (!activeStore) { + // Temp: Add proper error handling throw new Error("No active store found") } @@ -18,10 +16,7 @@ async function retrieveStore(query?: Record): Promise { } async function updateStore(id: string, payload: UpdateStoreReq) { - return makeRequest(`/admin/stores/${id}`, undefined, { - method: "POST", - body: JSON.stringify(payload), - }) + return postRequest(`/admin/stores/${id}`, payload) } export const stores = { diff --git a/packages/admin-next/dashboard/src/lib/client/users.ts b/packages/admin-next/dashboard/src/lib/client/users.ts index a3ec57ad86f37..5826a60959f23 100644 --- a/packages/admin-next/dashboard/src/lib/client/users.ts +++ b/packages/admin-next/dashboard/src/lib/client/users.ts @@ -1,24 +1,25 @@ import { UpdateUserReq } from "../../types/api-payloads" -import { UserListRes, UserRes } from "../../types/api-responses" -import { makeRequest } from "./common" +import { UserDeleteRes, UserListRes, UserRes } from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" async function me() { - return makeRequest("/admin/users/me") + return getRequest("/admin/users/me") } async function retrieveUser(id: string, query?: Record) { - return makeRequest>(`/admin/users/${id}`, query) + return getRequest(`/admin/users/${id}`, query) } async function listUsers(query?: Record) { - return makeRequest(`/admin/users`, undefined, query) + return getRequest(`/admin/users`, query) } async function updateUser(id: string, payload: UpdateUserReq) { - return makeRequest(`/admin/users/${id}`, undefined, { - method: "POST", - body: JSON.stringify(payload), - }) + return postRequest(`/admin/users/${id}`, payload) +} + +async function deleteUser(id: string) { + return deleteRequest(`/admin/users/${id}`) } export const users = { @@ -26,4 +27,5 @@ export const users = { retrieve: retrieveUser, list: listUsers, update: updateUser, + delete: deleteUser, } diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx b/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx deleted file mode 100644 index d3ca4618aee65..0000000000000 --- a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx +++ /dev/null @@ -1,260 +0,0 @@ -import { PencilSquare, Trash } from "@medusajs/icons" -import { SalesChannel } from "@medusajs/medusa" -import { - Button, - Container, - Heading, - StatusBadge, - Table, - clx, - usePrompt, -} from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { useAdminDeleteSalesChannel, useAdminSalesChannels } from "medusa-react" -import { useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import { Link, useNavigate } from "react-router-dom" -import { ActionMenu } from "../../../../components/common/action-menu" -import { OrderBy } from "../../../../components/filtering/order-by" -import { Query } from "../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../components/localization/localized-table-pagination" -import { useQueryParams } from "../../../../hooks/use-query-params" - -const PAGE_SIZE = 50 - -export const SalesChannelListTable = () => { - const navigate = useNavigate() - const { t } = useTranslation() - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - const { q, order } = useQueryParams(["q", "order"]) - - const { sales_channels, count, isLoading, isError, error } = - useAdminSalesChannels( - { - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - q, - order, - }, - { - keepPreviousData: true, - } - ) - - const columns = useColumns() - - const table = useReactTable({ - data: sales_channels ?? [], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - }) - - if (isError) { - throw error - } - - return ( - -
- {t("salesChannels.domain")} - - - -
-
-
-
- - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - navigate(`/settings/sales-channels/${row.original.id}`) - } - > - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
- -
-
- ) -} - -const SalesChannelActions = ({ - salesChannel, -}: { - salesChannel: SalesChannel -}) => { - const { t } = useTranslation() - const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteSalesChannel(salesChannel.id) - - const handleDelete = async () => { - const confirm = await prompt({ - title: t("general.areYouSure"), - description: t("salesChannels.deleteSalesChannelWarning", { - name: salesChannel.name, - }), - verificationInstruction: t("general.typeToConfirm"), - verificationText: salesChannel.name, - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) - - if (!confirm) { - return - } - - await mutateAsync() - } - - return ( - , - label: t("actions.edit"), - to: `/settings/sales-channels/${salesChannel.id}/edit`, - }, - ], - }, - { - actions: [ - { - icon: , - label: t("actions.delete"), - onClick: handleDelete, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("name", { - header: t("fields.name"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("description", { - header: t("fields.description"), - cell: ({ getValue }) => ( -
- {getValue()} -
- ), - }), - columnHelper.accessor("is_disabled", { - header: t("fields.status"), - cell: ({ getValue }) => { - const value = getValue() - return ( -
- - {value ? t("general.disabled") : t("general.enabled")} - -
- ) - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row }) => { - return - }, - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx index 0d582d200df4d..72168e7d0b1aa 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx @@ -8,8 +8,6 @@ import type { AdminProductsRes, AdminPublishableApiKeysRes, AdminRegionsRes, - AdminSalesChannelsRes, - AdminUserRes, } from "@medusajs/medusa" import { Outlet, RouteObject } from "react-router-dom" @@ -646,38 +644,6 @@ export const v1Routes: RouteObject[] = [ }, ], }, - { - path: "users", - element: , - handle: { - crumb: () => "Users", - }, - children: [ - { - path: "", - lazy: () => import("../../routes/users/user-list"), - children: [ - { - path: "invite", - lazy: () => import("../../routes/users/user-invite"), - }, - ], - }, - { - path: ":id", - lazy: () => import("../../routes/users/user-detail"), - handle: { - crumb: (data: AdminUserRes) => data.user.email, - }, - children: [ - { - path: "edit", - lazy: () => import("../../routes/users/user-edit"), - }, - ], - }, - ], - }, { path: "taxes", handle: { @@ -716,52 +682,6 @@ export const v1Routes: RouteObject[] = [ }, ], }, - { - path: "sales-channels", - element: , - handle: { - crumb: () => "Sales Channels", - }, - children: [ - { - path: "", - lazy: () => - import("../../routes/sales-channels/sales-channel-list"), - children: [ - { - path: "create", - lazy: () => - import( - "../../routes/sales-channels/sales-channel-create" - ), - }, - ], - }, - { - path: ":id", - lazy: () => - import("../../routes/sales-channels/sales-channel-detail"), - handle: { - crumb: (data: AdminSalesChannelsRes) => - data.sales_channel.name, - }, - children: [ - { - path: "edit", - lazy: () => - import("../../routes/sales-channels/sales-channel-edit"), - }, - { - path: "add-products", - lazy: () => - import( - "../../routes/sales-channels/sales-channel-add-products" - ), - }, - ], - }, - ], - }, { path: "api-key-management", element: , diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx deleted file mode 100644 index d254d424242de..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod" -import { Product, SalesChannel } from "@medusajs/medusa" -import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { - adminProductKeys, - useAdminAddProductsToSalesChannel, - useAdminProducts, -} from "medusa-react" -import { useEffect, useMemo, useState } from "react" -import { useForm } from "react-hook-form" -import { useTranslation } from "react-i18next" -import * as zod from "zod" -import { - ProductAvailabilityCell, - ProductCollectionCell, - ProductStatusCell, - ProductTitleCell, - ProductVariantCell, -} from "../../../../components/common/product-table-cells" -import { OrderBy } from "../../../../components/filtering/order-by" -import { Query } from "../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../components/localization/localized-table-pagination" -import { - RouteFocusModal, - useRouteModal, -} from "../../../../components/route-modal" -import { useQueryParams } from "../../../../hooks/use-query-params" -import { queryClient } from "../../../../lib/medusa" - -type AddProductsToSalesChannelFormProps = { - salesChannel: SalesChannel -} - -const AddProductsToSalesChannelSchema = zod.object({ - product_ids: zod.array(zod.string()).min(1), -}) - -const PAGE_SIZE = 50 - -export const AddProductsToSalesChannelForm = ({ - salesChannel, -}: AddProductsToSalesChannelFormProps) => { - const { t } = useTranslation() - const { handleSuccess } = useRouteModal() - - const form = useForm>({ - defaultValues: { - product_ids: [], - }, - resolver: zodResolver(AddProductsToSalesChannelSchema), - }) - - const { setValue } = form - - const { mutateAsync, isLoading: isMutating } = - useAdminAddProductsToSalesChannel(salesChannel.id) - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - useEffect(() => { - setValue( - "product_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]), - { - shouldDirty: true, - shouldTouch: true, - } - ) - }, [rowSelection, setValue]) - - const params = useQueryParams(["q", "order"]) - - const { products, count } = useAdminProducts( - { - expand: "variants,sales_channels", - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, - }, - { - keepPreviousData: true, - } - ) - - const columns = useColumns() - - const table = useReactTable({ - data: (products ?? []) as Product[], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - getRowId: (row) => row.id, - enableRowSelection(row) { - return !row.original.sales_channels - ?.map((sc) => sc.id) - .includes(salesChannel.id) - }, - meta: { - salesChannelId: salesChannel.id, - }, - }) - - const handleSubmit = form.handleSubmit(async (values) => { - await mutateAsync( - { - product_ids: values.product_ids.map((p) => ({ id: p })), - }, - { - onSuccess: () => { - /** - * Invalidate the products list query to refetch products and - * determine if they are added to the sales channel or not. - */ - queryClient.invalidateQueries(adminProductKeys.lists()) - handleSuccess() - }, - } - ) - }) - - return ( - -
- -
- {form.formState.errors.product_ids && ( - - {form.formState.errors.product_ids.message} - - )} - - - - -
-
- -
-
-
- - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - sc.id) - .includes(salesChannel.id), - } - )} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
-
-
- -
-
-
-
- ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.display({ - id: "select", - header: ({ table }) => { - return ( - - table.toggleAllPageRowsSelected(!!value) - } - /> - ) - }, - cell: ({ row, table }) => { - const { salesChannelId } = table.options.meta as { - salesChannelId: string - } - - const isAdded = row.original.sales_channels - ?.map((sc) => sc.id) - .includes(salesChannelId) - - const isSelected = row.getIsSelected() || isAdded - - const Component = ( - row.toggleSelected(!!value)} - onClick={(e) => { - e.stopPropagation() - }} - /> - ) - - if (isAdded) { - return ( - - {Component} - - ) - } - - return Component - }, - }), - columnHelper.accessor("title", { - header: t("fields.title"), - cell: ({ row }) => { - const product = row.original - - return - }, - }), - columnHelper.accessor("collection", { - header: t("fields.collection"), - cell: ({ getValue }) => { - const collection = getValue() - - return - }, - }), - columnHelper.accessor("sales_channels", { - header: t("fields.availability"), - cell: ({ getValue }) => { - const salesChannels = getValue() - - return - }, - }), - columnHelper.accessor("variants", { - header: t("fields.inventory"), - cell: (cell) => { - const variants = cell.getValue() - - return - }, - }), - columnHelper.accessor("status", { - header: t("fields.status"), - cell: ({ getValue }) => { - const status = getValue() - - return - }, - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/index.ts deleted file mode 100644 index 87d988e4af33e..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./add-products-to-sales-channel-form" diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/index.ts deleted file mode 100644 index 3d06c4174c04a..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SalesChannelAddProducts as Component } from "./sales-channel-add-products" diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx deleted file mode 100644 index 1b28c6d5d3fcd..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useAdminSalesChannel } from "medusa-react" -import { useParams } from "react-router-dom" -import { RouteFocusModal } from "../../../components/route-modal" -import { AddProductsToSalesChannelForm } from "./components" - -export const SalesChannelAddProducts = () => { - const { id } = useParams() - const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) - - if (isError) { - throw error - } - - return ( - - {!isLoading && sales_channel && ( - - )} - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/index.ts deleted file mode 100644 index 2d294e8ae7abc..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SalesChannelCreate as Component } from "./sales-channel-create" diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx deleted file mode 100644 index e1ee3874081e9..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-create/sales-channel-create.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { RouteFocusModal } from "../../../components/route-modal" -import { CreateSalesChannelForm } from "../../../modules/sales-channels/sales-channel-create/components/create-sales-channel-form" - -export const SalesChannelCreate = () => { - return ( - - - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/index.ts deleted file mode 100644 index 72a7d4ad25203..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./sales-channel-product-section" diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx deleted file mode 100644 index 498ba70ef576b..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx +++ /dev/null @@ -1,349 +0,0 @@ -import { PencilSquare, Trash } from "@medusajs/icons" -import { Product, SalesChannel } from "@medusajs/medusa" -import { - Button, - Checkbox, - CommandBar, - Container, - Heading, - Table, - clx, - usePrompt, -} from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { - adminProductKeys, - useAdminDeleteProductsFromSalesChannel, - useAdminProducts, -} from "medusa-react" -import { useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" - -import { - ProductStatusCell, - ProductTitleCell, - ProductVariantCell, -} from "../../../../../components/common/product-table-cells" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" - -import { ActionMenu } from "../../../../../components/common/action-menu" -import { FilterGroup } from "../../../../../components/filtering/filter-group" -import { OrderBy } from "../../../../../components/filtering/order-by" -import { Query } from "../../../../../components/filtering/query" -import { useQueryParams } from "../../../../../hooks/use-query-params" -import { queryClient } from "../../../../../lib/medusa" - -const PAGE_SIZE = 10 - -type SalesChannelProductSection = { - salesChannel: SalesChannel -} - -export const SalesChannelProductSection = ({ - salesChannel, -}: SalesChannelProductSection) => { - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - const params = useQueryParams(["q", "order"]) - const { products, count, isLoading, isError, error } = useAdminProducts( - { - sales_channel_id: [salesChannel.id], - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, - }, - { - keepPreviousData: true, - } - ) - - const columns = useListColumns(salesChannel.id) - - const table = useReactTable({ - data: (products ?? []) as Product[], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - getRowId: (row) => row.id, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - }) - - const { mutateAsync } = useAdminDeleteProductsFromSalesChannel( - salesChannel.id - ) - const prompt = usePrompt() - - const { t } = useTranslation() - - const onRemove = async () => { - const ids = Object.keys(rowSelection).map((k) => ({ id: k })) - - const result = await prompt({ - title: t("general.areYouSure"), - description: t("salesChannels.removeProductsWarning", { - count: ids.length, - sales_channel: salesChannel.name, - }), - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) - - if (!result) { - return - } - - await mutateAsync( - { - product_ids: ids, - }, - { - onSuccess: () => { - setRowSelection({}) - queryClient.invalidateQueries(adminProductKeys.lists()) - }, - } - ) - } - - if (isError) { - throw error - } - - return ( - -
- {t("products.domain")} - - - -
-
- -
- - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
- - - - - {t("general.countSelected", { - count: Object.keys(rowSelection).length, - })} - - - - - -
-
- ) -} - -const listColumnHelper = createColumnHelper() - -const useListColumns = (id: string) => { - const { t } = useTranslation() - - return useMemo( - () => [ - listColumnHelper.display({ - id: "select", - header: ({ table }) => { - return ( - - table.toggleAllPageRowsSelected(!!value) - } - /> - ) - }, - cell: ({ row }) => { - return ( - row.toggleSelected(!!value)} - onClick={(e) => { - e.stopPropagation() - }} - /> - ) - }, - }), - listColumnHelper.accessor("title", { - header: t("fields.title"), - cell: ({ row }) => { - const product = row.original - - return - }, - }), - listColumnHelper.accessor("variants", { - header: t("fields.variants"), - cell: (cell) => { - const variants = cell.getValue() - - return - }, - }), - listColumnHelper.accessor("status", { - header: t("fields.status"), - cell: ({ getValue }) => { - const status = getValue() - - return - }, - }), - listColumnHelper.display({ - id: "actions", - cell: ({ row }) => { - return ( - - ) - }, - }), - ], - [t] - ) -} - -const ProductListCellActions = ({ - salesChannelId, - productId, -}: { - productId: string - salesChannelId: string -}) => { - const { t } = useTranslation() - const { mutateAsync } = useAdminDeleteProductsFromSalesChannel(salesChannelId) - - const onRemove = async () => { - await mutateAsync({ - product_ids: [{ id: productId }], - }) - } - - return ( - , - label: t("actions.edit"), - to: `/products/${productId}`, - }, - ], - }, - { - actions: [ - { - icon: , - label: t("actions.remove"), - onClick: onRemove, - }, - ], - }, - ]} - /> - ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts deleted file mode 100644 index 8d5a59045f7b8..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { salesChannelLoader as loader } from "./loader" -export { SalesChannelDetail as Component } from "./sales-channel-detail" diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts deleted file mode 100644 index 5208c2e065737..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/loader.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { AdminSalesChannelsRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" -import { adminProductKeys } from "medusa-react" -import { LoaderFunctionArgs } from "react-router-dom" - -import { medusa, queryClient } from "../../../lib/medusa" - -const salesChannelDetailQuery = (id: string) => ({ - queryKey: adminProductKeys.detail(id), - queryFn: async () => medusa.admin.salesChannels.retrieve(id), -}) - -export const salesChannelLoader = async ({ params }: LoaderFunctionArgs) => { - const id = params.id - const query = salesChannelDetailQuery(id!) - - return ( - queryClient.getQueryData>(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx deleted file mode 100644 index 09110f86c3b42..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useAdminSalesChannel } from "medusa-react" -import { Outlet, useLoaderData, useParams } from "react-router-dom" - -import { JsonViewSection } from "../../../components/common/json-view-section" -import { SalesChannelGeneralSection } from "../../../modules/sales-channels/sales-channel-detail/components/sales-channel-general-section" -import { SalesChannelProductSection } from "./components/sales-channel-product-section" -import { salesChannelLoader } from "./loader" - -export const SalesChannelDetail = () => { - const initialData = useLoaderData() as Awaited< - ReturnType - > - - const { id } = useParams() - const { sales_channel, isLoading } = useAdminSalesChannel(id!, { - initialData, - }) - - if (isLoading || !sales_channel) { - return
Loading...
- } - - console.log("SalesChannelDetail") - - return ( -
- - - - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/index.ts deleted file mode 100644 index 04317d1479145..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SalesChannelEdit as Component } from "./sales-channel-edit" diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx deleted file mode 100644 index bff248ecef7f2..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Heading } from "@medusajs/ui" -import { useAdminSalesChannel } from "medusa-react" -import { useTranslation } from "react-i18next" -import { useParams } from "react-router-dom" - -import { RouteDrawer } from "../../../components/route-modal" -import { EditSalesChannelForm } from "../../../modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form" - -export const SalesChannelEdit = () => { - const { id } = useParams() - const { t } = useTranslation() - - const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) - - if (isError) { - throw error - } - - return ( - - - - {t("salesChannels.editSalesChannel")} - - - {!isLoading && !!sales_channel && ( - - )} - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/index.ts b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/index.ts deleted file mode 100644 index 3900dde000e04..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SalesChannelList as Component } from "./sales-channel-list"; diff --git a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/sales-channel-list.tsx b/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/sales-channel-list.tsx deleted file mode 100644 index 9088b57b3cf65..0000000000000 --- a/packages/admin-next/dashboard/src/routes/sales-channels/sales-channel-list/sales-channel-list.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Outlet } from "react-router-dom" -import { SalesChannelListTable } from "../../../modules/sales-channels/sales-channel-list/components" - -export const SalesChannelList = () => { - return ( -
- - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/index.ts b/packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/index.ts deleted file mode 100644 index 4712aa66578da..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./user-general-section" diff --git a/packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/user-general-section.tsx b/packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/user-general-section.tsx deleted file mode 100644 index 1e573089c8f25..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-detail/components/user-general-section/user-general-section.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { PencilSquare, Trash } from "@medusajs/icons" -import { User } from "@medusajs/medusa" -import { Container, Heading, Text, clx, usePrompt } from "@medusajs/ui" -import { useAdminDeleteUser } from "medusa-react" -import { useTranslation } from "react-i18next" -import { useNavigate } from "react-router-dom" -import { ActionMenu } from "../../../../../components/common/action-menu" - -type UserGeneralSectionProps = { - user: Omit -} - -export const UserGeneralSection = ({ user }: UserGeneralSectionProps) => { - const { t } = useTranslation() - const navigate = useNavigate() - const prompt = usePrompt() - - const { mutateAsync } = useAdminDeleteUser(user.id) - - const name = [user.first_name, user.last_name].filter(Boolean).join(" ") - - const handleDeleteUser = async () => { - const res = await prompt({ - title: t("general.areYouSure"), - description: t("users.deleteUserWarning", { - name: name ?? user.email, - }), - verificationText: name ?? user.email, - verificationInstruction: t("general.typeToConfirm"), - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) - - if (!res) { - return - } - - await mutateAsync(undefined, { - onSuccess: () => { - navigate("..") - }, - }) - } - - return ( - -
- {user.email} - , - }, - ], - }, - { - actions: [ - { - label: t("actions.delete"), - onClick: handleDeleteUser, - icon: , - }, - ], - }, - ]} - /> -
-
- - {t("fields.name")} - - - {name ?? "-"} - -
-
- - {t("fields.role")} - - - {t(`users.roles.${user.role}`)} - -
-
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-detail/index.ts b/packages/admin-next/dashboard/src/routes/users/user-detail/index.ts deleted file mode 100644 index d91450c4f035f..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-detail/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { userLoader as loader } from "./loader" -export { UserDetail as Component } from "./user-detail" diff --git a/packages/admin-next/dashboard/src/routes/users/user-detail/loader.ts b/packages/admin-next/dashboard/src/routes/users/user-detail/loader.ts deleted file mode 100644 index 1dbeb58ba85a8..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-detail/loader.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { AdminUserRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" -import { adminProductKeys } from "medusa-react" -import { LoaderFunctionArgs } from "react-router-dom" - -import { medusa, queryClient } from "../../../lib/medusa" - -const userDetailQuery = (id: string) => ({ - queryKey: adminProductKeys.detail(id), - queryFn: async () => medusa.admin.users.retrieve(id), -}) - -export const userLoader = async ({ params }: LoaderFunctionArgs) => { - const id = params.id - const query = userDetailQuery(id!) - - return ( - queryClient.getQueryData>(query.queryKey) ?? - (await queryClient.fetchQuery(query)) - ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-detail/user-detail.tsx b/packages/admin-next/dashboard/src/routes/users/user-detail/user-detail.tsx deleted file mode 100644 index 460d6ba81f335..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-detail/user-detail.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useAdminUser } from "medusa-react" -import { Outlet, json, useLoaderData, useParams } from "react-router-dom" -import { JsonViewSection } from "../../../components/common/json-view-section" -import { UserGeneralSection } from "./components/user-general-section" -import { userLoader } from "./loader" - -export const UserDetail = () => { - const initialData = useLoaderData() as Awaited> - - const { id } = useParams() - const { user, isLoading, isError, error } = useAdminUser(id!, { - initialData, - }) - - if (isLoading) { - return
Loading...
- } - - if (isError || !user) { - if (error) { - throw error - } - - throw json("An unknown error has occured", 500) - } - - return ( -
- - - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-edit/index.ts b/packages/admin-next/dashboard/src/routes/users/user-edit/index.ts deleted file mode 100644 index 50a4319a8d27b..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-edit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserEdit as Component } from "../../../modules/user/user-edit" diff --git a/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx b/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx deleted file mode 100644 index da1df79768ca3..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-invite/components/invite-user-form/invite-user-form.tsx +++ /dev/null @@ -1,417 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod" -import { ArrowPath, Link, XCircle } from "@medusajs/icons" -import { Invite } from "@medusajs/medusa" -import { - Button, - Container, - Heading, - Input, - Select, - StatusBadge, - Table, - Text, - Tooltip, - clx, - usePrompt, -} from "@medusajs/ui" -import { - createColumnHelper, - flexRender, - getCoreRowModel, - getPaginationRowModel, - useReactTable, -} from "@tanstack/react-table" -import { format } from "date-fns" -import { - useAdminCreateInvite, - useAdminDeleteInvite, - useAdminInvites, - useAdminResendInvite, - useAdminStore, -} from "medusa-react" -import { useMemo } from "react" -import { useForm } from "react-hook-form" -import { Trans, useTranslation } from "react-i18next" -import * as zod from "zod" -import { ActionMenu } from "../../../../../components/common/action-menu" -import { NoRecords } from "../../../../../components/common/empty-table-content" -import { Form } from "../../../../../components/common/form" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" -import { RouteFocusModal } from "../../../../../components/route-modal" - -enum UserRole { - MEMBER = "member", - DEVELOPER = "developer", - ADMIN = "admin", -} - -const InviteUserSchema = zod.object({ - user: zod.string().email(), - role: zod.nativeEnum(UserRole), -}) - -const PAGE_SIZE = 10 - -export const InviteUserForm = () => { - const { t } = useTranslation() - - const form = useForm>({ - defaultValues: { - user: "", - role: UserRole.MEMBER, - }, - resolver: zodResolver(InviteUserSchema), - }) - - const { invites, isLoading, isError, error } = useAdminInvites() - const count = invites?.length ?? 0 - - const noRecords = !isLoading && count === 0 - - const columns = useColumns() - - const table = useReactTable({ - data: invites ?? [], - columns, - pageCount: Math.ceil(count / PAGE_SIZE), - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - }) - - const { mutateAsync, isLoading: isMutating } = useAdminCreateInvite() - - const handleSubmit = form.handleSubmit(async (values) => { - await mutateAsync( - { - role: values.role, - user: values.user, - }, - { - onSuccess: () => { - form.reset() - }, - } - ) - }) - - if (isError) { - throw error - } - - return ( - -
- - -
-
-
- {t("users.inviteUser")} - - {t("users.inviteUserHint")} - -
-
-
- { - return ( - - {t("fields.email")} - - - - - - ) - }} - /> - { - return ( - - {t("fields.role")} - - - - - - ) - }} - /> -
-
- -
-
-
- {t("users.pendingInvites")} - - {!noRecords ? ( -
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- -
- ) : ( - - )} -
-
-
-
-
- -
- ) -} - -const InviteActions = ({ invite }: { invite: Invite }) => { - const { mutateAsync: revokeAsync } = useAdminDeleteInvite(invite.id) - const { mutateAsync: resendAsync } = useAdminResendInvite(invite.id) - const { store, isLoading, isError, error } = useAdminStore() - const prompt = usePrompt() - const { t } = useTranslation() - - const handleRevoke = async () => { - const res = await prompt({ - title: t("general.areYouSure"), - description: t("users.revokeInviteWarning", { - email: invite.user_email, - }), - cancelText: t("actions.cancel"), - confirmText: t("actions.revoke"), - }) - - if (!res) { - return - } - - await revokeAsync() - } - - const handleResend = async () => { - await resendAsync() - } - - const handleCopyInviteLink = () => { - const template = store?.invite_link_template - - if (!template) { - return - } - - const link = template.replace("{invite_token}", invite.token) - navigator.clipboard.writeText(link) - } - - if (isError) { - throw error - } - - return ( - , - label: t("users.copyInviteLink"), - disabled: isLoading || !store?.invite_link_template, - onClick: handleCopyInviteLink, - }, - { - icon: , - label: t("users.resendInvite"), - onClick: handleResend, - }, - ], - }, - { - actions: [ - { - icon: , - label: t("actions.revoke"), - onClick: handleRevoke, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("user_email", { - header: t("fields.email"), - cell: ({ getValue }) => { - return getValue() - }, - }), - columnHelper.accessor("role", { - header: t("fields.role"), - cell: ({ getValue }) => { - return t(`users.roles.${getValue()}`) - }, - }), - columnHelper.accessor("accepted", { - header: t("fields.status"), - cell: ({ getValue, row }) => { - const accepted = getValue() - const expired = new Date(row.original.expires_at) < new Date() - - if (accepted) { - return ( - - - {t("users.inviteStatus.accepted")} - - - ) - } - - if (expired) { - return ( - - - {t("users.inviteStatus.expired")} - - - ) - } - - return ( - , - , - ]} - values={{ - from: format( - new Date(row.original.created_at), - "dd MMM, yyyy" - ), - until: format( - new Date(row.original.expires_at), - "dd MMM, yyyy" - ), - }} - /> - } - > - - {t("users.inviteStatus.pending")} - - - ) - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row }) => , - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-invite/index.ts b/packages/admin-next/dashboard/src/routes/users/user-invite/index.ts deleted file mode 100644 index 882f7aca37e66..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-invite/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserInvite as Component } from "./user-invite" diff --git a/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx b/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx deleted file mode 100644 index 23432b8d22755..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-invite/user-invite.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { RouteFocusModal } from "../../../components/route-modal" -import { InviteUserForm } from "./components/invite-user-form/invite-user-form" - -export const UserInvite = () => { - return ( - - - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/index.ts b/packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/index.ts deleted file mode 100644 index 13c4e5e68055c..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./user-list-table" diff --git a/packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/user-list-table.tsx b/packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/user-list-table.tsx deleted file mode 100644 index 6c0ae34734804..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-list/components/user-list-table/user-list-table.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import { PencilSquare } from "@medusajs/icons" -import { User } from "@medusajs/medusa" -import { Button, Container, Heading, Table, clx } from "@medusajs/ui" -import { - PaginationState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { useAdminUsers } from "medusa-react" -import { useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import { Link, useNavigate } from "react-router-dom" -import { ActionMenu } from "../../../../../components/common/action-menu" -import { - NoRecords, - NoResults, -} from "../../../../../components/common/empty-table-content" -import { OrderBy } from "../../../../../components/filtering/order-by" -import { Query } from "../../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" -import { useQueryParams } from "../../../../../hooks/use-query-params" - -const PAGE_SIZE = 50 - -export const UserListTable = () => { - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const params = useQueryParams(["q", "order"]) - const { users, count, isLoading, isError, error } = useAdminUsers( - { - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, - }, - { - keepPreviousData: true, - } - ) - - const columns = useColumns() - - const table = useReactTable({ - data: users ?? [], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - }, - onPaginationChange: setPagination, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - }) - - const { t } = useTranslation() - const navigate = useNavigate() - - const noRecords = - !isLoading && - !users?.length && - !Object.values(params).filter(Boolean).length - - if (isError) { - throw error - } - - return ( - -
- {t("users.domain")} - -
- {!noRecords && ( -
-
-
- - -
-
- )} - {noRecords ? ( - - ) : ( -
- {!isLoading && !users?.length ? ( -
- -
- ) : ( - - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - navigate(row.original.id)} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- )} - -
- )} -
- ) -} - -const UserActions = ({ user }: { user: Omit }) => { - const { t } = useTranslation() - - return ( - , - label: t("actions.edit"), - to: `${user.id}/edit`, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper>() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("email", { - header: t("fields.email"), - cell: ({ row }) => { - return row.original.email - }, - }), - columnHelper.display({ - id: "name", - header: t("fields.name"), - cell: ({ row }) => { - const name = [row.original.first_name, row.original.last_name] - .filter(Boolean) - .join(" ") - - if (!name) { - return - - } - - return name - }, - }), - columnHelper.accessor("role", { - header: t("fields.role"), - cell: ({ row }) => { - return t(`users.roles.${row.original.role}`) - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row }) => , - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/routes/users/user-list/index.ts b/packages/admin-next/dashboard/src/routes/users/user-list/index.ts deleted file mode 100644 index c6b98dbbbd32e..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserList as Component } from "./user-list" diff --git a/packages/admin-next/dashboard/src/routes/users/user-list/user-list.tsx b/packages/admin-next/dashboard/src/routes/users/user-list/user-list.tsx deleted file mode 100644 index c90a5215fd05b..0000000000000 --- a/packages/admin-next/dashboard/src/routes/users/user-list/user-list.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Outlet } from "react-router-dom" -import { UserListTable } from "./components/user-list-table" - -export const UserList = () => { - return ( -
- - -
- ) -} diff --git a/packages/admin-next/dashboard/src/types/api-payloads.ts b/packages/admin-next/dashboard/src/types/api-payloads.ts index 45183e28392a1..d4145fc916699 100644 --- a/packages/admin-next/dashboard/src/types/api-payloads.ts +++ b/packages/admin-next/dashboard/src/types/api-payloads.ts @@ -5,6 +5,7 @@ import { CreateApiKeyDTO, CreateCustomerDTO, + CreateInviteDTO, CreateRegionDTO, CreateSalesChannelDTO, UpdateApiKeyDTO, @@ -36,6 +37,11 @@ export type UpdateCustomerReq = UpdateCustomerDTO // Sales Channels export type CreateSalesChannelReq = CreateSalesChannelDTO export type UpdateSalesChannelReq = UpdateSalesChannelDTO +export type AddProductsSalesChannelReq = { product_ids: string[] } +export type RemoveProductsSalesChannelReq = { product_ids: string[] } // Users export type UpdateUserReq = Omit + +// Invites +export type CreateInviteReq = CreateInviteDTO diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index fe1dad5297a49..4e78eea3e2186 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -6,6 +6,12 @@ import { ApiKeyDTO, CurrencyDTO, CustomerDTO, + InviteDTO, + ProductCategoryDTO, + ProductCollectionDTO, + ProductDTO, + ProductTypeDTO, + ProductVariantDTO, PromotionDTO, RegionDTO, SalesChannelDTO, @@ -40,6 +46,7 @@ export type PromotionDeleteRes = DeleteRes // Users export type UserRes = { user: UserDTO } export type UserListRes = { users: UserDTO[] } & ListRes +export type UserDeleteRes = DeleteRes // Stores export type ExtendedStoreDTO = StoreDTO & { @@ -69,3 +76,23 @@ export type SalesChannelDeleteRes = DeleteRes // Currencies export type CurrencyRes = { currency: CurrencyDTO } export type CurrencyListRes = { currencies: CurrencyDTO[] } & ListRes + +// Invites +export type InviteRes = { invite: InviteDTO } +export type InviteListRes = { invites: InviteDTO[] } & ListRes +export type InviteDeleteRes = DeleteRes + +// Products +export type ExtendedProductDTO = ProductDTO & { + variants: ProductVariantDTO[] | null + sales_channels: SalesChannelDTO[] | null + collections: ProductCollectionDTO[] | null + categories: ProductCategoryDTO[] | null +} +export type ProductRes = { product: ExtendedProductDTO } +export type ProductListRes = { products: ExtendedProductDTO[] } & ListRes +export type ProductDeleteRes = DeleteRes + +// Product Types +export type ProductTypeRes = { product_type: ProductTypeDTO } +export type ProductTypeListRes = { product_types: ProductTypeDTO[] } & ListRes diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx index b3c24b2b6a20c..1e922c029bc5f 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/components/add-products-to-sales-channel-form.tsx @@ -1,43 +1,31 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { Product, SalesChannel } from "@medusajs/medusa" -import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui" +import { SalesChannelDTO } from "@medusajs/types" +import { Button, Checkbox, Hint, Tooltip } from "@medusajs/ui" +import { keepPreviousData } from "@tanstack/react-query" import { - PaginationState, + OnChangeFn, RowSelectionState, createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, } from "@tanstack/react-table" -import { - adminProductKeys, - adminSalesChannelsKeys, - useAdminCustomPost, - useAdminProducts, -} from "medusa-react" -import { useEffect, useMemo, useState } from "react" +import { useMemo, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { - ProductAvailabilityCell, - ProductCollectionCell, - ProductStatusCell, - ProductTitleCell, - ProductVariantCell, -} from "../../../../components/common/product-table-cells" -import { OrderBy } from "../../../../components/filtering/order-by" -import { Query } from "../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../components/localization/localized-table-pagination" import { RouteFocusModal, useRouteModal, } from "../../../../components/route-modal" -import { useQueryParams } from "../../../../hooks/use-query-params" -import { queryClient } from "../../../../lib/medusa" +import { DataTable } from "../../../../components/table/data-table" +import { useProducts } from "../../../../hooks/api/products" +import { useSalesChannelAddProducts } from "../../../../hooks/api/sales-channels" +import { useProductTableColumns } from "../../../../hooks/table/columns/use-product-table-columns" +import { useProductTableFilters } from "../../../../hooks/table/filters/use-product-table-filters" +import { useProductTableQuery } from "../../../../hooks/table/query/use-product-table-query" +import { useDataTable } from "../../../../hooks/use-data-table" +import { ExtendedProductDTO } from "../../../../types/api-responses" type AddProductsToSalesChannelFormProps = { - salesChannel: SalesChannel + salesChannel: SalesChannelDTO } const AddProductsToSalesChannelSchema = zod.object({ @@ -61,74 +49,53 @@ export const AddProductsToSalesChannelForm = ({ const { setValue } = form - const { mutateAsync, isLoading: isMutating } = useAdminCustomPost( - `/admin/sales-channels/${salesChannel.id}/products/batch/add`, - [ - adminSalesChannelsKeys.lists(), - adminSalesChannelsKeys.detail(salesChannel.id), - ] - ) + const { mutateAsync, isPending } = useSalesChannelAddProducts(salesChannel.id) - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) + const [rowSelection, setRowSelection] = useState({}) - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) + const updater: OnChangeFn = (fn) => { + const state = typeof fn === "function" ? fn(rowSelection) : fn - const [rowSelection, setRowSelection] = useState({}) + const ids = Object.keys(state) - useEffect(() => { - setValue( - "product_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]), - { - shouldDirty: true, - shouldTouch: true, - } - ) - }, [rowSelection, setValue]) + setValue("product_ids", ids, { + shouldDirty: true, + shouldTouch: true, + }) - const params = useQueryParams(["q", "order"]) + setRowSelection(state) + } - const { products, count } = useAdminProducts( + const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE }) + const { products, count, isLoading, isError, error } = useProducts( { expand: "variants,sales_channels", - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, + ...searchParams, }, { - keepPreviousData: true, + placeholderData: keepPreviousData, } ) const columns = useColumns() + const filters = useProductTableFilters(["sales_channel_id"]) - const table = useReactTable({ - data: (products ?? []) as Product[], + const { table } = useDataTable({ + data: products ?? [], columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - getRowId: (row) => row.id, - enableRowSelection(row) { + enableRowSelection: (row) => { return !row.original.sales_channels ?.map((sc) => sc.id) .includes(salesChannel.id) }, + enablePagination: true, + getRowId: (row) => row.id, + pageSize: PAGE_SIZE, + count, + rowSelection: { + state: rowSelection, + updater, + }, meta: { salesChannelId: salesChannel.id, }, @@ -141,17 +108,16 @@ export const AddProductsToSalesChannelForm = ({ }, { onSuccess: () => { - /** - * Invalidate the products list query to refetch products and - * determine if they are added to the sales channel or not. - */ - queryClient.invalidateQueries(adminProductKeys.lists()) handleSuccess() }, } ) }) + if (isError) { + throw error + } + return (
- -
-
-
- - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - sc.id) - .includes(salesChannel.id), - } - )} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
-
-
- -
+
) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { + const base = useProductTableColumns() const { t } = useTranslation() return useMemo( @@ -314,47 +221,8 @@ const useColumns = () => { return Component }, }), - columnHelper.accessor("title", { - header: t("fields.title"), - cell: ({ row }) => { - const product = row.original - - return - }, - }), - columnHelper.accessor("collection", { - header: t("fields.collection"), - cell: ({ getValue }) => { - const collection = getValue() - - return - }, - }), - columnHelper.accessor("sales_channels", { - header: t("fields.availability"), - cell: ({ getValue }) => { - const salesChannels = getValue() - - return - }, - }), - columnHelper.accessor("variants", { - header: t("fields.inventory"), - cell: (cell) => { - const variants = cell.getValue() - - return - }, - }), - columnHelper.accessor("status", { - header: t("fields.status"), - cell: ({ getValue }) => { - const status = getValue() - - return - }, - }), + ...base, ], - [t] + [t, base] ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx index 1b28c6d5d3fcd..1c68da5d53dde 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-add-products/sales-channel-add-products.tsx @@ -1,11 +1,11 @@ -import { useAdminSalesChannel } from "medusa-react" import { useParams } from "react-router-dom" import { RouteFocusModal } from "../../../components/route-modal" +import { useSalesChannel } from "../../../hooks/api/sales-channels" import { AddProductsToSalesChannelForm } from "./components" export const SalesChannelAddProducts = () => { const { id } = useParams() - const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) + const { sales_channel, isLoading, isError, error } = useSalesChannel(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx similarity index 95% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx index 1eb5c31e735e2..69d724c8c5abf 100644 --- a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/components/create-sales-channel-form/create-sales-channel-form.tsx @@ -1,6 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Button, Heading, Input, Switch, Text, Textarea } from "@medusajs/ui" -import { useAdminCreateSalesChannel } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" @@ -10,6 +9,7 @@ import { RouteFocusModal, useRouteModal, } from "../../../../../components/route-modal" +import { useCreateSalesChannel } from "../../../../../hooks/api/sales-channels" const CreateSalesChannelSchema = zod.object({ name: zod.string().min(1), @@ -29,7 +29,7 @@ export const CreateSalesChannelForm = () => { }, resolver: zodResolver(CreateSalesChannelSchema), }) - const { mutateAsync, isLoading } = useAdminCreateSalesChannel() + const { mutateAsync, isPending } = useCreateSalesChannel() const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( @@ -59,7 +59,7 @@ export const CreateSalesChannelForm = () => { {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-create/components/create-sales-channel-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/components/create-sales-channel-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-create/components/create-sales-channel-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/components/create-sales-channel-form/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/sales-channel-create.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/sales-channel-create.tsx index e1ee3874081e9..d4abcb0126301 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/sales-channel-create.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-create/sales-channel-create.tsx @@ -1,5 +1,5 @@ import { RouteFocusModal } from "../../../components/route-modal" -import { CreateSalesChannelForm } from "../../../modules/sales-channels/sales-channel-create/components/create-sales-channel-form" +import { CreateSalesChannelForm } from "./components/create-sales-channel-form" export const SalesChannelCreate = () => { return ( diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-detail/components/sales-channel-general-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-general-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-detail/components/sales-channel-general-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-general-section/index.ts diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx similarity index 74% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx index 13b5a33b7326f..6d9893b6b2d33 100644 --- a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-general-section/sales-channel-general-section.tsx @@ -1,24 +1,24 @@ import { PencilSquare, Trash } from "@medusajs/icons" -import { SalesChannel } from "@medusajs/medusa" import { Container, Heading, StatusBadge, Text, usePrompt } from "@medusajs/ui" -import { useAdminDeleteSalesChannel } from "medusa-react" import { useTranslation } from "react-i18next" +import { SalesChannelDTO } from "@medusajs/types" import { useNavigate } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useDeleteSalesChannel } from "../../../../../hooks/api/sales-channels" -type SalesChannelGeneralSection = { - salesChannel: SalesChannel +type SalesChannelGeneralSectionProps = { + salesChannel: SalesChannelDTO } export const SalesChannelGeneralSection = ({ salesChannel, -}: SalesChannelGeneralSection) => { +}: SalesChannelGeneralSectionProps) => { const { t } = useTranslation() const prompt = usePrompt() const navigate = useNavigate() - const { mutateAsync } = useAdminDeleteSalesChannel(salesChannel.id) + const { mutateAsync } = useDeleteSalesChannel(salesChannel.id) const handleDelete = async () => { const confirm = await prompt({ @@ -44,14 +44,9 @@ export const SalesChannelGeneralSection = ({ } return ( - +
-
- {salesChannel.name} - - {salesChannel.description} - -
+ {salesChannel.name}
{t(`general.${salesChannel.is_disabled ? "disabled" : "enabled"}`)} @@ -80,6 +75,14 @@ export const SalesChannelGeneralSection = ({ />
+
+ + {t("fields.description")} + + + {salesChannel.description || "-"} + +
) } diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx index 03304b3e1e610..a066320532c48 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/components/sales-channel-product-section/sales-channel-product-section.tsx @@ -1,115 +1,70 @@ import { PencilSquare, Trash } from "@medusajs/icons" -import { Product, SalesChannel } from "@medusajs/medusa" -import { - Button, - Checkbox, - CommandBar, - Container, - Heading, - Table, - clx, - usePrompt, -} from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { - adminProductKeys, - adminSalesChannelsKeys, - useAdminCustomPost, - useAdminDeleteProductsFromSalesChannel, - useAdminProducts, -} from "medusa-react" +import { Button, Checkbox, Container, Heading, usePrompt } from "@medusajs/ui" +import { RowSelectionState, createColumnHelper } from "@tanstack/react-table" import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { Link } from "react-router-dom" -import { - ProductStatusCell, - ProductTitleCell, - ProductVariantCell, -} from "../../../../../components/common/product-table-cells" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" - +import { SalesChannelDTO } from "@medusajs/types" +import { keepPreviousData } from "@tanstack/react-query" import { ActionMenu } from "../../../../../components/common/action-menu" -import { FilterGroup } from "../../../../../components/filtering/filter-group" -import { OrderBy } from "../../../../../components/filtering/order-by" -import { Query } from "../../../../../components/filtering/query" -import { useQueryParams } from "../../../../../hooks/use-query-params" -import { queryClient } from "../../../../../lib/medusa" +import { DataTable } from "../../../../../components/table/data-table" +import { useProducts } from "../../../../../hooks/api/products" +import { useSalesChannelRemoveProducts } from "../../../../../hooks/api/sales-channels" +import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns" +import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters" +import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { ExtendedProductDTO } from "../../../../../types/api-responses" const PAGE_SIZE = 10 -type SalesChannelProductSection = { - salesChannel: SalesChannel +type SalesChannelProductSectionProps = { + salesChannel: SalesChannelDTO } export const SalesChannelProductSection = ({ salesChannel, -}: SalesChannelProductSection) => { - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - +}: SalesChannelProductSectionProps) => { const [rowSelection, setRowSelection] = useState({}) - const params = useQueryParams(["q", "order"]) - const { products, count, isLoading, isError, error } = useAdminProducts( + const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE }) + const { products, count, isLoading, isError, error } = useProducts( { + ...searchParams, sales_channel_id: [salesChannel.id], - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, }, { - keepPreviousData: true, + placeholderData: keepPreviousData, } ) - const columns = useListColumns(salesChannel.id) + const columns = useColumns() + const filters = useProductTableFilters(["sales_channel_id"]) - const table = useReactTable({ - data: (products ?? []) as Product[], + const { table } = useDataTable({ + data: products ?? [], columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, + count, + enablePagination: true, + enableRowSelection: true, + pageSize: PAGE_SIZE, getRowId: (row) => row.id, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, + rowSelection: { + state: rowSelection, + updater: setRowSelection, + }, + meta: { + salesChannelId: salesChannel.id, + }, }) - const { mutateAsync } = useAdminCustomPost( - `/admin/sales-channels/${salesChannel.id}/products/batch/remove`, - [ - adminSalesChannelsKeys.lists(), - adminSalesChannelsKeys.detail(salesChannel.id), - ] - ) + const { mutateAsync } = useSalesChannelRemoveProducts(salesChannel.id) const prompt = usePrompt() - const { t } = useTranslation() - const onRemove = async () => { + const handleRemove = async () => { const ids = Object.keys(rowSelection) const result = await prompt({ @@ -133,7 +88,6 @@ export const SalesChannelProductSection = ({ { onSuccess: () => { setRowSelection({}) - queryClient.invalidateQueries(adminProductKeys.lists()) }, } ) @@ -153,99 +107,38 @@ export const SalesChannelProductSection = ({ -
- -
- - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
- - - - - {t("general.countSelected", { - count: Object.keys(rowSelection).length, - })} - - - - - -
+ `/products/${row.id}`} + isLoading={isLoading} + orderBy={["title", "variants", "status", "created_at", "updated_at"]} + queryObject={raw} + />
) } -const listColumnHelper = createColumnHelper() +const columnHelper = createColumnHelper() -const useListColumns = (id: string) => { - const { t } = useTranslation() +const useColumns = () => { + const base = useProductTableColumns() return useMemo( () => [ - listColumnHelper.display({ + columnHelper.display({ id: "select", header: ({ table }) => { return ( @@ -273,43 +166,24 @@ const useListColumns = (id: string) => { ) }, }), - listColumnHelper.accessor("title", { - header: t("fields.title"), - cell: ({ row }) => { - const product = row.original - - return - }, - }), - listColumnHelper.accessor("variants", { - header: t("fields.variants"), - cell: (cell) => { - const variants = cell.getValue() - - return - }, - }), - listColumnHelper.accessor("status", { - header: t("fields.status"), - cell: ({ getValue }) => { - const status = getValue() - - return - }, - }), - listColumnHelper.display({ + ...base, + columnHelper.display({ id: "actions", - cell: ({ row }) => { + cell: ({ row, table }) => { + const { salesChannelId } = table.options.meta as { + salesChannelId: string + } + return ( ) }, }), ], - [t] + [base] ) } @@ -322,13 +196,7 @@ const ProductListCellActions = ({ }) => { const { t } = useTranslation() - const { mutateAsync } = useAdminCustomPost( - `/admin/sales-channels/${salesChannelId}/products/batch/remove`, - [ - ...adminSalesChannelsKeys.lists(), - ...adminSalesChannelsKeys.detail(salesChannelId), - ] - ) + const { mutateAsync } = useSalesChannelRemoveProducts(salesChannelId) const onRemove = async () => { await mutateAsync({ diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/loader.ts index 5208c2e065737..f74c0f7b30306 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/loader.ts @@ -1,13 +1,13 @@ -import { AdminSalesChannelsRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" import { adminProductKeys } from "medusa-react" import { LoaderFunctionArgs } from "react-router-dom" -import { medusa, queryClient } from "../../../lib/medusa" +import { client } from "../../../lib/client" +import { queryClient } from "../../../lib/medusa" +import { SalesChannelRes } from "../../../types/api-responses" const salesChannelDetailQuery = (id: string) => ({ queryKey: adminProductKeys.detail(id), - queryFn: async () => medusa.admin.salesChannels.retrieve(id), + queryFn: async () => client.salesChannels.retrieve(id), }) export const salesChannelLoader = async ({ params }: LoaderFunctionArgs) => { @@ -15,7 +15,7 @@ export const salesChannelLoader = async ({ params }: LoaderFunctionArgs) => { const query = salesChannelDetailQuery(id!) return ( - queryClient.getQueryData>(query.queryKey) ?? + queryClient.getQueryData(query.queryKey) ?? (await queryClient.fetchQuery(query)) ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx index 9ba2161176adc..30448f72f7c23 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-detail/sales-channel-detail.tsx @@ -1,8 +1,8 @@ -import { useAdminSalesChannel } from "medusa-react" import { Outlet, useLoaderData, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" -import { SalesChannelGeneralSection } from "../../../modules/sales-channels/sales-channel-detail/components/sales-channel-general-section" +import { useSalesChannel } from "../../../hooks/api/sales-channels" +import { SalesChannelGeneralSection } from "./components/sales-channel-general-section" import { SalesChannelProductSection } from "./components/sales-channel-product-section" import { salesChannelLoader } from "./loader" @@ -12,7 +12,7 @@ export const SalesChannelDetail = () => { > const { id } = useParams() - const { sales_channel, isLoading } = useAdminSalesChannel(id!, { + const { sales_channel, isLoading } = useSalesChannel(id!, { initialData, }) diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx similarity index 92% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx index af2de9816db0a..edbfe3213341e 100644 --- a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/edit-sales-channel-form.tsx @@ -1,19 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { SalesChannel } from "@medusajs/medusa" import { Button, Input, Switch, Textarea } from "@medusajs/ui" -import { useAdminUpdateSalesChannel } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" +import { SalesChannelDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" +import { useUpdateSalesChannel } from "../../../../../hooks/api/sales-channels" type EditSalesChannelFormProps = { - salesChannel: SalesChannel + salesChannel: SalesChannelDTO } const EditSalesChannelSchema = zod.object({ @@ -37,7 +37,7 @@ export const EditSalesChannelForm = ({ resolver: zodResolver(EditSalesChannelSchema), }) - const { mutateAsync, isLoading } = useAdminUpdateSalesChannel(salesChannel.id) + const { mutateAsync, isPending } = useUpdateSalesChannel(salesChannel.id) const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( @@ -121,7 +121,7 @@ export const EditSalesChannelForm = ({ {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/components/edit-sales-channel-form/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx index bff248ecef7f2..be3129ccee3c0 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-edit/sales-channel-edit.tsx @@ -1,16 +1,16 @@ import { Heading } from "@medusajs/ui" -import { useAdminSalesChannel } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" -import { EditSalesChannelForm } from "../../../modules/sales-channels/sales-channel-edit/components/edit-sales-channel-form" +import { useSalesChannel } from "../../../hooks/api/sales-channels" +import { EditSalesChannelForm } from "./components/edit-sales-channel-form" export const SalesChannelEdit = () => { const { id } = useParams() const { t } = useTranslation() - const { sales_channel, isLoading, isError, error } = useAdminSalesChannel(id!) + const { sales_channel, isLoading, isError, error } = useSalesChannel(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-list/components/index.ts b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/components/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/sales-channels/sales-channel-list/components/index.ts rename to packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/components/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx new file mode 100644 index 0000000000000..59946ff27b414 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/components/sales-channel-list-table.tsx @@ -0,0 +1,146 @@ +import { PencilSquare, Trash } from "@medusajs/icons" +import { SalesChannelDTO } from "@medusajs/types" +import { Button, Container, Heading, usePrompt } from "@medusajs/ui" +import { keepPreviousData } from "@tanstack/react-query" +import { createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" +import { Link } from "react-router-dom" +import { ActionMenu } from "../../../../components/common/action-menu" +import { DataTable } from "../../../../components/table/data-table" +import { + useDeleteSalesChannel, + useSalesChannels, +} from "../../../../hooks/api/sales-channels" +import { useSalesChannelTableColumns } from "../../../../hooks/table/columns/use-sales-channel-table-columns" +import { useSalesChannelTableQuery } from "../../../../hooks/table/query/use-sales-channel-table-query" +import { useDataTable } from "../../../../hooks/use-data-table" + +const PAGE_SIZE = 20 + +export const SalesChannelListTable = () => { + const { t } = useTranslation() + + const { raw, searchParams } = useSalesChannelTableQuery({ + pageSize: PAGE_SIZE, + }) + const { sales_channels, count, isLoading, isError, error } = useSalesChannels( + searchParams, + { + placeholderData: keepPreviousData, + } + ) + + const columns = useColumns() + + const { table } = useDataTable({ + data: sales_channels ?? [], + columns, + count, + enablePagination: true, + getRowId: (row) => row.id, + pageSize: PAGE_SIZE, + }) + + if (isError) { + throw error + } + + return ( + +
+ {t("salesChannels.domain")} + + + +
+ row.id} + isLoading={isLoading} + queryObject={raw} + orderBy={["name", "created_at", "updated_at"]} + /> +
+ ) +} + +const SalesChannelActions = ({ + salesChannel, +}: { + salesChannel: SalesChannelDTO +}) => { + const { t } = useTranslation() + const prompt = usePrompt() + const { mutateAsync } = useDeleteSalesChannel(salesChannel.id) + + const handleDelete = async () => { + const confirm = await prompt({ + title: t("general.areYouSure"), + description: t("salesChannels.deleteSalesChannelWarning", { + name: salesChannel.name, + }), + verificationInstruction: t("general.typeToConfirm"), + verificationText: salesChannel.name, + confirmText: t("actions.delete"), + cancelText: t("actions.cancel"), + }) + + if (!confirm) { + return + } + + await mutateAsync() + } + + return ( + , + label: t("actions.edit"), + to: `/settings/sales-channels/${salesChannel.id}/edit`, + }, + ], + }, + { + actions: [ + { + icon: , + label: t("actions.delete"), + onClick: handleDelete, + }, + ], + }, + ]} + /> + ) +} + +const columnHelper = createColumnHelper() + +const useColumns = () => { + const base = useSalesChannelTableColumns() + + return useMemo( + () => [ + ...base, + columnHelper.display({ + id: "actions", + cell: ({ row }) => { + return + }, + }), + ], + [base] + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/sales-channel-list.tsx b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/sales-channel-list.tsx index 9088b57b3cf65..9ae07a1ac2a94 100644 --- a/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/sales-channel-list.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/sales-channels/sales-channel-list/sales-channel-list.tsx @@ -1,5 +1,5 @@ import { Outlet } from "react-router-dom" -import { SalesChannelListTable } from "../../../modules/sales-channels/sales-channel-list/components" +import { SalesChannelListTable } from "./components/sales-channel-list-table" export const SalesChannelList = () => { return ( diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-detail/components/user-general-section/user-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-detail/components/user-general-section/user-general-section.tsx index 8bb7871bf8ce4..b5e35ca8e248c 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-detail/components/user-general-section/user-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-detail/components/user-general-section/user-general-section.tsx @@ -1,13 +1,13 @@ import { PencilSquare, Trash } from "@medusajs/icons" -import { User } from "@medusajs/medusa" +import { UserDTO } from "@medusajs/types" import { Container, Heading, Text, clx, usePrompt } from "@medusajs/ui" -import { useAdminDeleteUser } from "medusa-react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useDeleteUser } from "../../../../../hooks/api/users" type UserGeneralSectionProps = { - user: Omit + user: UserDTO } export const UserGeneralSection = ({ user }: UserGeneralSectionProps) => { @@ -15,7 +15,7 @@ export const UserGeneralSection = ({ user }: UserGeneralSectionProps) => { const navigate = useNavigate() const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteUser(user.id) + const { mutateAsync } = useDeleteUser(user.id) const name = [user.first_name, user.last_name].filter(Boolean).join(" ") diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/users/user-detail/loader.ts index 1dbeb58ba85a8..026b647c93326 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-detail/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-detail/loader.ts @@ -1,13 +1,13 @@ -import { AdminUserRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" import { adminProductKeys } from "medusa-react" import { LoaderFunctionArgs } from "react-router-dom" -import { medusa, queryClient } from "../../../lib/medusa" +import { client } from "../../../lib/client" +import { queryClient } from "../../../lib/medusa" +import { UserRes } from "../../../types/api-responses" const userDetailQuery = (id: string) => ({ queryKey: adminProductKeys.detail(id), - queryFn: async () => medusa.admin.users.retrieve(id), + queryFn: async () => client.users.retrieve(id), }) export const userLoader = async ({ params }: LoaderFunctionArgs) => { @@ -15,7 +15,7 @@ export const userLoader = async ({ params }: LoaderFunctionArgs) => { const query = userDetailQuery(id!) return ( - queryClient.getQueryData>(query.queryKey) ?? + queryClient.getQueryData(query.queryKey) ?? (await queryClient.fetchQuery(query)) ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-detail/user-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-detail/user-detail.tsx index 460d6ba81f335..8b8ffc0ab93fb 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-detail/user-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-detail/user-detail.tsx @@ -1,6 +1,6 @@ -import { useAdminUser } from "medusa-react" import { Outlet, json, useLoaderData, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" +import { useUser } from "../../../hooks/api/users" import { UserGeneralSection } from "./components/user-general-section" import { userLoader } from "./loader" @@ -8,7 +8,7 @@ export const UserDetail = () => { const initialData = useLoaderData() as Awaited> const { id } = useParams() - const { user, isLoading, isError, error } = useAdminUser(id!, { + const { user, isLoading, isError, error } = useUser(id!, undefined, { initialData, }) diff --git a/packages/admin-next/dashboard/src/modules/user/user-edit/components/edit-user-form/edit-user-form.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/components/edit-user-form/edit-user-form.tsx similarity index 91% rename from packages/admin-next/dashboard/src/modules/user/user-edit/components/edit-user-form/edit-user-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/users/user-edit/components/edit-user-form/edit-user-form.tsx index b9f677d805e46..f0c0cad5ecee2 100644 --- a/packages/admin-next/dashboard/src/modules/user/user-edit/components/edit-user-form/edit-user-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/components/edit-user-form/edit-user-form.tsx @@ -1,19 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { User } from "@medusajs/medusa" import { Button, Input } from "@medusajs/ui" -import { useAdminUpdateUser } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" +import { UserDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" +import { useUpdateUser } from "../../../../../hooks/api/users" type EditUserFormProps = { - user: Omit + user: UserDTO } const EditUserFormSchema = zod.object({ @@ -33,7 +33,7 @@ export const EditUserForm = ({ user }: EditUserFormProps) => { resolver: zodResolver(EditUserFormSchema), }) - const { mutateAsync, isLoading } = useAdminUpdateUser(user.id) + const { mutateAsync, isPending } = useUpdateUser(user.id) const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync(values, { @@ -88,7 +88,7 @@ export const EditUserForm = ({ user }: EditUserFormProps) => { {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/modules/user/user-edit/components/edit-user-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/components/edit-user-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/user/user-edit/components/edit-user-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/users/user-edit/components/edit-user-form/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-edit/index.ts b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/index.ts index 50a4319a8d27b..ca520c7d9ea72 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-edit/index.ts +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/index.ts @@ -1 +1 @@ -export { UserEdit as Component } from "../../../modules/user/user-edit" +export { UserEdit as Component } from "./user-edit" diff --git a/packages/admin-next/dashboard/src/modules/user/user-edit/index.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/user-edit.tsx similarity index 84% rename from packages/admin-next/dashboard/src/modules/user/user-edit/index.tsx rename to packages/admin-next/dashboard/src/v2-routes/users/user-edit/user-edit.tsx index 8d51a1f0b772a..8822dbf8d23fe 100644 --- a/packages/admin-next/dashboard/src/modules/user/user-edit/index.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-edit/user-edit.tsx @@ -1,14 +1,14 @@ import { Heading } from "@medusajs/ui" -import { useAdminUser } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" +import { useUser } from "../../../hooks/api/users" import { EditUserForm } from "./components/edit-user-form" export const UserEdit = () => { const { t } = useTranslation() const { id } = useParams() - const { user, isLoading, isError, error } = useAdminUser(id!) + const { user, isLoading, isError, error } = useUser(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-invite/components/invite-user-form/invite-user-form.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-invite/components/invite-user-form/invite-user-form.tsx index 504868ab4b0d0..6204c214d05cc 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-invite/components/invite-user-form/invite-user-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-invite/components/invite-user-form/invite-user-form.tsx @@ -1,6 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { ArrowPath, Link, XCircle } from "@medusajs/icons" -import { Invite } from "@medusajs/medusa" +import { ArrowPath, Trash } from "@medusajs/icons" +import { InviteDTO } from "@medusajs/types" import { Button, Container, @@ -21,13 +21,6 @@ import { useReactTable, } from "@tanstack/react-table" import { format } from "date-fns" -import { - adminInviteKeys, - useAdminCustomPost, - useAdminDeleteInvite, - useAdminInvites, - useAdminResendInvite, -} from "medusa-react" import { useMemo } from "react" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" @@ -37,8 +30,12 @@ import { NoRecords } from "../../../../../components/common/empty-table-content" import { Form } from "../../../../../components/common/form" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" import { RouteFocusModal } from "../../../../../components/route-modal" -import { useV2Store } from "../../../../../lib/api-v2" -import { InviteDTO } from "@medusajs/types" +import { + useCreateInvite, + useDeleteInvite, + useInvites, + useResendInvite, +} from "../../../../../hooks/api/invites" const InviteUserSchema = zod.object({ email: zod.string().email(), @@ -56,7 +53,7 @@ export const InviteUserForm = () => { resolver: zodResolver(InviteUserSchema), }) - const { invites, isLoading, isError, error } = useAdminInvites() + const { invites, isLoading, isError, error } = useInvites() const count = invites?.length ?? 0 const noRecords = !isLoading && count === 0 @@ -72,10 +69,7 @@ export const InviteUserForm = () => { getPaginationRowModel: getPaginationRowModel(), }) - const { mutateAsync, isLoading: isMutating } = useAdminCustomPost( - "/admin/invites", - adminInviteKeys.lists() - ) + const { mutateAsync, isPending } = useCreateInvite() const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( @@ -133,7 +127,7 @@ export const InviteUserForm = () => { size="small" variant="secondary" type="submit" - isLoading={isMutating} + isLoading={isPending} > {t("users.sendInvite")} @@ -215,20 +209,20 @@ export const InviteUserForm = () => { } const InviteActions = ({ invite }: { invite: InviteDTO }) => { - const { mutateAsync: revokeAsync } = useAdminDeleteInvite(invite.id) - const { mutateAsync: resendAsync } = useAdminResendInvite(invite.id) - const { store, isLoading, isError, error } = useV2Store({}) + const { mutateAsync: revokeAsync } = useDeleteInvite(invite.id) + const { mutateAsync: resendAsync } = useResendInvite(invite.id) + const prompt = usePrompt() const { t } = useTranslation() - const handleRevoke = async () => { + const handleDelete = async () => { const res = await prompt({ title: t("general.areYouSure"), - description: t("users.revokeInviteWarning", { + description: t("users.deleteInviteWarning", { email: invite.email, }), cancelText: t("actions.cancel"), - confirmText: t("actions.revoke"), + confirmText: t("actions.delete"), }) if (!res) { @@ -242,32 +236,11 @@ const InviteActions = ({ invite }: { invite: InviteDTO }) => { await resendAsync() } - const handleCopyInviteLink = () => { - const template = store?.invite_link_template - - if (!template) { - return - } - - const link = template.replace("{invite_token}", invite.token) - navigator.clipboard.writeText(link) - } - - if (isError) { - throw error - } - return ( , - label: t("users.copyInviteLink"), - disabled: isLoading || !store?.invite_link_template, - onClick: handleCopyInviteLink, - }, { icon: , label: t("users.resendInvite"), @@ -278,9 +251,9 @@ const InviteActions = ({ invite }: { invite: InviteDTO }) => { { actions: [ { - icon: , - label: t("actions.revoke"), - onClick: handleRevoke, + icon: , + label: t("actions.delete"), + onClick: handleDelete, }, ], }, diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-columns.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-columns.tsx new file mode 100644 index 0000000000000..41537300d5363 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-columns.tsx @@ -0,0 +1,42 @@ +import { UserDTO } from "@medusajs/types" +import { createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" +import { UserRowActions } from "./user-row-actions" + +const columnHelper = createColumnHelper() + +export const useUserTableColumns = () => { + const { t } = useTranslation() + + return useMemo( + () => [ + columnHelper.accessor("email", { + header: t("fields.email"), + cell: ({ row }) => { + return row.original.email + }, + }), + columnHelper.display({ + id: "name", + header: t("fields.name"), + cell: ({ row }) => { + const name = [row.original.first_name, row.original.last_name] + .filter(Boolean) + .join(" ") + + if (!name) { + return - + } + + return name + }, + }), + columnHelper.display({ + id: "actions", + cell: ({ row }) => , + }), + ], + [t] + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-query.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-query.tsx new file mode 100644 index 0000000000000..070663be15d20 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/use-user-table-query.tsx @@ -0,0 +1,24 @@ +import { useQueryParams } from "../../../../../hooks/use-query-params" + +export const useUserTableQuery = ({ + pageSize = 20, + prefix, +}: { + pageSize?: number + prefix?: string +}) => { + const raw = useQueryParams(["q", "order", "offset"], prefix) + + const { offset, ...params } = raw + + const searchParams = { + limit: pageSize, + offset: offset ? parseInt(offset) : 0, + ...params, + } + + return { + searchParams, + raw, + } +} diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx index 6aa6437952ebc..62fa9b9363515 100644 --- a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-list-table.tsx @@ -1,77 +1,35 @@ -import { PencilSquare } from "@medusajs/icons" -import { UserDTO } from "@medusajs/types" -import { Button, Container, Heading, Table, clx } from "@medusajs/ui" +import { Button, Container, Heading } from "@medusajs/ui" import { keepPreviousData } from "@tanstack/react-query" -import { - PaginationState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" -import { Link, useNavigate } from "react-router-dom" -import { ActionMenu } from "../../../../../components/common/action-menu" -import { - NoRecords, - NoResults, -} from "../../../../../components/common/empty-table-content" -import { OrderBy } from "../../../../../components/filtering/order-by" -import { Query } from "../../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +import { Link } from "react-router-dom" +import { DataTable } from "../../../../../components/table/data-table" import { useUsers } from "../../../../../hooks/api/users" -import { useQueryParams } from "../../../../../hooks/use-query-params" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { useUserTableColumns } from "./use-user-table-columns" +import { useUserTableQuery } from "./use-user-table-query" -const PAGE_SIZE = 50 +const PAGE_SIZE = 20 export const UserListTable = () => { - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, + const { raw, searchParams } = useUserTableQuery({ pageSize: PAGE_SIZE, }) + const { users, count, isLoading, isError, error } = useUsers(searchParams, { + placeholderData: keepPreviousData, + }) - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const params = useQueryParams(["q", "order"]) - const { users, count, isLoading, isError, error } = useUsers( - { - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, - }, - { - placeholderData: keepPreviousData, - } - ) - - const columns = useColumns() + const columns = useUserTableColumns() - const table = useReactTable({ + const { table } = useDataTable({ data: users ?? [], columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - }, - onPaginationChange: setPagination, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, + count, + enablePagination: true, + getRowId: (row) => row.id, + pageSize: PAGE_SIZE, }) const { t } = useTranslation() - const navigate = useNavigate() - - const noRecords = - !isLoading && - !users?.length && - !Object.values(params).filter(Boolean).length if (isError) { throw error @@ -85,149 +43,18 @@ export const UserListTable = () => { {t("users.invite")} - {!noRecords && ( -
-
-
- - -
-
- )} - {noRecords ? ( - - ) : ( -
- {!isLoading && !users?.length ? ( -
- -
- ) : ( - - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - navigate(row.original.id)} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- )} - -
- )} + `${row.id}`} + search + pagination + queryObject={raw} + /> ) } - -const UserActions = ({ user }: { user: UserDTO }) => { - const { t } = useTranslation() - - return ( - , - label: t("actions.edit"), - to: `${user.id}/edit`, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("email", { - header: t("fields.email"), - cell: ({ row }) => { - return row.original.email - }, - }), - columnHelper.display({ - id: "name", - header: t("fields.name"), - cell: ({ row }) => { - const name = [row.original.first_name, row.original.last_name] - .filter(Boolean) - .join(" ") - - if (!name) { - return - - } - - return name - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row }) => , - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-row-actions.tsx b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-row-actions.tsx new file mode 100644 index 0000000000000..c5d3f1beb82c5 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/users/user-list/components/user-list-table/user-row-actions.tsx @@ -0,0 +1,24 @@ +import { PencilSquare } from "@medusajs/icons" +import { UserDTO } from "@medusajs/types" +import { useTranslation } from "react-i18next" +import { ActionMenu } from "../../../../../components/common/action-menu" + +export const UserRowActions = ({ user }: { user: UserDTO }) => { + const { t } = useTranslation() + + return ( + , + label: t("actions.edit"), + to: `${user.id}/edit`, + }, + ], + }, + ]} + /> + ) +} diff --git a/yarn.lock b/yarn.lock index 794f2f19d6e11..7726e21bcf4a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8159,7 +8159,7 @@ __metadata: "@medusajs/vite-plugin-extension": "workspace:^" "@radix-ui/react-collapsible": 1.0.3 "@radix-ui/react-hover-card": ^1.0.7 - "@tanstack/react-query": 4.22.0 + "@tanstack/react-query": ^5.28.14 "@tanstack/react-table": 8.10.7 "@tanstack/react-virtual": ^3.0.4 "@types/node": ^20.11.15 @@ -8178,6 +8178,7 @@ __metadata: medusa-react: "workspace:^" postcss: ^8.4.33 prettier: ^3.1.1 + qs: ^6.12.0 react: 18.2.0 react-country-flag: ^3.1.0 react-dom: 18.2.0 @@ -17074,6 +17075,13 @@ __metadata: languageName: node linkType: hard +"@tanstack/query-core@npm:5.28.13": + version: 5.28.13 + resolution: "@tanstack/query-core@npm:5.28.13" + checksum: 4eb2c162a05948caf556d84ee65a7d640786adcfdc3fbefaf3e010ab462bdfb3ea7bdb200829ca33ccc60699a9a6d9414e7d6a3ea92b819a5de25337ea07b0d7 + languageName: node + linkType: hard + "@tanstack/react-query@npm:4.22.0": version: 4.22.0 resolution: "@tanstack/react-query@npm:4.22.0" @@ -17093,6 +17101,17 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-query@npm:^5.28.14": + version: 5.28.14 + resolution: "@tanstack/react-query@npm:5.28.14" + dependencies: + "@tanstack/query-core": 5.28.13 + peerDependencies: + react: ^18.0.0 + checksum: 34d269be1653d1ac313603c145540e15c5bd94da012b0fafda2f2d90bbc0712a213e41d4335d5b2f2fd54ac2f238dd0582aeb617b71edba5046235ec6bc69939 + languageName: node + linkType: hard + "@tanstack/react-table@npm:8.10.7": version: 8.10.7 resolution: "@tanstack/react-table@npm:8.10.7" @@ -22718,6 +22737,19 @@ __metadata: languageName: node linkType: hard +"call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" + dependencies: + es-define-property: ^1.0.0 + es-errors: ^1.3.0 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.4 + set-function-length: ^1.2.1 + checksum: a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d + languageName: node + linkType: hard + "call-me-maybe@npm:^1.0.1": version: 1.0.2 resolution: "call-me-maybe@npm:1.0.2" @@ -25403,6 +25435,17 @@ __metadata: languageName: node linkType: hard +"define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: ^1.0.0 + es-errors: ^1.3.0 + gopd: ^1.0.1 + checksum: dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + "define-lazy-prop@npm:^2.0.0": version: 2.0.0 resolution: "define-lazy-prop@npm:2.0.0" @@ -26526,6 +26569,22 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: ^1.2.4 + checksum: 6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + "es-get-iterator@npm:^1.0.2, es-get-iterator@npm:^1.1.3": version: 1.1.3 resolution: "es-get-iterator@npm:1.1.3" @@ -30027,6 +30086,19 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" + dependencies: + es-errors: ^1.3.0 + function-bind: ^1.1.2 + has-proto: ^1.0.1 + has-symbols: ^1.0.3 + hasown: ^2.0.0 + checksum: 0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7 + languageName: node + linkType: hard + "get-nonce@npm:^1.0.0": version: 1.0.1 resolution: "get-nonce@npm:1.0.1" @@ -30790,6 +30862,15 @@ __metadata: languageName: node linkType: hard +"has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: ^1.0.0 + checksum: 253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + "has-proto@npm:^1.0.1": version: 1.0.1 resolution: "has-proto@npm:1.0.1" @@ -40369,6 +40450,13 @@ __metadata: languageName: node linkType: hard +"object-inspect@npm:^1.13.1": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: fad603f408e345c82e946abdf4bfd774260a5ed3e5997a0b057c44153ac32c7271ff19e3a5ae39c858da683ba045ccac2f65245c12763ce4e8594f818f4a648d + languageName: node + linkType: hard + "object-is@npm:^1.1.5": version: 1.1.5 resolution: "object-is@npm:1.1.5" @@ -43533,6 +43621,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.12.0": + version: 6.12.0 + resolution: "qs@npm:6.12.0" + dependencies: + side-channel: ^1.0.6 + checksum: e165a77ac5f3ca60c15c5f3d51b321ddec7aa438804436b29d160117bc6fb7bf7dab94abd0c7d7c0785890d3a75ae41e1d6346e158aaf1540c6fe53a31f11675 + languageName: node + linkType: hard + "qs@npm:~6.5.2": version: 6.5.3 resolution: "qs@npm:6.5.3" @@ -46677,6 +46774,20 @@ __metadata: languageName: node linkType: hard +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: ^1.1.4 + es-errors: ^1.3.0 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.4 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.2 + checksum: 82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + "set-value@npm:^2.0.0, set-value@npm:^2.0.1": version: 2.0.1 resolution: "set-value@npm:2.0.1" @@ -46882,6 +46993,18 @@ __metadata: languageName: node linkType: hard +"side-channel@npm:^1.0.6": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" + dependencies: + call-bind: ^1.0.7 + es-errors: ^1.3.0 + get-intrinsic: ^1.2.4 + object-inspect: ^1.13.1 + checksum: d2afd163dc733cc0a39aa6f7e39bf0c436293510dbccbff446733daeaf295857dbccf94297092ec8c53e2503acac30f0b78830876f0485991d62a90e9cad305f + languageName: node + linkType: hard + "siginfo@npm:^2.0.0": version: 2.0.0 resolution: "siginfo@npm:2.0.0" From 2e6eb94b5aeb6dd09eb18ff111f164253a0027f5 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 16:17:42 +0200 Subject: [PATCH 3/7] add worflows --- .../dashboard/src/hooks/api/promotions.tsx | 19 +- .../src/hooks/api/stock-locations.tsx | 117 ++++++++ .../src/hooks/api/workflow-executions.tsx | 55 ++++ .../dashboard/src/lib/client/client.ts | 4 + .../dashboard/src/lib/client/promotions.ts | 7 +- .../src/lib/client/stock-locations.ts | 41 +++ .../src/lib/client/workflow-executions.ts | 27 ++ .../location-sales-channel-section.tsx | 248 ----------------- .../locations-list-table.tsx | 262 ------------------ .../dashboard/src/types/api-payloads.ts | 6 + .../dashboard/src/types/api-responses.ts | 20 ++ .../location-add-sales-channels.tsx | 3 +- .../create-location-form.tsx | 6 +- .../components/create-location-form/index.ts | 0 .../location-create/location-create.tsx | 2 +- .../location-general-section/index.ts | 0 .../location-general-section.tsx | 31 ++- .../location-sales-channel-section/index.ts | 0 .../location-sales-channel-section.tsx | 139 ++++++++++ .../location-detail/location-detail.tsx | 25 +- .../edit-location-form/edit-location-form.tsx | 35 ++- .../components/edit-location-form/index.ts | 1 + .../locations/location-edit/location-edit.tsx | 18 +- .../components/locations-list-table/index.ts | 0 .../location-row-actions.tsx | 57 ++++ .../locations-list-table.tsx | 66 +++++ .../use-location-table-columns.tsx | 37 +++ .../use-location-table-query.tsx | 21 ++ .../locations/location-list/location-list.tsx | 2 +- .../promotion-list-table.tsx | 42 +-- .../workflow-execution-detail/loader.ts | 10 +- .../workflow-detail.tsx | 22 +- .../workflow-execution-list-table.tsx | 35 +-- 33 files changed, 715 insertions(+), 643 deletions(-) create mode 100644 packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/api/workflow-executions.tsx create mode 100644 packages/admin-next/dashboard/src/lib/client/stock-locations.ts create mode 100644 packages/admin-next/dashboard/src/lib/client/workflow-executions.ts delete mode 100644 packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx delete mode 100644 packages/admin-next/dashboard/src/modules/locations/location-list/components/locations-list-table/locations-list-table.tsx rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-create/components/create-location-form/create-location-form.tsx (97%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-create/components/create-location-form/index.ts (100%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-detail/components/location-general-section/index.ts (100%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-detail/components/location-general-section/location-general-section.tsx (78%) rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-detail/components/location-sales-channel-section/index.ts (100%) create mode 100644 packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-edit/components/edit-location-form/edit-location-form.tsx (91%) create mode 100644 packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/index.ts rename packages/admin-next/dashboard/src/{modules => v2-routes}/locations/location-list/components/locations-list-table/index.ts (100%) create mode 100644 packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/location-row-actions.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/locations-list-table.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-columns.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-query.tsx diff --git a/packages/admin-next/dashboard/src/hooks/api/promotions.tsx b/packages/admin-next/dashboard/src/hooks/api/promotions.tsx index 8d8f095f05c4d..1ee3ade1879ca 100644 --- a/packages/admin-next/dashboard/src/hooks/api/promotions.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/promotions.tsx @@ -1,8 +1,9 @@ import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { AdminGetPromotionsParams } from "@medusajs/medusa" import { client } from "../../lib/client" import { queryKeysFactory } from "../../lib/query-key-factory" -import { PromotionRes } from "../../types/api-responses" +import { PromotionListRes, PromotionRes } from "../../types/api-responses" const PROMOTIONS_QUERY_KEY = "promotions" as const const promotionsQueryKeys = queryKeysFactory(PROMOTIONS_QUERY_KEY) @@ -22,3 +23,19 @@ export const usePromotion = ( return { ...data, ...rest } } + +export const usePromotions = ( + query?: AdminGetPromotionsParams, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: promotionsQueryKeys.list(query), + queryFn: async () => client.promotions.list(query), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx b/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx new file mode 100644 index 0000000000000..a1205fa462e14 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx @@ -0,0 +1,117 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" + +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { + CreateStockLocationReq, + UpdateStockLocationReq, +} from "../../types/api-payloads" +import { + StockLocationDeleteRes, + StockLocationListRes, + StockLocationRes, +} from "../../types/api-responses" + +const STOCK_LOCATIONS_QUERY_KEY = "stock_locations" as const +const stockLocationsQueryKeys = queryKeysFactory(STOCK_LOCATIONS_QUERY_KEY) + +export const useStockLocation = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.stockLocations.retrieve(id, query), + queryKey: stockLocationsQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useStockLocations = ( + query?: Record, + options?: Omit< + UseQueryOptions< + StockLocationListRes, + Error, + StockLocationListRes, + QueryKey + >, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.stockLocations.list(query), + queryKey: stockLocationsQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateStockLocation = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.stockLocations.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: stockLocationsQueryKeys.lists(), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateStockLocation = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.stockLocations.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: stockLocationsQueryKeys.detail(id), + }) + queryClient.invalidateQueries({ + queryKey: stockLocationsQueryKeys.lists(), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteStockLocation = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.stockLocations.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: stockLocationsQueryKeys.lists(), + }) + queryClient.invalidateQueries({ + queryKey: stockLocationsQueryKeys.detail(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/workflow-executions.tsx b/packages/admin-next/dashboard/src/hooks/api/workflow-executions.tsx new file mode 100644 index 0000000000000..d059f26babe42 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/workflow-executions.tsx @@ -0,0 +1,55 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { + WorkflowExecutionListRes, + WorkflowExecutionRes, +} from "../../types/api-responses" + +const WORKFLOW_EXECUTIONS_QUERY_KEY = "workflow_executions" as const +const workflowExecutionsQueryKeys = queryKeysFactory( + WORKFLOW_EXECUTIONS_QUERY_KEY +) + +export const useWorkflowExecutions = ( + query?: Record, + options?: Omit< + UseQueryOptions< + WorkflowExecutionListRes, + Error, + WorkflowExecutionListRes, + QueryKey + >, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.workflowExecutions.list(query), + queryKey: workflowExecutionsQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useWorkflowExecution = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions< + WorkflowExecutionRes, + Error, + WorkflowExecutionRes, + QueryKey + >, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.workflowExecutions.retrieve(id, query), + queryKey: workflowExecutionsQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index 9263134603f20..13d241e0d916d 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -8,8 +8,10 @@ import { products } from "./products" import { promotions } from "./promotions" import { regions } from "./regions" import { salesChannels } from "./sales-channels" +import { stockLocations } from "./stock-locations" import { stores } from "./stores" import { users } from "./users" +import { workflowExecutions } from "./workflow-executions" export const client = { auth: auth, @@ -24,4 +26,6 @@ export const client = { invites: invites, products: products, productTypes: productTypes, + stockLocations: stockLocations, + workflowExecutions: workflowExecutions, } diff --git a/packages/admin-next/dashboard/src/lib/client/promotions.ts b/packages/admin-next/dashboard/src/lib/client/promotions.ts index 574bb819a9c8f..0912f615327f7 100644 --- a/packages/admin-next/dashboard/src/lib/client/promotions.ts +++ b/packages/admin-next/dashboard/src/lib/client/promotions.ts @@ -1,6 +1,6 @@ import { AdminGetPromotionsParams } from "@medusajs/medusa" -import { PromotionRes } from "../../types/api-responses" +import { PromotionListRes, PromotionRes } from "../../types/api-responses" import { getRequest } from "./common" const retrievePromotion = async ( @@ -13,6 +13,11 @@ const retrievePromotion = async ( ) } +const listPromotions = async (query?: AdminGetPromotionsParams) => { + return getRequest(`/admin/promotions`, query) +} + export const promotions = { retrieve: retrievePromotion, + list: listPromotions, } diff --git a/packages/admin-next/dashboard/src/lib/client/stock-locations.ts b/packages/admin-next/dashboard/src/lib/client/stock-locations.ts new file mode 100644 index 0000000000000..47af3490c0a7b --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/stock-locations.ts @@ -0,0 +1,41 @@ +import { + CreateStockLocationReq, + UpdateStockLocationReq, +} from "../../types/api-payloads" +import { + StockLocationDeleteRes, + StockLocationListRes, + StockLocationRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" + +async function listStockLocations(query?: Record) { + return getRequest(`/admin/stock-locations`, query) +} + +async function retrieveStockLocation(id: string, query?: Record) { + return getRequest(`/admin/stock-locations/${id}`, query) +} + +async function createStockLocation(payload: CreateStockLocationReq) { + return postRequest(`/admin/stock-locations`, payload) +} + +async function updateStockLocation( + id: string, + payload: UpdateStockLocationReq +) { + return postRequest(`/admin/stock-locations/${id}`, payload) +} + +async function deleteStockLocation(id: string) { + return deleteRequest(`/admin/stock-locations/${id}`) +} + +export const stockLocations = { + list: listStockLocations, + retrieve: retrieveStockLocation, + create: createStockLocation, + update: updateStockLocation, + delete: deleteStockLocation, +} diff --git a/packages/admin-next/dashboard/src/lib/client/workflow-executions.ts b/packages/admin-next/dashboard/src/lib/client/workflow-executions.ts new file mode 100644 index 0000000000000..30e14ad895ed0 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/workflow-executions.ts @@ -0,0 +1,27 @@ +import { + WorkflowExecutionListRes, + WorkflowExecutionRes, +} from "../../types/api-responses" +import { getRequest } from "./common" + +async function retrieveWorkflowExecution( + id: string, + query?: Record +) { + return getRequest( + `/admin/workflows-executions/${id}`, + query + ) +} + +async function listWorkflowExecutions(query?: Record) { + return getRequest( + `/admin/workflows-executions`, + query + ) +} + +export const workflowExecutions = { + retrieve: retrieveWorkflowExecution, + list: listWorkflowExecutions, +} diff --git a/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx b/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx deleted file mode 100644 index 582ade8b4a6d1..0000000000000 --- a/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx +++ /dev/null @@ -1,248 +0,0 @@ -import { PencilSquare, Trash } from "@medusajs/icons" -import { SalesChannel } from "@medusajs/medusa" -import { StockLocationExpandedDTO } from "@medusajs/types" -import { - Button, - Container, - Heading, - StatusBadge, - Table, - clx, - usePrompt, -} from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { useAdminRemoveLocationFromSalesChannel } from "medusa-react" -import { useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import { Link, useNavigate } from "react-router-dom" -import { ActionMenu } from "../../../../../components/common/action-menu" -import { NoRecords } from "../../../../../components/common/empty-table-content/empty-table-content" - -type LocationSalesChannelSectionProps = { - location: StockLocationExpandedDTO -} - -const PAGE_SIZE = 10 - -export const LocationSalesChannelSection = ({ - location, -}: LocationSalesChannelSectionProps) => { - const { t } = useTranslation() - const navigate = useNavigate() - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - const salesChannels = location.sales_channels - const count = location.sales_channels?.length || 0 - const columns = useColumns() - - const table = useReactTable({ - data: salesChannels ?? [], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - meta: { - locationId: location.id, - }, - }) - - return ( - -
- Sales Channels - - - -
-
- {count ? ( - - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - navigate(`/settings/sales-channels/${row.original.id}`) - } - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- ) : ( - - )} -
-
- ) -} - -const SalesChannelActions = ({ - salesChannel, - locationId, -}: { - salesChannel: SalesChannel - locationId: string -}) => { - const { t } = useTranslation() - const prompt = usePrompt() - - const { mutateAsync } = useAdminRemoveLocationFromSalesChannel() - - const handleDelete = async () => { - const res = await prompt({ - title: t("general.areYouSure"), - description: t("locations.removeSalesChannelsWarning", { count: 1 }), - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) - - if (!res) { - return - } - - await mutateAsync({ - location_id: locationId, - sales_channel_id: salesChannel.id, - }) - } - - return ( - , - label: t("actions.edit"), - to: `/settings/sales-channels/${salesChannel.id}/edit`, - }, - { - icon: , - label: t("actions.delete"), - onClick: handleDelete, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("name", { - header: t("fields.name"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("description", { - header: t("fields.description"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("is_disabled", { - header: t("fields.status"), - cell: ({ getValue }) => { - const value = getValue() - return ( -
- - {value ? t("general.disabled") : t("general.enabled")} - -
- ) - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row, table }) => { - const { locationId } = table.options.meta as { - locationId: string - } - - return ( - - ) - }, - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/modules/locations/location-list/components/locations-list-table/locations-list-table.tsx b/packages/admin-next/dashboard/src/modules/locations/location-list/components/locations-list-table/locations-list-table.tsx deleted file mode 100644 index ad3acb92f8ac6..0000000000000 --- a/packages/admin-next/dashboard/src/modules/locations/location-list/components/locations-list-table/locations-list-table.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import { Button, Container, Heading, Table, clx, usePrompt } from "@medusajs/ui" -import { Link, useNavigate, useSearchParams } from "react-router-dom" -import { - NoRecords, - NoResults, -} from "../../../../../components/common/empty-table-content/empty-table-content" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { PencilSquare, Trash } from "@medusajs/icons" -import { - useAdminDeleteStockLocation, - useAdminStockLocations, -} from "medusa-react" -import { useMemo, useState } from "react" - -import { ActionMenu } from "../../../../../components/common/action-menu" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" -import { StockLocationExpandedDTO } from "@medusajs/types" -import { useTranslation } from "react-i18next" - -const PAGE_SIZE = 50 - -export const LocationsListTable = () => { - const navigate = useNavigate() - const { t } = useTranslation() - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - const { stock_locations, count, isLoading, isError, error } = - useAdminStockLocations({ - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - fields: "*address", - }) - - const columns = useColumns() - - const table = useReactTable({ - data: stock_locations ?? [], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - manualPagination: true, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - }) - - if (isLoading) { - return
Loading...
- } - - if (isError) { - if (error) { - throw error - } - } - - return ( - -
- Locations -
- - - -
-
- {(stock_locations?.length ?? 0) > 0 ? ( -
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - navigate(`/settings/locations/${row.original.id}`) - } - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- -
- ) : ( - - )} -
- ) -} - -const LocationActions = ({ - location, -}: { - location: StockLocationExpandedDTO -}) => { - const { t } = useTranslation() - const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteStockLocation(location.id) - - const handleDelete = async () => { - const res = await prompt({ - title: t("general.areYouSure"), - description: t("locations.deleteLocationWarning", { - name: location.name, - }), - verificationText: location.name, - verificationInstruction: t("general.typeToConfirm"), - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) - - if (!res) { - return - } - - await mutateAsync() - } - - return ( - , - label: t("actions.edit"), - to: `/settings/locations/${location.id}/edit`, - }, - { - icon: , - label: t("actions.delete"), - onClick: handleDelete, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.accessor("name", { - header: t("fields.name"), - cell: (cell) => cell.getValue(), - }), - columnHelper.accessor("address", { - header: t("fields.address"), - cell: (cell) => { - const value = cell.getValue() - - if (!value) { - return "-" - } - - return `${value.address_1}${value.city ? `, ${value.city}` : ""}` - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row }) => , - }), - ], - [t] - ) -} - -const NoLocations = () => { - const [params] = useSearchParams() - const { t } = useTranslation() - - const noParams = params.toString().length === 0 - - if (noParams) { - return ( - - ) - } - - return -} diff --git a/packages/admin-next/dashboard/src/types/api-payloads.ts b/packages/admin-next/dashboard/src/types/api-payloads.ts index d4145fc916699..1031fc2db3e3d 100644 --- a/packages/admin-next/dashboard/src/types/api-payloads.ts +++ b/packages/admin-next/dashboard/src/types/api-payloads.ts @@ -8,10 +8,12 @@ import { CreateInviteDTO, CreateRegionDTO, CreateSalesChannelDTO, + CreateStockLocationInput, UpdateApiKeyDTO, UpdateCustomerDTO, UpdateRegionDTO, UpdateSalesChannelDTO, + UpdateStockLocationInput, UpdateStoreDTO, UpdateUserDTO, } from "@medusajs/types" @@ -45,3 +47,7 @@ export type UpdateUserReq = Omit // Invites export type CreateInviteReq = CreateInviteDTO + +// Stock Locations +export type CreateStockLocationReq = CreateStockLocationInput +export type UpdateStockLocationReq = UpdateStockLocationInput diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index 4e78eea3e2186..a966ff36c7964 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -15,9 +15,12 @@ import { PromotionDTO, RegionDTO, SalesChannelDTO, + StockLocationAddressDTO, + StockLocationDTO, StoreDTO, UserDTO, } from "@medusajs/types" +import { WorkflowExecutionDTO } from "../v2-routes/workflow-executions/types" type ListRes = { count: number @@ -96,3 +99,20 @@ export type ProductDeleteRes = DeleteRes // Product Types export type ProductTypeRes = { product_type: ProductTypeDTO } export type ProductTypeListRes = { product_types: ProductTypeDTO[] } & ListRes + +// Stock Locations +export type ExtendedStockLocationDTO = StockLocationDTO & { + address: StockLocationAddressDTO | null + sales_channels: SalesChannelDTO[] | null +} +export type StockLocationRes = { stock_location: ExtendedStockLocationDTO } +export type StockLocationListRes = { + stock_locations: ExtendedStockLocationDTO[] +} & ListRes +export type StockLocationDeleteRes = DeleteRes + +// Worfklow Executions +export type WorkflowExecutionRes = { workflow_execution: WorkflowExecutionDTO } +export type WorkflowExecutionListRes = { + workflow_executions: WorkflowExecutionDTO[] +} & ListRes diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-add-sales-channels/location-add-sales-channels.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-add-sales-channels/location-add-sales-channels.tsx index c09882bb28b8f..519e724336ca2 100644 --- a/packages/admin-next/dashboard/src/v2-routes/locations/location-add-sales-channels/location-add-sales-channels.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-add-sales-channels/location-add-sales-channels.tsx @@ -1,8 +1,7 @@ -import { useAdminAddLocationToSalesChannel } from "medusa-react" import { RouteFocusModal } from "../../../components/route-modal" export const LocationAddSalesChannels = () => { - const { mutateAsync } = useAdminAddLocationToSalesChannel() // TODO: We need a batch mutation instead of this to avoid multiple requests + // We need a batch add sales channels endpoint return } diff --git a/packages/admin-next/dashboard/src/modules/locations/location-create/components/create-location-form/create-location-form.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-create/components/create-location-form/create-location-form.tsx similarity index 97% rename from packages/admin-next/dashboard/src/modules/locations/location-create/components/create-location-form/create-location-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/locations/location-create/components/create-location-form/create-location-form.tsx index ad40b5943ae7f..aff83be824cbb 100644 --- a/packages/admin-next/dashboard/src/modules/locations/location-create/components/create-location-form/create-location-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-create/components/create-location-form/create-location-form.tsx @@ -1,12 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Button, Heading, Input, Text } from "@medusajs/ui" -import { useAdminCreateStockLocation } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" import { RouteFocusModal } from "../../../../../components/route-modal" +import { useCreateStockLocation } from "../../../../../hooks/api/stock-locations" const CreateLocationSchema = zod.object({ name: zod.string().min(1), @@ -42,7 +42,7 @@ export const CreateLocationForm = () => { resolver: zodResolver(CreateLocationSchema), }) - const { mutateAsync, isLoading } = useAdminCreateStockLocation() + const { mutateAsync, isPending } = useCreateStockLocation() const handleSubmit = form.handleSubmit(async (values) => { mutateAsync( @@ -69,7 +69,7 @@ export const CreateLocationForm = () => { {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/modules/locations/location-create/components/create-location-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/locations/location-create/components/create-location-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/locations/location-create/components/create-location-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/locations/location-create/components/create-location-form/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-create/location-create.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-create/location-create.tsx index 77dc24f33370b..5d6a290e16ac3 100644 --- a/packages/admin-next/dashboard/src/v2-routes/locations/location-create/location-create.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-create/location-create.tsx @@ -1,5 +1,5 @@ -import { CreateLocationForm } from "../../../modules/locations/location-create/components/create-location-form" import { RouteFocusModal } from "../../../components/route-modal" +import { CreateLocationForm } from "./components/create-location-form" export const LocationCreate = () => { return ( diff --git a/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-general-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-general-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-general-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-general-section/index.ts diff --git a/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-general-section/location-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-general-section/location-general-section.tsx similarity index 78% rename from packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-general-section/location-general-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-general-section/location-general-section.tsx index b12aea39ab7ac..3a3b4e917a46c 100644 --- a/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-general-section/location-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-general-section/location-general-section.tsx @@ -1,13 +1,12 @@ -import type { - StockLocationAddressDTO, - StockLocationExpandedDTO, -} from "@medusajs/types" -import { Button, Container, Heading, Text, clx } from "@medusajs/ui" +import { PencilSquare } from "@medusajs/icons" +import type { StockLocationAddressDTO } from "@medusajs/types" +import { Container, Heading, Text, clx } from "@medusajs/ui" import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" +import { ActionMenu } from "../../../../../components/common/action-menu" +import { ExtendedStockLocationDTO } from "../../../../../types/api-responses" type LocationGeneralSectionProps = { - location: StockLocationExpandedDTO + location: ExtendedStockLocationDTO } export const LocationGeneralSection = ({ @@ -19,11 +18,19 @@ export const LocationGeneralSection = ({
{location.name} - - - + , + label: t("actions.edit"), + to: `edit`, + }, + ], + }, + ]} + />
diff --git a/packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-sales-channel-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-sales-channel-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/locations/location-detail/components/location-sales-channel-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-sales-channel-section/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx new file mode 100644 index 0000000000000..19acd8cf9c7a5 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/components/location-sales-channel-section/location-sales-channel-section.tsx @@ -0,0 +1,139 @@ +import { PencilSquare, Trash } from "@medusajs/icons" +import { SalesChannelDTO } from "@medusajs/types" +import { Button, Container, Heading, usePrompt } from "@medusajs/ui" +import { createColumnHelper } from "@tanstack/react-table" +import { useAdminRemoveLocationFromSalesChannel } from "medusa-react" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" +import { Link } from "react-router-dom" +import { ActionMenu } from "../../../../../components/common/action-menu" +import { DataTable } from "../../../../../components/table/data-table" +import { useSalesChannelTableColumns } from "../../../../../hooks/table/columns/use-sales-channel-table-columns" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { ExtendedStockLocationDTO } from "../../../../../types/api-responses" + +type LocationSalesChannelSectionProps = { + location: ExtendedStockLocationDTO +} + +const PAGE_SIZE = 10 + +export const LocationSalesChannelSection = ({ + location, +}: LocationSalesChannelSectionProps) => { + const { t } = useTranslation() + + const salesChannels = location.sales_channels + const count = location.sales_channels?.length || 0 + const columns = useColumns() + + const { table } = useDataTable({ + data: salesChannels ?? [], + columns, + count, + pageSize: PAGE_SIZE, + enablePagination: true, + getRowId: (row) => row.id, + }) + + return ( + +
+ {t("salesChannels.domain")} + + + +
+ row.id} + pagination + /> +
+ ) +} + +const SalesChannelActions = ({ + salesChannel, + locationId, +}: { + salesChannel: SalesChannelDTO + locationId: string +}) => { + const { t } = useTranslation() + const prompt = usePrompt() + + const { mutateAsync } = useAdminRemoveLocationFromSalesChannel() + + const handleDelete = async () => { + const res = await prompt({ + title: t("general.areYouSure"), + description: t("locations.removeSalesChannelsWarning", { count: 1 }), + confirmText: t("actions.delete"), + cancelText: t("actions.cancel"), + }) + + if (!res) { + return + } + + await mutateAsync({ + location_id: locationId, + sales_channel_id: salesChannel.id, + }) + } + + return ( + , + label: t("actions.edit"), + to: `/settings/sales-channels/${salesChannel.id}/edit`, + }, + { + icon: , + label: t("actions.delete"), + onClick: handleDelete, + }, + ], + }, + ]} + /> + ) +} + +const columnHelper = createColumnHelper() + +const useColumns = () => { + const base = useSalesChannelTableColumns() + + return useMemo( + () => [ + ...base, + columnHelper.display({ + id: "actions", + cell: ({ row, table }) => { + const { locationId } = table.options.meta as { + locationId: string + } + + return ( + + ) + }, + }), + ], + [base] + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/location-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/location-detail.tsx index 257f74e4acc4c..b3a02895755bd 100644 --- a/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/location-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-detail/location-detail.tsx @@ -1,20 +1,17 @@ -import { Outlet, json, useParams } from "react-router-dom" +import { Outlet, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" -import { LocationGeneralSection } from "../../../modules/locations/location-detail/components/location-general-section" -import { LocationSalesChannelSection } from "../../../modules/locations/location-detail/components/location-sales-channel-section" -import { useAdminStockLocations } from "medusa-react" +import { useStockLocation } from "../../../hooks/api/stock-locations" +import { LocationGeneralSection } from "./components/location-general-section" +import { LocationSalesChannelSection } from "./components/location-sales-channel-section" export const LocationDetail = () => { const { id } = useParams() - const { stock_locations, isLoading, isError, error } = useAdminStockLocations( - { - id, - fields: "*address,*sales_channels", - } - ) + const { stock_location, isLoading, isError, error } = useStockLocation(id!, { + fields: "*address,*sales_channels", + }) - if (isLoading) { + if (isLoading || !stock_location) { return
Loading...
} @@ -22,12 +19,6 @@ export const LocationDetail = () => { throw error } - const stock_location = stock_locations?.[0] - - if (!stock_location) { - throw json({ message: "Not found" }, 404) - } - return (
diff --git a/packages/admin-next/dashboard/src/modules/locations/location-edit/components/edit-location-form/edit-location-form.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx similarity index 91% rename from packages/admin-next/dashboard/src/modules/locations/location-edit/components/edit-location-form/edit-location-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx index 1c7a05a5fbe04..76979bc915dfb 100644 --- a/packages/admin-next/dashboard/src/modules/locations/location-edit/components/edit-location-form/edit-location-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/edit-location-form.tsx @@ -1,17 +1,20 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { StockLocationExpandedDTO } from "@medusajs/types" import { Button, Input } from "@medusajs/ui" -import { useAdminUpdateStockLocation } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" -import { RouteDrawer } from "../../../../../components/route-modal" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../components/route-modal" +import { useUpdateStockLocation } from "../../../../../hooks/api/stock-locations" +import { ExtendedStockLocationDTO } from "../../../../../types/api-responses" type EditLocationFormProps = { - location: StockLocationExpandedDTO + location: ExtendedStockLocationDTO } const EditLocationSchema = zod.object({ @@ -29,6 +32,9 @@ const EditLocationSchema = zod.object({ }) export const EditLocationForm = ({ location }: EditLocationFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + const form = useForm>({ defaultValues: { name: location.name, @@ -46,15 +52,20 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => { resolver: zodResolver(EditLocationSchema), }) - const { mutateAsync, isLoading } = useAdminUpdateStockLocation(location.id) - - const { t } = useTranslation() + const { mutateAsync, isPending } = useUpdateStockLocation(location.id) const handleSubmit = form.handleSubmit(async (values) => { - mutateAsync({ - name: values.name, - address: values.address, - }) + mutateAsync( + { + name: values.name, + address: values.address, + }, + { + onSuccess: () => { + handleSuccess() + }, + } + ) }) return ( @@ -211,7 +222,7 @@ export const EditLocationForm = ({ location }: EditLocationFormProps) => { {t("actions.cancel")} -
diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/index.ts new file mode 100644 index 0000000000000..3ed7544394166 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/components/edit-location-form/index.ts @@ -0,0 +1 @@ +export * from "./edit-location-form" diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/location-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/location-edit.tsx index 8f846e249fbf7..c3b4ce3b81c8b 100644 --- a/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/location-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-edit/location-edit.tsx @@ -1,19 +1,17 @@ -import { EditLocationForm } from "../../../modules/locations/location-edit/components/edit-location-form/edit-location-form" import { Heading } from "@medusajs/ui" -import { RouteDrawer } from "../../../components/route-modal" -import { useAdminStockLocations } from "medusa-react" -import { useParams } from "react-router-dom" import { useTranslation } from "react-i18next" +import { useParams } from "react-router-dom" +import { RouteDrawer } from "../../../components/route-modal" +import { useStockLocations } from "../../../hooks/api/stock-locations" +import { EditLocationForm } from "./components/edit-location-form" export const LocationEdit = () => { const { id } = useParams() - const { stock_locations, isLoading, isError, error } = useAdminStockLocations( - { - id, - expand: "address", - } - ) + const { stock_locations, isLoading, isError, error } = useStockLocations({ + id, + fields: "*address", + }) const { t } = useTranslation() diff --git a/packages/admin-next/dashboard/src/modules/locations/location-list/components/locations-list-table/index.ts b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/modules/locations/location-list/components/locations-list-table/index.ts rename to packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/index.ts diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/location-row-actions.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/location-row-actions.tsx new file mode 100644 index 0000000000000..5507a545a23f5 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/location-row-actions.tsx @@ -0,0 +1,57 @@ +import { PencilSquare, Trash } from "@medusajs/icons" +import { usePrompt } from "@medusajs/ui" +import { useTranslation } from "react-i18next" + +import { ActionMenu } from "../../../../../components/common/action-menu" +import { useDeleteStockLocation } from "../../../../../hooks/api/stock-locations" +import { ExtendedStockLocationDTO } from "../../../../../types/api-responses" + +export const LocationRowActions = ({ + location, +}: { + location: ExtendedStockLocationDTO +}) => { + const { t } = useTranslation() + const prompt = usePrompt() + const { mutateAsync } = useDeleteStockLocation(location.id) + + const handleDelete = async () => { + const res = await prompt({ + title: t("general.areYouSure"), + description: t("locations.deleteLocationWarning", { + name: location.name, + }), + verificationText: location.name, + verificationInstruction: t("general.typeToConfirm"), + confirmText: t("actions.delete"), + cancelText: t("actions.cancel"), + }) + + if (!res) { + return + } + + await mutateAsync() + } + + return ( + , + label: t("actions.edit"), + to: `/settings/locations/${location.id}/edit`, + }, + { + icon: , + label: t("actions.delete"), + onClick: handleDelete, + }, + ], + }, + ]} + /> + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/locations-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/locations-list-table.tsx new file mode 100644 index 0000000000000..7634b4108e0bf --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/locations-list-table.tsx @@ -0,0 +1,66 @@ +import { Button, Container, Heading } from "@medusajs/ui" +import { Link } from "react-router-dom" + +import { useTranslation } from "react-i18next" +import { DataTable } from "../../../../../components/table/data-table" +import { useStockLocations } from "../../../../../hooks/api/stock-locations" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { useLocationTableColumns } from "./use-location-table-columns" +import { useLocationTableQuery } from "./use-location-table-query" + +const PAGE_SIZE = 20 + +export const LocationsListTable = () => { + const { t } = useTranslation() + + const { raw, searchParams } = useLocationTableQuery({ pageSize: PAGE_SIZE }) + + /** + * Note: The endpoint is bugged and does not return count, causing the table to not render + * any rows. + */ + const { stock_locations, count, isLoading, isError, error } = + useStockLocations({ + ...searchParams, + fields: "*address", + }) + + const columns = useLocationTableColumns() + + const { table } = useDataTable({ + data: stock_locations ?? [], + columns, + count, + enablePagination: true, + getRowId: (row) => row.id, + pageSize: PAGE_SIZE, + }) + + if (isError) { + throw error + } + + return ( + +
+ {t("locations.domain")} +
+ +
+
+ row.id} + isLoading={isLoading} + queryObject={raw} + pagination + search + /> +
+ ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-columns.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-columns.tsx new file mode 100644 index 0000000000000..d28f29c08c54d --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-columns.tsx @@ -0,0 +1,37 @@ +import { createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" +import { ExtendedStockLocationDTO } from "../../../../../types/api-responses" +import { LocationRowActions } from "./location-row-actions" + +const columnHelper = createColumnHelper() + +export const useLocationTableColumns = () => { + const { t } = useTranslation() + + return useMemo( + () => [ + columnHelper.accessor("name", { + header: t("fields.name"), + cell: (cell) => cell.getValue(), + }), + columnHelper.accessor("address", { + header: t("fields.address"), + cell: (cell) => { + const value = cell.getValue() + + if (!value) { + return "-" + } + + return `${value.address_1}${value.city ? `, ${value.city}` : ""}` + }, + }), + columnHelper.display({ + id: "actions", + cell: ({ row }) => , + }), + ], + [t] + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-query.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-query.tsx new file mode 100644 index 0000000000000..54e99aefb16d3 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/components/locations-list-table/use-location-table-query.tsx @@ -0,0 +1,21 @@ +import { useQueryParams } from "../../../../../hooks/use-query-params" + +export const useLocationTableQuery = ({ + pageSize = 20, + prefix, +}: { + pageSize?: number + prefix?: string +}) => { + const raw = useQueryParams(["offset"], prefix) + + const searchParams = { + limit: pageSize, + offset: raw.offset, + } + + return { + searchParams, + raw, + } +} diff --git a/packages/admin-next/dashboard/src/v2-routes/locations/location-list/location-list.tsx b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/location-list.tsx index 76e8d7f3dedcb..02363aba29b1e 100644 --- a/packages/admin-next/dashboard/src/v2-routes/locations/location-list/location-list.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/locations/location-list/location-list.tsx @@ -1,5 +1,5 @@ -import { LocationsListTable } from "../../../modules/locations/location-list/components/locations-list-table" import { Outlet } from "react-router-dom" +import { LocationsListTable } from "./components/locations-list-table" export const LocationList = () => { return ( diff --git a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx index 62d07a921038c..b525b5ad4e5f1 100644 --- a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx @@ -1,19 +1,19 @@ -import { PencilSquare, Trash } from "@medusajs/icons" +import { PencilSquare } from "@medusajs/icons" import { PromotionDTO } from "@medusajs/types" -import { Button, Container, Heading, usePrompt } from "@medusajs/ui" +import { Button, Container, Heading } from "@medusajs/ui" import { createColumnHelper } from "@tanstack/react-table" -import { useAdminDeleteDiscount } from "medusa-react" import { useMemo } from "react" import { useTranslation } from "react-i18next" import { Link, Outlet, useLoaderData } from "react-router-dom" +import { keepPreviousData } from "@tanstack/react-query" import { ActionMenu } from "../../../../../components/common/action-menu" import { DataTable } from "../../../../../components/table/data-table" +import { usePromotions } from "../../../../../hooks/api/promotions" import { usePromotionTableColumns } from "../../../../../hooks/table/columns-v2/use-promotion-table-columns" import { usePromotionTableFilters } from "../../../../../hooks/table/filters-v2/use-promotion-table-filters" import { usePromotionTableQuery } from "../../../../../hooks/table/query-v2/use-promotion-table-query" import { useDataTable } from "../../../../../hooks/use-data-table" -import { useV2Promotions } from "../../../../../lib/api-v2" import { promotionsLoader } from "../../loader" const PAGE_SIZE = 20 @@ -25,11 +25,11 @@ export const PromotionListTable = () => { > const { searchParams, raw } = usePromotionTableQuery({ pageSize: PAGE_SIZE }) - const { promotions, count, isLoading, isError, error } = useV2Promotions( + const { promotions, count, isLoading, isError, error } = usePromotions( { ...searchParams }, { initialData, - keepPreviousData: true, + placeholderData: keepPreviousData, } ) @@ -79,27 +79,6 @@ export const PromotionListTable = () => { const PromotionActions = ({ promotion }: { promotion: PromotionDTO }) => { const { t } = useTranslation() - const prompt = usePrompt() - // TODO: change to promotions delete endpoint - const { mutateAsync } = useAdminDeleteDiscount(promotion.id) - - const handleDelete = async () => { - const res = await prompt({ - title: t("general.areYouSure"), - description: t("promotions.deleteWarning", { - code: promotion.code, - }), - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) - - if (!res) { - return - } - - // TODO: handle error scenario here - await mutateAsync() - } return ( { }, ], }, - { - actions: [ - { - icon: , - label: t("actions.delete"), - onClick: handleDelete, - }, - ], - }, ]} /> ) diff --git a/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/loader.ts index b1037283fb05a..616a19e995585 100644 --- a/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/loader.ts @@ -1,13 +1,13 @@ -import { AdminProductsRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" import { adminProductKeys } from "medusa-react" import { LoaderFunctionArgs } from "react-router-dom" -import { medusa, queryClient } from "../../../lib/medusa" +import { client } from "../../../lib/client" +import { queryClient } from "../../../lib/medusa" +import { WorkflowExecutionRes } from "../../../types/api-responses" const executionDetailQuery = (id: string) => ({ queryKey: adminProductKeys.detail(id), - queryFn: async () => medusa.admin.custom.get(`/workflows-executions/${id}`), + queryFn: async () => client.workflowExecutions.retrieve(id), }) export const executionLoader = async ({ params }: LoaderFunctionArgs) => { @@ -15,7 +15,7 @@ export const executionLoader = async ({ params }: LoaderFunctionArgs) => { const query = executionDetailQuery(id!) return ( - queryClient.getQueryData>(query.queryKey) ?? + queryClient.getQueryData(query.queryKey) ?? (await queryClient.fetchQuery(query)) ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx index e8c189f0f7053..483e183c2bb1b 100644 --- a/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-detail/workflow-detail.tsx @@ -1,7 +1,7 @@ -import { useAdminCustomQuery } from "medusa-react" import { useParams } from "react-router-dom" + import { JsonViewSection } from "../../../components/common/json-view-section" -import { adminExecutionKey } from "../utils" +import { useWorkflowExecution } from "../../../hooks/api/workflow-executions" import { WorkflowExecutionGeneralSection } from "./components/workflow-execution-general-section" import { WorkflowExecutionHistorySection } from "./components/workflow-execution-history-section" import { WorkflowExecutionPayloadSection } from "./components/workflow-execution-payload-section" @@ -10,12 +10,10 @@ import { WorkflowExecutionTimelineSection } from "./components/workflow-executio export const ExecutionDetail = () => { const { id } = useParams() - const { data, isLoading, isError, error } = useAdminCustomQuery( - `/workflows-executions/${id}`, - adminExecutionKey.detail(id!) - ) + const { workflow_execution, isLoading, isError, error } = + useWorkflowExecution(id!) - if (isLoading || !data) { + if (isLoading || !workflow_execution) { return
Loading...
} @@ -25,11 +23,11 @@ export const ExecutionDetail = () => { return (
- - - - - + + + + +
) } diff --git a/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx index ada5d0261a21c..8878115e8a185 100644 --- a/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/workflow-executions/workflow-execution-list/components/workflow-execution-list-table/workflow-execution-list-table.tsx @@ -1,11 +1,10 @@ -import { AdminGetWorkflowExecutionsParams } from "@medusajs/medusa" import { Container, Heading } from "@medusajs/ui" -import { useAdminCustomQuery } from "medusa-react" +import { keepPreviousData } from "@tanstack/react-query" import { useTranslation } from "react-i18next" import { DataTable } from "../../../../../components/table/data-table" +import { useWorkflowExecutions } from "../../../../../hooks/api/workflow-executions" import { useDataTable } from "../../../../../hooks/use-data-table" import { WorkflowExecutionDTO } from "../../../types" -import { adminExecutionKey } from "../../../utils" import { useWorkflowExecutionTableColumns } from "./use-workflow-execution-table-columns" import { useWorkflowExecutionTableQuery } from "./use-workflow-execution-table-query" @@ -27,27 +26,23 @@ export const WorkflowExecutionListTable = () => { const { searchParams, raw } = useWorkflowExecutionTableQuery({ pageSize: PAGE_SIZE, }) - const { data, isLoading, isError, error } = useAdminCustomQuery< - AdminGetWorkflowExecutionsParams, - WorkflowExecutionsRes - >( - "/workflows-executions", - adminExecutionKey.list(searchParams), - { - ...searchParams, - fields: "execution,state", - }, - { - keepPreviousData: true, - } - ) + const { workflow_executions, count, isLoading, isError, error } = + useWorkflowExecutions( + { + ...searchParams, + fields: "execution,state", + }, + { + placeholderData: keepPreviousData, + } + ) const columns = useWorkflowExecutionTableColumns() const { table } = useDataTable({ - data: data?.workflow_executions || [], + data: workflow_executions || [], columns, - count: data?.count, + count: count, pageSize: PAGE_SIZE, enablePagination: true, getRowId: (row) => row.id, @@ -65,7 +60,7 @@ export const WorkflowExecutionListTable = () => { `${row.id}`} From 31bc5566f373e314839eb0bcf50f1b2311d93199 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 16:40:59 +0200 Subject: [PATCH 4/7] add collections --- .../dashboard/src/hooks/api/collections.tsx | 116 ++++ .../dashboard/src/lib/client/client.ts | 2 + .../dashboard/src/lib/client/collections.ts | 46 ++ .../src/providers/router-provider/v1.tsx | 200 ------- .../location-add-sales-channels/index.ts | 1 - .../location-add-sales-channels.tsx | 8 - .../routes/locations/location-create/index.ts | 1 - .../location-create/location-create.tsx | 10 - .../routes/locations/location-detail/index.ts | 1 - .../location-detail/location-detail.tsx | 39 -- .../routes/locations/location-edit/index.ts | 1 - .../locations/location-edit/location-edit.tsx | 36 -- .../routes/locations/location-list/index.ts | 1 - .../locations/location-list/location-list.tsx | 11 - .../profile-general-section/index.ts | 1 - .../profile-general-section.tsx | 71 --- .../routes/profile/profile-detail/index.ts | 1 - .../profile/profile-detail/profile-detail.tsx | 26 - .../edit-profile-form/edit-profile-form.tsx | 202 ------- .../src/routes/profile/profile-edit/index.ts | 1 - .../profile/profile-edit/profile-edit.tsx | 26 - .../add-currencies-form.tsx | 323 ---------- .../store/store-add-currencies/index.ts | 1 - .../store-add-currencies.tsx | 17 - .../store-currencies-section.tsx/index.ts | 1 - .../store-currency-section.tsx | 292 --------- .../components/store-general-section/index.ts | 1 - .../store-general-section.tsx | 114 ---- .../src/routes/store/store-detail/index.ts | 2 - .../src/routes/store/store-detail/loader.ts | 37 -- .../store/store-detail/store-detail.tsx | 36 -- .../edit-store-form/edit-store-form.tsx | 146 ----- .../src/routes/store/store-edit/index.ts | 1 - .../routes/store/store-edit/store-edit.tsx | 28 - .../dashboard/src/types/api-payloads.ts | 6 + .../dashboard/src/types/api-responses.ts | 7 + .../collection-add-products.tsx | 4 +- .../add-products-to-collection-form.tsx | 552 ++++++++---------- .../collection-detail/collection-detail.tsx | 4 +- .../collection-general-section.tsx | 8 +- .../collection-product-section.tsx | 47 +- .../collections/collection-detail/loader.ts | 14 +- ...ction-add-edit.tsx => collection-edit.tsx} | 4 +- .../edit-collection-form.tsx | 10 +- .../collections/collection-edit/index.ts | 2 +- .../collection-list-table.tsx | 7 +- .../use-collection-table-columns.tsx | 10 +- 47 files changed, 474 insertions(+), 2001 deletions(-) create mode 100644 packages/admin-next/dashboard/src/hooks/api/collections.tsx create mode 100644 packages/admin-next/dashboard/src/lib/client/collections.ts delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-create/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-detail/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-detail/location-detail.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-edit/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-list/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/locations/location-list/location-list.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-detail/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-detail/profile-detail.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-edit/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/profile/profile-edit/profile-edit.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-add-currencies/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/store-general-section.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/loader.ts delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-detail/store-detail.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-edit/index.ts delete mode 100644 packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx rename packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/{collection-add-edit.tsx => collection-edit.tsx} (83%) diff --git a/packages/admin-next/dashboard/src/hooks/api/collections.tsx b/packages/admin-next/dashboard/src/hooks/api/collections.tsx new file mode 100644 index 0000000000000..841d16e54697a --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/collections.tsx @@ -0,0 +1,116 @@ +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { UpdateProductCollectionReq } from "../../types/api-payloads" +import { + ProductCollectionDeleteRes, + ProductCollectionListRes, + ProductCollectionRes, +} from "../../types/api-responses" + +const COLLECTION_QUERY_KEY = "collections" as const +export const collectionsQueryKeys = queryKeysFactory(COLLECTION_QUERY_KEY) + +export const useCollection = ( + id: string, + options?: Omit< + UseQueryOptions< + ProductCollectionRes, + Error, + ProductCollectionRes, + QueryKey + >, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: collectionsQueryKeys.detail(id), + queryFn: async () => client.collections.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCollections = ( + query?: Record, + options?: Omit< + UseQueryOptions< + ProductCollectionListRes, + Error, + ProductCollectionListRes, + QueryKey + >, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: collectionsQueryKeys.list(query), + queryFn: async () => client.collections.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useUpdateCollection = ( + id: string, + options?: UseMutationOptions< + ProductCollectionRes, + Error, + UpdateProductCollectionReq + > +) => { + return useMutation({ + mutationFn: (payload: UpdateProductCollectionReq) => + client.collections.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() }) + queryClient.invalidateQueries({ + queryKey: collectionsQueryKeys.detail(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useCreateCollection = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: any) => client.collections.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteCollection = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.collections.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() }) + queryClient.invalidateQueries({ + queryKey: collectionsQueryKeys.detail(id), + }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index 13d241e0d916d..b659d89e2a73d 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -1,5 +1,6 @@ import { apiKeys } from "./api-keys" import { auth } from "./auth" +import { collections } from "./collections" import { currencies } from "./currencies" import { customers } from "./customers" import { invites } from "./invites" @@ -18,6 +19,7 @@ export const client = { apiKeys: apiKeys, customers: customers, currencies: currencies, + collections: collections, promotions: promotions, stores: stores, salesChannels: salesChannels, diff --git a/packages/admin-next/dashboard/src/lib/client/collections.ts b/packages/admin-next/dashboard/src/lib/client/collections.ts new file mode 100644 index 0000000000000..db4551130378b --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/collections.ts @@ -0,0 +1,46 @@ +import { + CreateProductCollectionReq, + UpdateProductCollectionReq, +} from "../../types/api-payloads" +import { + ProductCollectionDeleteRes, + ProductCollectionListRes, + ProductCollectionRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" + +async function listProductCollections(query?: Record) { + return getRequest(`/admin/collections`, query) +} + +async function retrieveProductCollection( + id: string, + query?: Record +) { + return getRequest(`/admin/collections/${id}`, query) +} + +async function updateProductCollection( + id: string, + payload: UpdateProductCollectionReq +) { + return postRequest(`/admin/collections/${id}`, payload) +} + +async function createProductCollection(payload: CreateProductCollectionReq) { + return postRequest(`/admin/collections`, payload) +} + +async function deleteProductCollection(id: string) { + return deleteRequest( + `/admin/collections/${id}/delete` + ) +} + +export const collections = { + list: listProductCollections, + retrieve: retrieveProductCollection, + update: updateProductCollection, + create: createProductCollection, + delete: deleteProductCollection, +} diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx index 7d644d419f86c..179d36b416843 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx @@ -6,14 +6,12 @@ import type { AdminGiftCardsRes, AdminOrdersRes, AdminProductsRes, - AdminRegionsRes, } from "@medusajs/medusa" import { Outlet, RouteObject } from "react-router-dom" import { ProtectedRoute } from "../../components/authentication/require-auth" import { ErrorBoundary } from "../../components/error/error-boundary" import { MainLayout } from "../../components/layout/main-layout" -import { SettingsLayout } from "../../components/layout/settings-layout" import routes from "medusa-admin:routes/pages" import settings from "medusa-admin:settings/pages" @@ -492,204 +490,6 @@ export const v1Routes: RouteObject[] = [ }, ], }, - { - path: "/settings", - element: , - handle: { - crumb: () => "Settings", - }, - children: [ - { - index: true, - lazy: () => import("../../routes/settings"), - }, - { - path: "profile", - lazy: () => import("../../routes/profile/profile-detail"), - handle: { - crumb: () => "Profile", - }, - children: [ - { - path: "edit", - lazy: () => import("../../routes/profile/profile-edit"), - }, - ], - }, - { - path: "store", - lazy: () => import("../../routes/store/store-detail"), - handle: { - crumb: () => "Store", - }, - children: [ - { - path: "edit", - lazy: () => import("../../routes/store/store-edit"), - }, - { - path: "add-currencies", - lazy: () => import("../../routes/store/store-add-currencies"), - }, - ], - }, - { - path: "locations", - element: , - handle: { - crumb: () => "Locations", - }, - children: [ - { - path: "", - lazy: () => import("../../routes/locations/location-list"), - children: [ - { - path: "create", - lazy: () => - import("../../routes/locations/location-create"), - }, - ], - }, - { - path: ":id", - lazy: () => import("../../routes/locations/location-detail"), - children: [ - { - path: "edit", - lazy: () => import("../../routes/locations/location-edit"), - }, - { - path: "add-sales-channels", - lazy: () => - import( - "../../routes/locations/location-add-sales-channels" - ), - }, - ], - }, - ], - }, - { - path: "return-reasons", - element: , - handle: { - crumb: () => "Return Reasons", - }, - children: [ - { - path: "", - lazy: () => - import("../../routes/return-reasons/return-reason-list"), - children: [ - { - path: "create", - lazy: () => - import( - "../../routes/return-reasons/return-reason-create" - ), - }, - { - path: ":id/edit", - lazy: () => - import("../../routes/return-reasons/return-reason-edit"), - }, - ], - }, - ], - }, - { - path: "regions", - element: , - handle: { - crumb: () => "Regions", - }, - children: [ - { - path: "", - lazy: () => import("../../routes/regions/region-list"), - children: [ - { - path: "create", - lazy: () => import("../../routes/regions/region-create"), - }, - ], - }, - { - path: ":id", - lazy: () => import("../../routes/regions/region-detail"), - handle: { - crumb: (data: AdminRegionsRes) => data.region.name, - }, - children: [ - { - path: "edit", - lazy: () => import("../../routes/regions/region-edit"), - }, - { - path: "countries/add", - lazy: () => - import("../../routes/regions/region-add-countries"), - }, - { - path: "shipping-options/:so_id/edit", - lazy: () => - import( - "../../routes/regions/region-edit-shipping-option" - ), - }, - { - path: "shipping-options/create", - lazy: () => - import( - "../../routes/regions/region-create-shipping-option" - ), - }, - ], - }, - ], - }, - { - path: "taxes", - handle: { - crumb: () => "Taxes", - }, - children: [ - { - path: "", - lazy: () => import("../../routes/taxes/tax-list"), - }, - { - path: ":id", - lazy: () => import("../../routes/taxes/tax-detail"), - handle: { - crumb: (data: AdminRegionsRes) => data.region.name, - }, - children: [ - { - path: "edit", - lazy: () => import("../../routes/taxes/tax-edit"), - }, - { - path: "tax-rates/create", - lazy: () => import("../../routes/taxes/tax-rate-create"), - }, - { - path: "tax-rates/:rate_id/edit", - lazy: () => import("../../routes/taxes/tax-rate-edit"), - }, - { - path: "tax-rates/:rate_id/edit-overrides", - lazy: () => - import("../../routes/taxes/tax-rate-edit-overrides"), - }, - ], - }, - ], - }, - ...settingsExtensions, - ], - }, ...routeExtensions, ], }, diff --git a/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/index.ts b/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/index.ts deleted file mode 100644 index eb7ea74f11870..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LocationAddSalesChannels as Component } from "./location-add-sales-channels" diff --git a/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx b/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx deleted file mode 100644 index c09882bb28b8f..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-add-sales-channels/location-add-sales-channels.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { useAdminAddLocationToSalesChannel } from "medusa-react" -import { RouteFocusModal } from "../../../components/route-modal" - -export const LocationAddSalesChannels = () => { - const { mutateAsync } = useAdminAddLocationToSalesChannel() // TODO: We need a batch mutation instead of this to avoid multiple requests - - return -} diff --git a/packages/admin-next/dashboard/src/routes/locations/location-create/index.ts b/packages/admin-next/dashboard/src/routes/locations/location-create/index.ts deleted file mode 100644 index 9e7d96209e7e4..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-create/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LocationCreate as Component } from "./location-create" diff --git a/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx b/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx deleted file mode 100644 index 77dc24f33370b..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-create/location-create.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { CreateLocationForm } from "../../../modules/locations/location-create/components/create-location-form" -import { RouteFocusModal } from "../../../components/route-modal" - -export const LocationCreate = () => { - return ( - - - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/locations/location-detail/index.ts b/packages/admin-next/dashboard/src/routes/locations/location-detail/index.ts deleted file mode 100644 index 086ccf707af37..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-detail/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LocationDetail as Component } from "./location-detail" diff --git a/packages/admin-next/dashboard/src/routes/locations/location-detail/location-detail.tsx b/packages/admin-next/dashboard/src/routes/locations/location-detail/location-detail.tsx deleted file mode 100644 index d0dd46403882a..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-detail/location-detail.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Outlet, json, useParams } from "react-router-dom" - -import { JsonViewSection } from "../../../components/common/json-view-section" -import { LocationGeneralSection } from "../../../modules/locations/location-detail/components/location-general-section" -import { LocationSalesChannelSection } from "../../../modules/locations/location-detail/components/location-sales-channel-section" -import { useAdminStockLocations } from "medusa-react" - -export const LocationDetail = () => { - const { id } = useParams() - const { stock_locations, isLoading, isError, error } = useAdminStockLocations( - { - id, - expand: "address,sales_channels", - } - ) - - if (isLoading) { - return
Loading...
- } - - if (isError) { - throw error - } - - const stock_location = stock_locations?.[0] - - if (!stock_location) { - throw json({ message: "Not found" }, 404) - } - - return ( -
- - - - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/locations/location-edit/index.ts b/packages/admin-next/dashboard/src/routes/locations/location-edit/index.ts deleted file mode 100644 index 516dd003a1585..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-edit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LocationEdit as Component } from "./location-edit" diff --git a/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx b/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx deleted file mode 100644 index 38492662806a7..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-edit/location-edit.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { EditLocationForm } from "../../../modules/locations/location-edit/components/edit-location-form/edit-location-form" -import { Heading } from "@medusajs/ui" -import { RouteDrawer } from "../../../components/route-modal" -import { useAdminStockLocations } from "medusa-react" -import { useParams } from "react-router-dom" -import { useTranslation } from "react-i18next" - -export const LocationEdit = () => { - const { id } = useParams() - - const { stock_locations, isLoading, isError, error } = useAdminStockLocations( - { - id, - expand: "address", - } - ) - - const { t } = useTranslation() - - if (isError) { - throw error - } - - const stock_location = stock_locations?.[0] - - return ( - - - {t("locations.editLocation")} - - {!isLoading && stock_location && ( - - )} - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/locations/location-list/index.ts b/packages/admin-next/dashboard/src/routes/locations/location-list/index.ts deleted file mode 100644 index 883c2ba632d59..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LocationList as Component } from "./location-list" diff --git a/packages/admin-next/dashboard/src/routes/locations/location-list/location-list.tsx b/packages/admin-next/dashboard/src/routes/locations/location-list/location-list.tsx deleted file mode 100644 index 76e8d7f3dedcb..0000000000000 --- a/packages/admin-next/dashboard/src/routes/locations/location-list/location-list.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { LocationsListTable } from "../../../modules/locations/location-list/components/locations-list-table" -import { Outlet } from "react-router-dom" - -export const LocationList = () => { - return ( -
- - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/index.ts b/packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/index.ts deleted file mode 100644 index 948c77df96f70..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./profile-general-section" diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx b/packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx deleted file mode 100644 index f3bf780565d7e..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-detail/components/profile-general-section/profile-general-section.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { User } from "@medusajs/medusa" -import { Button, Container, Heading, StatusBadge, Text } from "@medusajs/ui" -import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" -import { languages } from "../../../../../i18n/config" - -type ProfileGeneralSectionProps = { - user: Omit -} - -export const ProfileGeneralSection = ({ user }: ProfileGeneralSectionProps) => { - const { i18n, t } = useTranslation() - return ( - -
-
- {t("profile.domain")} - - {t("profile.manageYourProfileDetails")} - -
- - - -
-
- - {t("fields.name")} - - - {user.first_name} {user.last_name} - -
-
- - {t("fields.email")} - - - {user.email} - -
-
- - {t("fields.role")} - - - {t(`users.roles.${user.role}`)} - -
-
- - {t("profile.language")} - - - {languages.find((lang) => lang.code === i18n.language) - ?.display_name || "-"} - -
-
- - {t("profile.usageInsights")} - - - {t("general.disabled")} - -
-
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-detail/index.ts b/packages/admin-next/dashboard/src/routes/profile/profile-detail/index.ts deleted file mode 100644 index 9d981a433dfd3..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-detail/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ProfileDetail as Component } from "./profile-detail" diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-detail/profile-detail.tsx b/packages/admin-next/dashboard/src/routes/profile/profile-detail/profile-detail.tsx deleted file mode 100644 index c69975be2884d..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-detail/profile-detail.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useAdminGetSession } from "medusa-react" -import { Outlet, json } from "react-router-dom" -import { ProfileGeneralSection } from "./components/profile-general-section" - -export const ProfileDetail = () => { - const { user, isLoading, isError, error } = useAdminGetSession() - - if (isLoading) { - return
Loading...
- } - - if (isError || !user) { - if (error) { - throw error - } - - throw json("An unknown error has occured", 500) - } - - return ( -
- - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx b/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx deleted file mode 100644 index d57353c27b341..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-edit/components/edit-profile-form/edit-profile-form.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod" -import { User } from "@medusajs/medusa" -import { Button, Input, Select, Switch } from "@medusajs/ui" -import { adminAuthKeys, useAdminUpdateUser } from "medusa-react" -import { useForm } from "react-hook-form" -import { Trans, useTranslation } from "react-i18next" -import * as zod from "zod" - -import { Form } from "../../../../../components/common/form" -import { - RouteDrawer, - useRouteModal, -} from "../../../../../components/route-modal" -import { languages } from "../../../../../i18n/config" -import { queryClient } from "../../../../../lib/medusa" - -type EditProfileProps = { - user: Omit - usageInsights: boolean -} - -const EditProfileSchema = zod.object({ - first_name: zod.string().optional(), - last_name: zod.string().optional(), - language: zod.string(), - usage_insights: zod.boolean(), -}) - -export const EditProfileForm = ({ user, usageInsights }: EditProfileProps) => { - const { t, i18n } = useTranslation() - const { handleSuccess } = useRouteModal() - - const form = useForm>({ - defaultValues: { - first_name: user.first_name ?? "", - last_name: user.last_name ?? "", - language: i18n.language, - usage_insights: usageInsights, - }, - resolver: zodResolver(EditProfileSchema), - }) - - const changeLanguage = (code: string) => { - i18n.changeLanguage(code) - } - - const sortedLanguages = languages.sort((a, b) => - a.display_name.localeCompare(b.display_name) - ) - - const { mutateAsync, isLoading } = useAdminUpdateUser(user.id) - - const handleSubmit = form.handleSubmit(async (values) => { - await mutateAsync( - { - first_name: values.first_name, - last_name: values.last_name, - }, - { - onSuccess: () => { - // Invalidate the current user session. - queryClient.invalidateQueries(adminAuthKeys.details()) - }, - onError: () => { - return - }, - } - ) - - changeLanguage(values.language) - - handleSuccess() - }) - - return ( - -
- -
-
- ( - - {t("fields.firstName")} - - - - - - )} - /> - ( - - {t("fields.lastName")} - - - - - - )} - /> -
- ( - -
- {t("profile.language")} - {t("profile.languageHint")} -
-
- - - - -
-
- )} - /> - ( - -
- {t("profile.usageInsights")} - - - -
- - - , - ]} - /> - - - -
- )} - /> -
-
- -
- - - - -
-
-
-
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-edit/index.ts b/packages/admin-next/dashboard/src/routes/profile/profile-edit/index.ts deleted file mode 100644 index 99be2393cbd2d..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-edit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ProfileEdit as Component } from "./profile-edit" diff --git a/packages/admin-next/dashboard/src/routes/profile/profile-edit/profile-edit.tsx b/packages/admin-next/dashboard/src/routes/profile/profile-edit/profile-edit.tsx deleted file mode 100644 index 63566185ebd37..0000000000000 --- a/packages/admin-next/dashboard/src/routes/profile/profile-edit/profile-edit.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Heading } from "@medusajs/ui" -import { useAdminGetSession } from "medusa-react" -import { useTranslation } from "react-i18next" -import { RouteDrawer } from "../../../components/route-modal" -import { EditProfileForm } from "./components/edit-profile-form/edit-profile-form" - -export const ProfileEdit = () => { - const { user, isLoading, isError, error } = useAdminGetSession() - - const { t } = useTranslation() - - if (isError) { - throw error - } - - return ( - - - {t("profile.editProfile")} - - {!isLoading && user && ( - - )} - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx deleted file mode 100644 index 6576e562f70e6..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/components/add-currencies-form/add-currencies-form.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import { Currency, type Store } from "@medusajs/medusa" -import { - Badge, - Button, - Checkbox, - Hint, - StatusBadge, - Table, - Tooltip, - clx, -} from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { useAdminCurrencies, useAdminUpdateStore } from "medusa-react" -import { useEffect, useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import * as zod from "zod" - -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { OrderBy } from "../../../../../components/filtering/order-by" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" -import { - RouteFocusModal, - useRouteModal, -} from "../../../../../components/route-modal" -import { useHandleTableScroll } from "../../../../../hooks/use-handle-table-scroll" -import { useQueryParams } from "../../../../../hooks/use-query-params" - -type AddCurrenciesFormProps = { - store: Store -} - -const AddCurrenciesSchema = zod.object({ - currencies: zod.array(zod.string()).min(1), -}) - -const PAGE_SIZE = 50 - -export const AddCurrenciesForm = ({ store }: AddCurrenciesFormProps) => { - const { t } = useTranslation() - const { handleSuccess } = useRouteModal() - - const form = useForm>({ - defaultValues: { - currencies: [], - }, - resolver: zodResolver(AddCurrenciesSchema), - }) - - const { setValue } = form - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - useEffect(() => { - const ids = Object.keys(rowSelection) - setValue("currencies", ids, { - shouldDirty: true, - shouldTouch: true, - }) - }, [rowSelection, setValue]) - - const params = useQueryParams(["order"]) - const { currencies, count, isError, error } = useAdminCurrencies({ - limit: PAGE_SIZE, - offset: pageIndex * PAGE_SIZE, - ...params, - }) - - const preSelectedRows = store.currencies.map((c) => c.code) - - const columns = useColumns() - - const table = useReactTable({ - data: currencies ?? [], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - getRowId: (row) => row.code, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - enableRowSelection: (row) => !preSelectedRows.includes(row.original.code), - manualPagination: true, - }) - - const { mutateAsync, isLoading: isMutating } = useAdminUpdateStore() - - const { handleScroll, isScrolled, tableContainerRef } = useHandleTableScroll() - - const handleSubmit = form.handleSubmit(async (data) => { - const currencies = Array.from( - new Set([...data.currencies, ...preSelectedRows]) - ) as string[] - - await mutateAsync( - { - currencies, - }, - { - onSuccess: () => { - handleSuccess() - }, - } - ) - }) - - if (isError) { - throw error - } - - return ( - -
- -
-
- {form.formState.errors.currencies && ( - - {form.formState.errors.currencies.message} - - )} -
-
- - - - -
-
-
- -
-
-
- -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
-
-
- -
-
-
-
- ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.display({ - id: "select", - header: ({ table }) => { - return ( - - table.toggleAllPageRowsSelected(!!value) - } - /> - ) - }, - cell: ({ row }) => { - const isPreSelected = !row.getCanSelect() - const isSelected = row.getIsSelected() || isPreSelected - - const Component = ( - row.toggleSelected(!!value)} - onClick={(e) => { - e.stopPropagation() - }} - /> - ) - - if (isPreSelected) { - return ( - - {Component} - - ) - } - - return Component - }, - }), - columnHelper.accessor("name", { - header: t("fields.name"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("code", { - header: t("fields.code"), - cell: ({ getValue }) => ( - {getValue().toUpperCase()} - ), - }), - columnHelper.accessor("includes_tax", { - header: t("fields.taxInclusivePricing"), - cell: ({ getValue }) => { - const value = getValue() - - return ( - - {value ? t("general.enabled") : t("general.disabled")} - - ) - }, - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/index.ts b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/index.ts deleted file mode 100644 index 8a88a62bd8629..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { StoreAddCurrencies as Component } from "./store-add-currencies" diff --git a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx b/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx deleted file mode 100644 index 18559e0c97ebc..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-add-currencies/store-add-currencies.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useAdminStore } from "medusa-react" -import { RouteFocusModal } from "../../../components/route-modal" -import { AddCurrenciesForm } from "./components/add-currencies-form/add-currencies-form" - -export const StoreAddCurrencies = () => { - const { store, isLoading, isError, error } = useAdminStore() - - if (isError) { - throw error - } - - return ( - - {!isLoading && store && } - - ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/index.ts b/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/index.ts deleted file mode 100644 index 9a84261a47f55..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./store-currency-section" diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx b/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx deleted file mode 100644 index f23a1db561e06..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-currency-section/store-currencies-section.tsx/store-currency-section.tsx +++ /dev/null @@ -1,292 +0,0 @@ -import { Trash } from "@medusajs/icons" -import { Currency, Store } from "@medusajs/medusa" -import { - Button, - Checkbox, - CommandBar, - Container, - Heading, - StatusBadge, - Table, - clx, - usePrompt, -} from "@medusajs/ui" -import { - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - getPaginationRowModel, - useReactTable, -} from "@tanstack/react-table" -import { useAdminUpdateStore } from "medusa-react" -import { useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" -import { ActionMenu } from "../../../../../../components/common/action-menu" -import { LocalizedTablePagination } from "../../../../../../components/localization/localized-table-pagination" - -type StoreCurrencySectionProps = { - store: Store -} - -const PAGE_SIZE = 20 - -export const StoreCurrencySection = ({ store }: StoreCurrencySectionProps) => { - const [rowSelection, setRowSelection] = useState({}) - - const columns = useColumns() - - const table = useReactTable({ - data: store.currencies, - columns, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - onRowSelectionChange: setRowSelection, - getRowId: (row) => row.code, - pageCount: Math.ceil(store.currencies.length / PAGE_SIZE), - state: { - rowSelection, - }, - meta: { - currencyCodes: store.currencies.map((c) => c.code), - }, - }) - - const { mutateAsync } = useAdminUpdateStore() - const { t } = useTranslation() - const prompt = usePrompt() - - const handleDeleteCurrencies = async () => { - const ids = Object.keys(rowSelection) - - const result = await prompt({ - title: t("general.areYouSure"), - description: t("store.removeCurrencyWarning", { - count: ids.length, - }), - confirmText: t("actions.remove"), - cancelText: t("actions.cancel"), - }) - - if (!result) { - return - } - - await mutateAsync( - { - currencies: store.currencies - .filter((c) => !ids.includes(c.code)) - .map((c) => c.code), - }, - { - onSuccess: () => { - setRowSelection({}) - }, - } - ) - } - - return ( - -
- {t("store.currencies")} -
- - - -
-
- - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} - -
- - - - - {t("general.countSelected", { - count: Object.keys(rowSelection).length, - })} - - - - - -
- ) -} - -const CurrencyActions = ({ - currency, - currencyCodes, -}: { - currency: Currency - currencyCodes: string[] -}) => { - const { mutateAsync } = useAdminUpdateStore() - const { t } = useTranslation() - const prompt = usePrompt() - - const handleRemove = async () => { - const result = await prompt({ - title: t("general.areYouSure"), - description: t("store.removeCurrencyWarning", { - count: 1, - }), - verificationInstruction: t("general.typeToConfirm"), - verificationText: currency.name, - confirmText: t("actions.remove"), - cancelText: t("actions.cancel"), - }) - - if (!result) { - return - } - - await mutateAsync({ - currencies: currencyCodes.filter((c) => c !== currency.code), - }) - } - - return ( - , - label: t("actions.remove"), - onClick: handleRemove, - }, - ], - }, - ]} - /> - ) -} - -const columnHelper = createColumnHelper() - -const useColumns = () => { - const { t } = useTranslation() - - return useMemo( - () => [ - columnHelper.display({ - id: "select", - header: ({ table }) => { - return ( - - table.toggleAllPageRowsSelected(!!value) - } - /> - ) - }, - cell: ({ row }) => { - return ( - row.toggleSelected(!!value)} - onClick={(e) => { - e.stopPropagation() - }} - /> - ) - }, - }), - columnHelper.accessor("code", { - header: t("fields.code"), - cell: ({ getValue }) => getValue().toUpperCase(), - }), - columnHelper.accessor("name", { - header: t("fields.name"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("includes_tax", { - header: "Tax Inclusive Prices", - cell: ({ getValue }) => { - const value = getValue() - const text = value ? t("general.enabled") : t("general.disabled") - - return ( - {text} - ) - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row, table }) => { - const { currencyCodes } = table.options.meta as { - currencyCodes: string[] - } - - return ( - - ) - }, - }), - ], - [t] - ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/index.ts b/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/index.ts deleted file mode 100644 index 0ea68a992d469..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./store-general-section" diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/store-general-section.tsx b/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/store-general-section.tsx deleted file mode 100644 index ccc4f022def20..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/components/store-general-section/store-general-section.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { Store } from "@medusajs/medusa" -import { Badge, Button, Container, Copy, Heading, Text } from "@medusajs/ui" -import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" - -type StoreGeneralSectionProps = { - store: Store -} - -export const StoreGeneralSection = ({ store }: StoreGeneralSectionProps) => { - const { t } = useTranslation() - - return ( - -
-
- {t("store.domain")} - - {t("store.manageYourStoresDetails")} - -
- - - -
-
- - {t("fields.name")} - - - {store.name} - -
-
- - {t("store.defaultCurrency")} - -
- - {store.default_currency_code.toUpperCase()} - - - {store.default_currency.name} - -
-
-
- - {t("store.swapLinkTemplate")} - - {store.swap_link_template ? ( -
- - {store.swap_link_template} - - -
- ) : ( - - - - - )} -
-
- - {t("store.paymentLinkTemplate")} - - {store.payment_link_template ? ( -
- - {store.payment_link_template} - - -
- ) : ( - - - - - )} -
-
- - {t("store.inviteLinkTemplate")} - - {store.invite_link_template ? ( -
- - {store.invite_link_template} - - -
- ) : ( - - - - - )} -
-
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/index.ts b/packages/admin-next/dashboard/src/routes/store/store-detail/index.ts deleted file mode 100644 index bc9d614088e58..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { storeLoader as loader } from "./loader" -export { StoreDetail as Component } from "./store-detail" diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/loader.ts b/packages/admin-next/dashboard/src/routes/store/store-detail/loader.ts deleted file mode 100644 index fe83eef7e7494..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/loader.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { AdminExtendedStoresRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" -import { adminStoreKeys } from "medusa-react" -import { redirect } from "react-router-dom" - -import { FetchQueryOptions } from "@tanstack/react-query" -import { medusa, queryClient } from "../../../lib/medusa" - -const storeDetailQuery = () => ({ - queryKey: adminStoreKeys.details(), - queryFn: async () => medusa.admin.store.retrieve(), -}) - -const fetchQuery = async ( - query: FetchQueryOptions> -) => { - try { - const res = await queryClient.fetchQuery(query) - return res - } catch (error) { - const err = error ? JSON.parse(JSON.stringify(error)) : null - - if ((err as Error & { status: number })?.status === 401) { - redirect("/login", 401) - } - } -} - -export const storeLoader = async () => { - const query = storeDetailQuery() - - return ( - queryClient.getQueryData>( - query.queryKey - ) ?? (await fetchQuery(query)) - ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-detail/store-detail.tsx b/packages/admin-next/dashboard/src/routes/store/store-detail/store-detail.tsx deleted file mode 100644 index 3e9ec14712270..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-detail/store-detail.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useAdminStore } from "medusa-react" -import { Outlet, useLoaderData } from "react-router-dom" - -import { JsonViewSection } from "../../../components/common/json-view-section/json-view-section.tsx" -import { StoreCurrencySection } from "./components/store-currency-section/store-currencies-section.tsx/index.ts" -import { StoreGeneralSection } from "./components/store-general-section/index.ts" -import { storeLoader } from "./loader.ts" - -export const StoreDetail = () => { - const initialData = useLoaderData() as Awaited> - - const { store, isLoading, isError, error } = useAdminStore({ - initialData: initialData, - }) - - if (isLoading) { - return
Loading...
- } - - if (isError || !store) { - if (error) { - throw error - } - - return
{JSON.stringify(error, null, 2)}
- } - - return ( -
- - - - -
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx b/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx deleted file mode 100644 index a47ee40c780d7..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-edit/components/edit-store-form/edit-store-form.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod" -import type { Store } from "@medusajs/medusa" -import { Button, Input } from "@medusajs/ui" -import { useAdminUpdateStore } from "medusa-react" -import { useForm } from "react-hook-form" -import { useTranslation } from "react-i18next" -import * as zod from "zod" -import { Form } from "../../../../../components/common/form" -import { - RouteDrawer, - useRouteModal, -} from "../../../../../components/route-modal" - -type EditStoreFormProps = { - store: Store -} - -const EditStoreSchema = zod.object({ - name: zod.string().optional(), - swap_link_template: zod.union([zod.literal(""), zod.string().trim().url()]), - payment_link_template: zod.union([ - zod.literal(""), - zod.string().trim().url(), - ]), - invite_link_template: zod.union([zod.literal(""), zod.string().trim().url()]), -}) - -export const EditStoreForm = ({ store }: EditStoreFormProps) => { - const { t } = useTranslation() - const { handleSuccess } = useRouteModal() - - const form = useForm>({ - defaultValues: { - name: store.name, - swap_link_template: store.swap_link_template ?? "", - payment_link_template: store.payment_link_template ?? "", - invite_link_template: store.invite_link_template ?? "", - }, - resolver: zodResolver(EditStoreSchema), - }) - - const { mutateAsync, isLoading } = useAdminUpdateStore() - - const handleSubmit = form.handleSubmit(async (values) => { - mutateAsync( - { - name: values.name, - invite_link_template: values.invite_link_template || undefined, - swap_link_template: values.swap_link_template || undefined, - payment_link_template: values.payment_link_template || undefined, - }, - { - onSuccess: () => { - handleSuccess() - }, - } - ) - }) - - return ( - -
- -
- ( - - {t("fields.name")} - - - - - - )} - /> - ( - - {t("store.swapLinkTemplate")} - - - - - - )} - /> - ( - - {t("store.paymentLinkTemplate")} - - - - - - )} - /> - ( - - {t("store.inviteLinkTemplate")} - - - - - - )} - /> -
-
- -
- - - - -
-
-
-
- ) -} diff --git a/packages/admin-next/dashboard/src/routes/store/store-edit/index.ts b/packages/admin-next/dashboard/src/routes/store/store-edit/index.ts deleted file mode 100644 index 16ec90284f11e..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-edit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { StoreEdit as Component } from "./store-edit" diff --git a/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx b/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx deleted file mode 100644 index 8f9cd33f7d16a..0000000000000 --- a/packages/admin-next/dashboard/src/routes/store/store-edit/store-edit.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Heading } from "@medusajs/ui" -import { useAdminStore } from "medusa-react" -import { useTranslation } from "react-i18next" -import { json } from "react-router-dom" -import { RouteDrawer } from "../../../components/route-modal" -import { EditStoreForm } from "./components/edit-store-form/edit-store-form" - -export const StoreEdit = () => { - const { t } = useTranslation() - const { store, isLoading, isError, error } = useAdminStore() - - if (isError) { - throw error - } - - if (!store && !isLoading) { - throw json("An unknown error has occured", 500) - } - - return ( - - - {t("store.editStore")} - - {store && } - - ) -} diff --git a/packages/admin-next/dashboard/src/types/api-payloads.ts b/packages/admin-next/dashboard/src/types/api-payloads.ts index 1031fc2db3e3d..425a4021f7241 100644 --- a/packages/admin-next/dashboard/src/types/api-payloads.ts +++ b/packages/admin-next/dashboard/src/types/api-payloads.ts @@ -6,11 +6,13 @@ import { CreateApiKeyDTO, CreateCustomerDTO, CreateInviteDTO, + CreateProductCollectionDTO, CreateRegionDTO, CreateSalesChannelDTO, CreateStockLocationInput, UpdateApiKeyDTO, UpdateCustomerDTO, + UpdateProductCollectionDTO, UpdateRegionDTO, UpdateSalesChannelDTO, UpdateStockLocationInput, @@ -51,3 +53,7 @@ export type CreateInviteReq = CreateInviteDTO // Stock Locations export type CreateStockLocationReq = CreateStockLocationInput export type UpdateStockLocationReq = UpdateStockLocationInput + +// Product Collections +export type CreateProductCollectionReq = CreateProductCollectionDTO +export type UpdateProductCollectionReq = UpdateProductCollectionDTO diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index a966ff36c7964..880d30c42df9e 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -116,3 +116,10 @@ export type WorkflowExecutionRes = { workflow_execution: WorkflowExecutionDTO } export type WorkflowExecutionListRes = { workflow_executions: WorkflowExecutionDTO[] } & ListRes + +// Product Collections +export type ProductCollectionRes = { collection: ProductCollectionDTO } +export type ProductCollectionListRes = { + collections: ProductCollectionDTO[] +} & ListRes +export type ProductCollectionDeleteRes = DeleteRes diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/collection-add-products.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/collection-add-products.tsx index 7daea150a5355..f339bfd353c4b 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/collection-add-products.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/collection-add-products.tsx @@ -1,12 +1,12 @@ -import { useAdminCollection } from "medusa-react" import { useParams } from "react-router-dom" import { RouteFocusModal } from "../../../components/route-modal" +import { useCollection } from "../../../hooks/api/collections" import { AddProductsToCollectionForm } from "./components/add-products-to-collection-form" export const CollectionAddProducts = () => { const { id } = useParams() - const { collection, isLoading, isError, error } = useAdminCollection(id!) + const { collection, isLoading, isError, error } = useCollection(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx index 18843f33c6e63..a1f70132614c8 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx @@ -1,47 +1,16 @@ -import { zodResolver } from "@hookform/resolvers/zod" -import type { Product, ProductCollection } from "@medusajs/medusa" -import { Button, Checkbox, Hint, Table, Tooltip, clx } from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { - adminProductKeys, - useAdminAddProductsToCollection, - useAdminProducts, -} from "medusa-react" -import { Fragment, useEffect, useMemo, useState } from "react" -import { useForm } from "react-hook-form" +import type { Product } from "@medusajs/medusa" +import { ProductCollectionDTO } from "@medusajs/types" +import { Checkbox, Tooltip } from "@medusajs/ui" +import { createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { - NoRecords, - NoResults, -} from "../../../../../components/common/empty-table-content" -import { - ProductAvailabilityCell, - ProductCollectionCell, - ProductStatusCell, - ProductTitleCell, - ProductVariantCell, -} from "../../../../../components/common/product-table-cells" -import { OrderBy } from "../../../../../components/filtering/order-by" -import { Query } from "../../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" -import { - RouteFocusModal, - useRouteModal, -} from "../../../../../components/route-modal" -import { useHandleTableScroll } from "../../../../../hooks/use-handle-table-scroll" -import { useQueryParams } from "../../../../../hooks/use-query-params" -import { queryClient } from "../../../../../lib/medusa" +import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns" + +// Re-add when supported on the backend type AddProductsToCollectionFormProps = { - collection: ProductCollection + collection: ProductCollectionDTO } const AddProductsToSalesChannelSchema = zod.object({ @@ -53,237 +22,238 @@ const PAGE_SIZE = 50 export const AddProductsToCollectionForm = ({ collection, }: AddProductsToCollectionFormProps) => { - const { t } = useTranslation() - const { handleSuccess } = useRouteModal() - - const form = useForm>({ - defaultValues: { - product_ids: [], - }, - resolver: zodResolver(AddProductsToSalesChannelSchema), - }) - - const { setValue } = form - - const { mutateAsync, isLoading: isMutating } = - useAdminAddProductsToCollection(collection.id) - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) - - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) - - const [rowSelection, setRowSelection] = useState({}) - - useEffect(() => { - setValue( - "product_ids", - Object.keys(rowSelection).filter((k) => rowSelection[k]), - { - shouldDirty: true, - shouldTouch: true, - } - ) - }, [rowSelection, setValue]) - - const params = useQueryParams(["q", "order"]) - - const { products, count, isLoading, isError, error } = useAdminProducts( - { - expand: "variants,sales_channels", - ...params, - }, - { - keepPreviousData: true, - } - ) - - const columns = useColumns() - - const table = useReactTable({ - data: (products ?? []) as Product[], - columns, - pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - getRowId: (row) => row.id, - enableRowSelection(row) { - return row.original.collection_id !== collection.id - }, - meta: { - collectionId: collection.id, - }, - }) - - const handleSubmit = form.handleSubmit(async (values) => { - await mutateAsync( - { - product_ids: values.product_ids.map((p) => p), - }, - { - onSuccess: () => { - /** - * Invalidate the products list query to refetch products and - * determine if they are added to the collection or not. - */ - queryClient.invalidateQueries(adminProductKeys.lists()) - handleSuccess() - }, - } - ) - }) - - const { handleScroll, isScrolled, tableContainerRef } = useHandleTableScroll() - - const noRecords = - !isLoading && - products?.length === 0 && - !Object.values(params).filter((v) => v).length - - if (isError) { - throw error - } + // const { t } = useTranslation() + // const { handleSuccess } = useRouteModal() + + // const form = useForm>({ + // defaultValues: { + // product_ids: [], + // }, + // resolver: zodResolver(AddProductsToSalesChannelSchema), + // }) + + // const { setValue } = form + + // const { mutateAsync, isLoading: isMutating } = + // useAdminAddProductsToCollection(collection.id) + + // const [{ pageIndex, pageSize }, setPagination] = useState({ + // pageIndex: 0, + // pageSize: PAGE_SIZE, + // }) + + // const pagination = useMemo( + // () => ({ + // pageIndex, + // pageSize, + // }), + // [pageIndex, pageSize] + // ) + + // const [rowSelection, setRowSelection] = useState({}) + + // useEffect(() => { + // setValue( + // "product_ids", + // Object.keys(rowSelection).filter((k) => rowSelection[k]), + // { + // shouldDirty: true, + // shouldTouch: true, + // } + // ) + // }, [rowSelection, setValue]) + + // const params = useQueryParams(["q", "order"]) + + // const { products, count, isLoading, isError, error } = useAdminProducts( + // { + // expand: "variants,sales_channels", + // ...params, + // }, + // { + // keepPreviousData: true, + // } + // ) + + // const columns = useColumns() + + // const table = useReactTable({ + // data: (products ?? []) as Product[], + // columns, + // pageCount: Math.ceil((count ?? 0) / PAGE_SIZE), + // state: { + // pagination, + // rowSelection, + // }, + // onPaginationChange: setPagination, + // onRowSelectionChange: setRowSelection, + // getCoreRowModel: getCoreRowModel(), + // manualPagination: true, + // getRowId: (row) => row.id, + // enableRowSelection(row) { + // return row.original.collection_id !== collection.id + // }, + // meta: { + // collectionId: collection.id, + // }, + // }) + + // const handleSubmit = form.handleSubmit(async (values) => { + // await mutateAsync( + // { + // product_ids: values.product_ids.map((p) => p), + // }, + // { + // onSuccess: () => { + // /** + // * Invalidate the products list query to refetch products and + // * determine if they are added to the collection or not. + // */ + // queryClient.invalidateQueries(adminProductKeys.lists()) + // handleSuccess() + // }, + // } + // ) + // }) + + // const { handleScroll, isScrolled, tableContainerRef } = useHandleTableScroll() + + // const noRecords = + // !isLoading && + // products?.length === 0 && + // !Object.values(params).filter((v) => v).length + + // if (isError) { + // throw error + // } return ( - -
- -
- {form.formState.errors.product_ids && ( - - {form.formState.errors.product_ids.message} - - )} - - - - -
-
- - {!noRecords && ( -
-
-
- - -
-
- )} - {!noRecords ? ( - -
- {!isLoading && !products?.length ? ( -
- -
- ) : ( - - - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- )} -
-
- -
-
- ) : ( -
- - {/* TODO: fix this, and add NoRecords as well */} -
- )} -
-
-
+ // + //
+ // + //
+ // {form.formState.errors.product_ids && ( + // + // {form.formState.errors.product_ids.message} + // + // )} + // + // + // + // + //
+ //
+ // + // {!noRecords && ( + //
+ //
+ //
+ // + // + //
+ //
+ // )} + // {!noRecords ? ( + // + //
+ // {!isLoading && !products?.length ? ( + //
+ // + //
+ // ) : ( + // + // + // {table.getHeaderGroups().map((headerGroup) => { + // return ( + // + // {headerGroup.headers.map((header) => { + // return ( + // + // {flexRender( + // header.column.columnDef.header, + // header.getContext() + // )} + // + // ) + // })} + // + // ) + // })} + // + // + // {table.getRowModel().rows.map((row) => ( + // + // {row.getVisibleCells().map((cell) => ( + // + // {flexRender( + // cell.column.columnDef.cell, + // cell.getContext() + // )} + // + // ))} + // + // ))} + // + //
+ // )} + //
+ //
+ // + //
+ //
+ // ) : ( + //
+ // + // {/* TODO: fix this, and add NoRecords as well */} + //
+ // )} + //
+ //
+ //
+
NOT IMPLEMETED
) } @@ -291,6 +261,7 @@ const columnHelper = createColumnHelper() const useColumns = () => { const { t } = useTranslation() + const base = useProductTableColumns() return useMemo( () => [ @@ -344,47 +315,8 @@ const useColumns = () => { return Component }, }), - columnHelper.accessor("title", { - header: t("fields.title"), - cell: ({ row }) => { - const product = row.original - - return - }, - }), - columnHelper.accessor("collection", { - header: t("fields.collection"), - cell: ({ getValue }) => { - const collection = getValue() - - return - }, - }), - columnHelper.accessor("sales_channels", { - header: t("fields.availability"), - cell: ({ getValue }) => { - const salesChannels = getValue() - - return - }, - }), - columnHelper.accessor("variants", { - header: t("fields.inventory"), - cell: (cell) => { - const variants = cell.getValue() - - return - }, - }), - columnHelper.accessor("status", { - header: t("fields.status"), - cell: ({ getValue }) => { - const status = getValue() - - return - }, - }), + ...base, ], - [t] + [t, base] ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/collection-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/collection-detail.tsx index 82a02a1c7f84c..3f433d127cb7f 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/collection-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/collection-detail.tsx @@ -1,6 +1,6 @@ -import { useAdminCollection } from "medusa-react" import { Outlet, json, useLoaderData, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" +import { useCollection } from "../../../hooks/api/collections" import { CollectionGeneralSection } from "./components/collection-general-section" import { CollectionProductSection } from "./components/collection-product-section" import { collectionLoader } from "./loader" @@ -11,7 +11,7 @@ export const CollectionDetail = () => { > const { id } = useParams() - const { collection, isLoading, isError, error } = useAdminCollection(id!, { + const { collection, isLoading, isError, error } = useCollection(id!, { initialData, }) diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-general-section/collection-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-general-section/collection-general-section.tsx index 1842b2e9ca1f0..e1710c2dca344 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-general-section/collection-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-general-section/collection-general-section.tsx @@ -1,12 +1,12 @@ import { PencilSquare, Trash } from "@medusajs/icons" -import type { ProductCollection } from "@medusajs/medusa" +import { ProductCollectionDTO } from "@medusajs/types" import { Container, Heading, Text, usePrompt } from "@medusajs/ui" -import { useAdminDeleteCollection } from "medusa-react" import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useDeleteCollection } from "../../../../../hooks/api/collections" type CollectionGeneralSectionProps = { - collection: ProductCollection + collection: ProductCollectionDTO } export const CollectionGeneralSection = ({ @@ -15,7 +15,7 @@ export const CollectionGeneralSection = ({ const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteCollection(collection.id) + const { mutateAsync } = useDeleteCollection(collection.id) const handleDelete = async () => { const res = await prompt({ diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx index c3df90cdc6c98..933008f7f443b 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx @@ -1,24 +1,22 @@ import { PencilSquare, Plus, Trash } from "@medusajs/icons" -import type { Product, ProductCollection } from "@medusajs/medusa" +import { ProductCollectionDTO } from "@medusajs/types" import { Checkbox, Container, Heading, usePrompt } from "@medusajs/ui" +import { keepPreviousData } from "@tanstack/react-query" import { createColumnHelper } from "@tanstack/react-table" -import { - adminProductKeys, - useAdminProducts, - useAdminRemoveProductsFromCollection, -} from "medusa-react" +import { useAdminRemoveProductsFromCollection } from "medusa-react" import { useMemo } from "react" import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../components/common/action-menu" import { DataTable } from "../../../../../components/table/data-table" +import { useProducts } from "../../../../../hooks/api/products" import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns" import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters" import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query" import { useDataTable } from "../../../../../hooks/use-data-table" -import { queryClient } from "../../../../../lib/medusa" +import { ExtendedProductDTO } from "../../../../../types/api-responses" type CollectionProductSectionProps = { - collection: ProductCollection + collection: ProductCollectionDTO } const PAGE_SIZE = 10 @@ -29,14 +27,14 @@ export const CollectionProductSection = ({ const { t } = useTranslation() const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE }) - const { products, count, isLoading, isError, error } = useAdminProducts( + const { products, count, isLoading, isError, error } = useProducts( { limit: PAGE_SIZE, ...searchParams, collection_id: [collection.id], }, { - keepPreviousData: true, + placeholderData: keepPreviousData, } ) @@ -44,7 +42,7 @@ export const CollectionProductSection = ({ const columns = useColumns() const { table } = useDataTable({ - data: (products ?? []) as Product[], + data: products ?? [], columns, getRowId: (row) => row.id, count, @@ -57,7 +55,8 @@ export const CollectionProductSection = ({ }) const prompt = usePrompt() - const { mutateAsync } = useAdminRemoveProductsFromCollection(collection.id) + // Not implemented in 2.0 + // const { mutateAsync } = useAdminRemoveProductsFromCollection(collection.id) const handleRemove = async (selection: Record) => { const ids = Object.keys(selection) @@ -75,16 +74,16 @@ export const CollectionProductSection = ({ return } - await mutateAsync( - { - product_ids: ids, - }, - { - onSuccess: () => { - queryClient.invalidateQueries(adminProductKeys.lists()) - }, - } - ) + // await mutateAsync( + // { + // product_ids: ids, + // }, + // { + // onSuccess: () => { + // queryClient.invalidateQueries(adminProductKeys.lists()) + // }, + // } + // ) } if (isError) { @@ -137,7 +136,7 @@ const ProductActions = ({ product, collectionId, }: { - product: Product + product: ExtendedProductDTO collectionId: string }) => { const { t } = useTranslation() @@ -189,7 +188,7 @@ const ProductActions = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const columns = useProductTableColumns() diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/loader.ts index 05d1a929f0f5d..2d2dc52b7725b 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-detail/loader.ts @@ -1,13 +1,13 @@ -import { AdminCollectionsRes } from "@medusajs/medusa" -import { Response } from "@medusajs/medusa-js" -import { adminProductKeys } from "medusa-react" import { LoaderFunctionArgs } from "react-router-dom" -import { medusa, queryClient } from "../../../lib/medusa" +import { collectionsQueryKeys } from "../../../hooks/api/collections" +import { client } from "../../../lib/client" +import { queryClient } from "../../../lib/medusa" +import { ProductCollectionRes } from "../../../types/api-responses" const collectionDetailQuery = (id: string) => ({ - queryKey: adminProductKeys.detail(id), - queryFn: async () => medusa.admin.collections.retrieve(id), + queryKey: collectionsQueryKeys.detail(id), + queryFn: async () => client.collections.retrieve(id), }) export const collectionLoader = async ({ params }: LoaderFunctionArgs) => { @@ -15,7 +15,7 @@ export const collectionLoader = async ({ params }: LoaderFunctionArgs) => { const query = collectionDetailQuery(id!) return ( - queryClient.getQueryData>(query.queryKey) ?? + queryClient.getQueryData(query.queryKey) ?? (await queryClient.fetchQuery(query)) ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/collection-add-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/collection-edit.tsx similarity index 83% rename from packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/collection-add-edit.tsx rename to packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/collection-edit.tsx index 6d146da32b29e..690c44bed6d85 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/collection-add-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/collection-edit.tsx @@ -1,14 +1,14 @@ import { Heading } from "@medusajs/ui" -import { useAdminCollection } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" +import { useCollection } from "../../../hooks/api/collections" import { EditCollectionForm } from "./components/edit-collection-form" export const CollectionEdit = () => { const { id } = useParams() const { t } = useTranslation() - const { collection, isLoading, isError, error } = useAdminCollection(id!) + const { collection, isLoading, isError, error } = useCollection(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx index c4a2af64a0303..7f2d5b656b589 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/components/edit-collection-form/edit-collection-form.tsx @@ -1,19 +1,19 @@ import { zodResolver } from "@hookform/resolvers/zod" -import type { ProductCollection } from "@medusajs/medusa" import { Button, Input, Text } from "@medusajs/ui" -import { useAdminUpdateCollection } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" +import { ProductCollectionDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" +import { useUpdateCollection } from "../../../../../hooks/api/collections" type EditCollectionFormProps = { - collection: ProductCollection + collection: ProductCollectionDTO } const EditCollectionSchema = zod.object({ @@ -33,7 +33,7 @@ export const EditCollectionForm = ({ collection }: EditCollectionFormProps) => { resolver: zodResolver(EditCollectionSchema), }) - const { mutateAsync, isLoading } = useAdminUpdateCollection(collection.id) + const { mutateAsync, isPending } = useUpdateCollection(collection.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync(data, { @@ -101,7 +101,7 @@ export const EditCollectionForm = ({ collection }: EditCollectionFormProps) => { {t("actions.cancel")} -
diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/index.ts b/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/index.ts index 069cdd1eee59f..7adce91f62dc3 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/index.ts +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-edit/index.ts @@ -1 +1 @@ -export { CollectionEdit as Component } from "./collection-add-edit" +export { CollectionEdit as Component } from "./collection-edit" diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/collection-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/collection-list-table.tsx index 81a97ca4cec0b..369621ffa7085 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/collection-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/collection-list-table.tsx @@ -1,9 +1,10 @@ import { Button, Container, Heading } from "@medusajs/ui" -import { useAdminCollections } from "medusa-react" import { useTranslation } from "react-i18next" import { Link } from "react-router-dom" +import { keepPreviousData } from "@tanstack/react-query" import { DataTable } from "../../../../../components/table/data-table" +import { useCollections } from "../../../../../hooks/api/collections" import { useDataTable } from "../../../../../hooks/use-data-table" import { useCollectionTableColumns } from "./use-collection-table-columns" import { useCollectionTableFilters } from "./use-collection-table-filters" @@ -14,12 +15,12 @@ const PAGE_SIZE = 20 export const CollectionListTable = () => { const { t } = useTranslation() const { searchParams, raw } = useCollectionTableQuery({ pageSize: PAGE_SIZE }) - const { collections, count, isError, error, isLoading } = useAdminCollections( + const { collections, count, isError, error, isLoading } = useCollections( { ...searchParams, }, { - keepPreviousData: true, + placeholderData: keepPreviousData, } ) diff --git a/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/use-collection-table-columns.tsx b/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/use-collection-table-columns.tsx index 39cea769ce19f..57e9615d2417b 100644 --- a/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/use-collection-table-columns.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/collections/collection-list/components/collection-list-table/use-collection-table-columns.tsx @@ -1,13 +1,13 @@ import { PencilSquare, Trash } from "@medusajs/icons" -import { ProductCollection } from "@medusajs/medusa" +import { ProductCollectionDTO } from "@medusajs/types" import { usePrompt } from "@medusajs/ui" import { createColumnHelper } from "@tanstack/react-table" -import { useAdminDeleteCollection } from "medusa-react" import { useMemo } from "react" import { useTranslation } from "react-i18next" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useDeleteCollection } from "../../../../../hooks/api/collections" -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() export const useCollectionTableColumns = () => { const { t } = useTranslation() @@ -41,12 +41,12 @@ export const useCollectionTableColumns = () => { const CollectionActions = ({ collection, }: { - collection: ProductCollection + collection: ProductCollectionDTO }) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteCollection(collection.id) + const { mutateAsync } = useDeleteCollection(collection.id) const handleDeleteCollection = async () => { const res = await prompt({ From 49c30a78134feba94d39fb9ebb2558a6ae779376 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 17:14:35 +0200 Subject: [PATCH 5/7] address feedback --- .../dashboard/src/hooks/api/api-keys.tsx | 113 ++++++++++++++++++ .../dashboard/src/hooks/api/collections.tsx | 13 +- .../dashboard/src/lib/client/api-keys.ts | 29 ++++- .../dashboard/src/lib/client/collections.ts | 4 +- .../src/providers/router-provider/v2.tsx | 7 +- .../dashboard/src/types/api-responses.ts | 7 +- .../api-key-management-create.tsx | 11 +- .../create-publishable-api-key-form.tsx | 22 ++-- .../api-key-management-detail.tsx | 31 ++--- .../api-key-general-section.tsx | 35 ++---- .../api-key-sales-channel-section.tsx | 86 ++++++------- .../api-key-management-detail/loader.ts | 17 ++- .../api-key-management-edit.tsx | 21 +--- .../api-key-management-list-table.tsx | 19 ++- .../use-api-key-management-table-columns.tsx | 4 +- 15 files changed, 265 insertions(+), 154 deletions(-) create mode 100644 packages/admin-next/dashboard/src/hooks/api/api-keys.tsx diff --git a/packages/admin-next/dashboard/src/hooks/api/api-keys.tsx b/packages/admin-next/dashboard/src/hooks/api/api-keys.tsx new file mode 100644 index 0000000000000..bfe31bd849b12 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/api-keys.tsx @@ -0,0 +1,113 @@ +import { + MutationOptions, + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { CreateApiKeyReq, UpdateApiKeyReq } from "../../types/api-payloads" +import { + ApiKeyDeleteRes, + ApiKeyListRes, + ApiKeyRes, +} from "../../types/api-responses" + +const API_KEYS_QUERY_KEY = "api_keys" as const +export const apiKeysQueryKeys = queryKeysFactory(API_KEYS_QUERY_KEY) + +export const useApiKey = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.apiKeys.retrieve(id, query), + queryKey: apiKeysQueryKeys.detail(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useApiKeys = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.apiKeys.list(query), + queryKey: apiKeysQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCreateApiKey = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.apiKeys.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.lists() }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateApiKey = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload) => client.apiKeys.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useRevokeApiKey = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.apiKeys.revoke(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + }) +} + +export const useDeleteApiKey = ( + id: string, + options?: MutationOptions +) => { + return useMutation({ + mutationFn: () => client.apiKeys.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: apiKeysQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/collections.tsx b/packages/admin-next/dashboard/src/hooks/api/collections.tsx index 841d16e54697a..53a6ccf7abd09 100644 --- a/packages/admin-next/dashboard/src/hooks/api/collections.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/collections.tsx @@ -8,7 +8,10 @@ import { import { client } from "../../lib/client" import { queryClient } from "../../lib/medusa" import { queryKeysFactory } from "../../lib/query-key-factory" -import { UpdateProductCollectionReq } from "../../types/api-payloads" +import { + CreateProductCollectionReq, + UpdateProductCollectionReq, +} from "../../types/api-payloads" import { ProductCollectionDeleteRes, ProductCollectionListRes, @@ -84,10 +87,14 @@ export const useUpdateCollection = ( } export const useCreateCollection = ( - options?: UseMutationOptions + options?: UseMutationOptions< + ProductCollectionRes, + Error, + CreateProductCollectionReq + > ) => { return useMutation({ - mutationFn: (payload: any) => client.collections.create(payload), + mutationFn: (payload) => client.collections.create(payload), onSuccess: (data, variables, context) => { queryClient.invalidateQueries({ queryKey: collectionsQueryKeys.lists() }) diff --git a/packages/admin-next/dashboard/src/lib/client/api-keys.ts b/packages/admin-next/dashboard/src/lib/client/api-keys.ts index 0f43fa001db99..c422f0cdc5964 100644 --- a/packages/admin-next/dashboard/src/lib/client/api-keys.ts +++ b/packages/admin-next/dashboard/src/lib/client/api-keys.ts @@ -1,5 +1,10 @@ -import { ApiKeyListRes, ApiKeyRes } from "../../types/api-responses" -import { getRequest } from "./common" +import { CreateApiKeyReq, UpdateApiKeyReq } from "../../types/api-payloads" +import { + ApiKeyDeleteRes, + ApiKeyListRes, + ApiKeyRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" const retrieveApiKey = async (id: string, query?: Record) => { return getRequest(`/admin/api-keys/${id}`, query) @@ -9,7 +14,27 @@ const listApiKeys = async (query?: Record) => { return getRequest(`/admin/api-keys`, query) } +const deleteApiKey = async (id: string) => { + return deleteRequest(`/admin/api-keys/${id}`) +} + +const revokeApiKey = async (id: string) => { + return postRequest(`/admin/api-keys/${id}/revoke`) +} + +const createApiKey = async (payload: CreateApiKeyReq) => { + return postRequest(`/admin/api-keys`, payload) +} + +const updateApiKey = async (id: string, payload: UpdateApiKeyReq) => { + return postRequest(`/admin/api-keys/${id}`, payload) +} + export const apiKeys = { retrieve: retrieveApiKey, list: listApiKeys, + delete: deleteApiKey, + create: createApiKey, + update: updateApiKey, + revoke: revokeApiKey, } diff --git a/packages/admin-next/dashboard/src/lib/client/collections.ts b/packages/admin-next/dashboard/src/lib/client/collections.ts index db4551130378b..f6e8d59951218 100644 --- a/packages/admin-next/dashboard/src/lib/client/collections.ts +++ b/packages/admin-next/dashboard/src/lib/client/collections.ts @@ -32,9 +32,7 @@ async function createProductCollection(payload: CreateProductCollectionReq) { } async function deleteProductCollection(id: string) { - return deleteRequest( - `/admin/collections/${id}/delete` - ) + return deleteRequest(`/admin/collections/${id}`) } export const collections = { diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx index 2cb6556bc5653..8bdb4aa81cea3 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx @@ -3,11 +3,11 @@ import { Navigate, Outlet, RouteObject, useLocation } from "react-router-dom" import { Spinner } from "@medusajs/icons" import { AdminCollectionsRes } from "@medusajs/medusa" -import { ApiKeyDTO } from "@medusajs/types" import { ErrorBoundary } from "../../components/error/error-boundary" import { MainLayout } from "../../components/layout-v2/main-layout" import { SettingsLayout } from "../../components/layout/settings-layout" import { useMe } from "../../hooks/api/users" +import { ApiKeyRes } from "../../types/api-responses" import { SearchProvider } from "../search-provider" import { SidebarProvider } from "../sidebar-provider" @@ -362,7 +362,10 @@ export const v2Routes: RouteObject[] = [ "../../v2-routes/api-key-management/api-key-management-detail" ), handle: { - crumb: (data: { api_key: ApiKeyDTO }) => data.api_key.title, + crumb: (data: ApiKeyRes) => { + console.log("data", data) + return data.apiKey.title + }, }, children: [ { diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index 880d30c42df9e..23d39d7c12765 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -65,8 +65,11 @@ export type RegionListRes = { regions: RegionDTO[] } & ListRes export type RegionDeleteRes = DeleteRes // API Keys -export type ApiKeyRes = { api_key: ApiKeyDTO } -export type ApiKeyListRes = { api_keys: ApiKeyDTO[] } & ListRes +export type ExtendedApiKeyDTO = ApiKeyDTO & { + sales_channels: SalesChannelDTO[] | null +} +export type ApiKeyRes = { apiKey: ExtendedApiKeyDTO } +export type ApiKeyListRes = { apiKeys: ExtendedApiKeyDTO[] } & ListRes export type ApiKeyDeleteRes = DeleteRes // Sales Channels diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx index e354f1834aaf3..bdd0f20e92a09 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx @@ -1,10 +1,19 @@ import { RouteFocusModal } from "../../../components/route-modal" +import { useMe } from "../../../hooks/api/users" import { CreatePublishableApiKeyForm } from "./components/create-publishable-api-key-form" export const ApiKeyManagementCreate = () => { + const { user, isLoading, isError, error } = useMe() + + const ready = !isLoading && !!user + + if (isError) { + throw error + } + return ( - + {ready && } ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx index cadc874f86138..0713f3f05d2f0 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx @@ -1,21 +1,28 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Button, Heading, Input, Text } from "@medusajs/ui" -import { adminPublishableApiKeysKeys, useAdminCustomPost } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" +import { UserDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteFocusModal, useRouteModal, } from "../../../../../components/route-modal" +import { useCreateApiKey } from "../../../../../hooks/api/api-keys" + +type CreatePublishableApiKeyFormProps = { + user: UserDTO +} const CreatePublishableApiKeySchema = zod.object({ title: zod.string().min(1), }) -export const CreatePublishableApiKeyForm = () => { +export const CreatePublishableApiKeyForm = ({ + user, +}: CreatePublishableApiKeyFormProps) => { const { t } = useTranslation() const { handleSuccess } = useRouteModal() @@ -26,16 +33,15 @@ export const CreatePublishableApiKeyForm = () => { resolver: zodResolver(CreatePublishableApiKeySchema), }) - const { mutateAsync, isLoading } = useAdminCustomPost(`/admin/api-keys`, [ - adminPublishableApiKeysKeys.lists(), - ]) + const { mutateAsync, isPending } = useCreateApiKey() const handleSubmit = form.handleSubmit(async (values) => { await mutateAsync( + // @ts-ignore type is wrong compared to validation { title: values.title, type: "publishable" }, { - onSuccess: () => { - handleSuccess(`/settings/api-key-management`) + onSuccess: ({ api_key }) => { + handleSuccess(`/settings/api-key-management/${api_key.id}`) }, } ) @@ -54,7 +60,7 @@ export const CreatePublishableApiKeyForm = () => { {t("actions.cancel")} - diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx index d31d4d7b8b789..0e7b94cc3d489 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx @@ -1,6 +1,6 @@ -import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react" -import { Outlet, json, useLoaderData, useParams } from "react-router-dom" +import { Outlet, useLoaderData, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" +import { useApiKey } from "../../../hooks/api/api-keys" import { ApiKeyGeneralSection } from "./components/api-key-general-section" import { ApiKeySalesChannelSection } from "./components/api-key-sales-channel-section" import { apiKeyLoader } from "./loader" @@ -11,32 +11,23 @@ export const ApiKeyManagementDetail = () => { > const { id } = useParams() - const { data, isLoading, isError, error } = useAdminCustomQuery( - `/api-keys/${id}`, - [adminPublishableApiKeysKeys.detail(id!)], - undefined, - { - initialData: initialData, - } - ) + const { apiKey, isLoading, isError, error } = useApiKey(id!, undefined, { + initialData: initialData, + }) - if (isLoading) { + if (isLoading || !apiKey) { return
Loading...
} - if (isError || !data?.api_key) { - if (error) { - throw error - } - - throw json("An unknown error occurred", 500) + if (isError) { + throw error } return (
- - - + + +
) diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-general-section/api-key-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-general-section/api-key-general-section.tsx index 3074fba3273fe..6e42b7c49b9b4 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-general-section/api-key-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-general-section/api-key-general-section.tsx @@ -1,5 +1,5 @@ import { PencilSquare, Trash, XCircle } from "@medusajs/icons" -import { PublishableApiKey } from "@medusajs/medusa" +import { ApiKeyDTO } from "@medusajs/types" import { Container, Copy, @@ -8,18 +8,16 @@ import { Text, usePrompt, } from "@medusajs/ui" -import { - useAdminCustomQuery, - useAdminDeletePublishableApiKey, - useAdminRevokePublishableApiKey, - useAdminUser, -} from "medusa-react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" import { Skeleton } from "../../../../../components/common/skeleton" import { UserLink } from "../../../../../components/common/user-link" -import { ApiKeyDTO } from "@medusajs/types" +import { + useDeleteApiKey, + useRevokeApiKey, +} from "../../../../../hooks/api/api-keys" +import { useUser } from "../../../../../hooks/api/users" type ApiKeyGeneralSectionProps = { apiKey: ApiKeyDTO @@ -30,12 +28,8 @@ export const ApiKeyGeneralSection = ({ apiKey }: ApiKeyGeneralSectionProps) => { const navigate = useNavigate() const prompt = usePrompt() - const { mutateAsync: revokeAsync } = useAdminRevokePublishableApiKey( - apiKey.id - ) - const { mutateAsync: deleteAsync } = useAdminDeletePublishableApiKey( - apiKey.id - ) + const { mutateAsync: revokeAsync } = useRevokeApiKey(apiKey.id) + const { mutateAsync: deleteAsync } = useDeleteApiKey(apiKey.id) const handleDelete = async () => { const res = await prompt({ @@ -151,12 +145,9 @@ export const ApiKeyGeneralSection = ({ apiKey }: ApiKeyGeneralSectionProps) => { } const ActionBy = ({ userId }: { userId: string | null }) => { - const { data, isLoading, isError, error } = useAdminCustomQuery( - `/users/${userId}`, - [], - {}, - { enabled: !!userId } - ) + const { user, isLoading, isError, error } = useUser(userId!, undefined, { + enabled: !!userId, + }) if (!userId) { return ( @@ -179,7 +170,7 @@ const ActionBy = ({ userId }: { userId: string | null }) => { ) } - if (!data?.user) { + if (!user) { return ( - @@ -187,5 +178,5 @@ const ActionBy = ({ userId }: { userId: string | null }) => { ) } - return + return } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx index 26a84282d130f..ba7f51d7120f1 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx @@ -1,5 +1,5 @@ -import { PencilSquare, Trash } from "@medusajs/icons" -import { PublishableApiKey, SalesChannel } from "@medusajs/medusa" +import { PencilSquare } from "@medusajs/icons" +import { SalesChannelDTO } from "@medusajs/types" import { Button, Checkbox, @@ -19,12 +19,7 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table" -import { - adminPublishableApiKeysKeys, - useAdminCustomPost, - useAdminCustomQuery, - useAdminRemovePublishableKeySalesChannelsBatch, -} from "medusa-react" +import { adminPublishableApiKeysKeys, useAdminCustomPost } from "medusa-react" import { useMemo, useState } from "react" import { useTranslation } from "react-i18next" import { Link, useNavigate } from "react-router-dom" @@ -36,10 +31,10 @@ import { import { Query } from "../../../../../components/filtering/query" import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" import { useQueryParams } from "../../../../../hooks/use-query-params" -import { ApiKeyDTO } from "@medusajs/types" +import { ExtendedApiKeyDTO } from "../../../../../types/api-responses" type ApiKeySalesChannelSectionProps = { - apiKey: ApiKeyDTO + apiKey: ExtendedApiKeyDTO } const PAGE_SIZE = 10 @@ -73,16 +68,7 @@ export const ApiKeySalesChannelSection = ({ fields: "id,*sales_channels", } - const { data, isLoading, isError, error } = useAdminCustomQuery( - `/api-keys/${apiKey.id}`, - [adminPublishableApiKeysKeys.detailSalesChannels(apiKey.id, query)], - query, - { - keepPreviousData: true, - } - ) - - const salesChannels = data?.api_key?.sales_channels + const salesChannels = apiKey?.sales_channels const count = salesChannels?.length || 0 const columns = useColumns() @@ -138,11 +124,7 @@ export const ApiKeySalesChannelSection = ({ ) } - const noRecords = !isLoading && !salesChannels?.length && !params.q - - if (isError) { - throw error - } + const noRecords = !salesChannels?.length && !params.q return ( @@ -164,7 +146,7 @@ export const ApiKeySalesChannelSection = ({ ) : (
- {!isLoading && salesChannels?.length !== 0 ? ( + {salesChannels?.length !== 0 ? ( {table.getHeaderGroups().map((headerGroup) => { @@ -252,30 +234,30 @@ const SalesChannelActions = ({ salesChannel, apiKey, }: { - salesChannel: SalesChannel + salesChannel: SalesChannelDTO apiKey: string }) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = useAdminRemovePublishableKeySalesChannelsBatch(apiKey) + // const { mutateAsync } = useAdminRemovePublishableKeySalesChannelsBatch(apiKey) - const handleDelete = async () => { - const res = await prompt({ - title: t("general.areYouSure"), - description: t("apiKeyManagement.removeSalesChannelWarning"), - confirmText: t("actions.delete"), - cancelText: t("actions.cancel"), - }) + // const handleDelete = async () => { + // const res = await prompt({ + // title: t("general.areYouSure"), + // description: t("apiKeyManagement.removeSalesChannelWarning"), + // confirmText: t("actions.delete"), + // cancelText: t("actions.cancel"), + // }) - if (!res) { - return - } + // if (!res) { + // return + // } - await mutateAsync({ - sales_channel_ids: [{ id: salesChannel.id }], - }) - } + // await mutateAsync({ + // sales_channel_ids: [{ id: salesChannel.id }], + // }) + // } return ( , - label: t("actions.delete"), - onClick: handleDelete, - }, - ], - }, + // { + // actions: [ + // { + // icon: , + // label: t("actions.delete"), + // onClick: handleDelete, + // }, + // ], + // }, ]} /> ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const { t } = useTranslation() diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/loader.ts index c24627accbfbf..7d991ef4515e9 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/loader.ts @@ -1,13 +1,13 @@ -import { Response } from "@medusajs/medusa-js" -import { adminPublishableApiKeysKeys } from "medusa-react" import { LoaderFunctionArgs } from "react-router-dom" -import { ApiKeyDTO } from "@medusajs/types" -import { medusa, queryClient } from "../../../lib/medusa" +import { apiKeysQueryKeys } from "../../../hooks/api/api-keys" +import { client } from "../../../lib/client" +import { queryClient } from "../../../lib/medusa" +import { ApiKeyRes } from "../../../types/api-responses" const apiKeyDetailQuery = (id: string) => ({ - queryKey: adminPublishableApiKeysKeys.detail(id), - queryFn: async () => medusa.admin.custom.get(`/api-keys/${id}`), + queryKey: apiKeysQueryKeys.detail(id), + queryFn: async () => client.apiKeys.retrieve(id), }) export const apiKeyLoader = async ({ params }: LoaderFunctionArgs) => { @@ -15,8 +15,7 @@ export const apiKeyLoader = async ({ params }: LoaderFunctionArgs) => { const query = apiKeyDetailQuery(id!) return ( - queryClient.getQueryData>( - query.queryKey - ) ?? (await queryClient.fetchQuery(query)) + queryClient.getQueryData(query.queryKey) ?? + (await queryClient.fetchQuery(query)) ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx index ed7abab7aa48a..c8ad349f8ef73 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx @@ -1,25 +1,18 @@ import { Heading } from "@medusajs/ui" -import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react" import { useTranslation } from "react-i18next" -import { json, useParams } from "react-router-dom" +import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" +import { useApiKey } from "../../../hooks/api/api-keys" import { EditApiKeyForm } from "./components/edit-api-key-form" export const ApiKeyManagementEdit = () => { const { id } = useParams() const { t } = useTranslation() - const { data, isLoading, isError, error } = useAdminCustomQuery( - `/api-keys/${id}`, - [adminPublishableApiKeysKeys.detail(id!)] - ) - - if (isError || !data?.api_key) { - if (error) { - throw error - } + const { apiKey, isLoading, isError, error } = useApiKey(id!) - throw json("An unknown error has occured", 500) + if (isError) { + throw error } return ( @@ -27,9 +20,7 @@ export const ApiKeyManagementEdit = () => { {t("apiKeyManagement.editKey")} - {!isLoading && !!data?.api_key && ( - - )} + {!isLoading && !!apiKey && } ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx index 9ab46f920bb60..5d717b4b97c0e 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx @@ -1,13 +1,14 @@ import { Button, Container, Heading } from "@medusajs/ui" -import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react" +import { keepPreviousData } from "@tanstack/react-query" import { useTranslation } from "react-i18next" import { Link } from "react-router-dom" import { DataTable } from "../../../../../components/table/data-table" +import { useApiKeys } from "../../../../../hooks/api/api-keys" import { useDataTable } from "../../../../../hooks/use-data-table" +import { upperCaseFirst } from "../../../../../lib/uppercase-first" import { useApiKeyManagementTableColumns } from "./use-api-key-management-table-columns" import { useApiKeyManagementTableFilters } from "./use-api-key-management-table-filters" import { useApiKeyManagementTableQuery } from "./use-api-key-management-table-query" -import { upperCaseFirst } from "../../../../../lib/uppercase-first" const PAGE_SIZE = 20 @@ -30,17 +31,15 @@ export const ApiKeyManagementListTable = ({ } // @ts-ignore - const { data, count, isLoading, isError, error } = useAdminCustomQuery( - "/api-keys", - [adminPublishableApiKeysKeys.list(query)], - query - ) + const { apiKeys, count, isLoading, isError, error } = useApiKeys(query, { + placeholderData: keepPreviousData, + }) const filters = useApiKeyManagementTableFilters() const columns = useApiKeyManagementTableColumns() const { table } = useDataTable({ - data: data?.api_keys || [], + data: apiKeys || [], columns, count, enablePagination: true, @@ -48,10 +47,6 @@ export const ApiKeyManagementListTable = ({ pageSize: PAGE_SIZE, }) - if (isLoading) { - return
Loading...
- } - if (isError) { throw error } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/use-api-key-management-table-columns.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/use-api-key-management-table-columns.tsx index eaa8de0986446..d025953c4feb9 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/use-api-key-management-table-columns.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/use-api-key-management-table-columns.tsx @@ -6,16 +6,14 @@ import { adminPublishableApiKeysKeys, useAdminCustomDelete, useAdminCustomPost, - useAdminDeletePublishableApiKey, - useAdminRevokePublishableApiKey, } from "medusa-react" import { useMemo } from "react" import { useTranslation } from "react-i18next" +import { ApiKeyDTO } from "@medusajs/types" import { ActionMenu } from "../../../../../components/common/action-menu" import { DateCell } from "../../../../../components/table/table-cells/common/date-cell" import { StatusCell } from "../../../../../components/table/table-cells/common/status-cell" -import { ApiKeyDTO } from "@medusajs/types" const columnHelper = createColumnHelper() From 81787e638ff5e9b28240c91269ddf56255420c8d Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 17:31:18 +0200 Subject: [PATCH 6/7] comment out api key management that is not supported in v2 --- .../api-key-management-add-sales-channels.tsx | 37 +- .../create-publishable-api-key-form.tsx | 4 +- .../api-key-management-detail.tsx | 3 +- .../api-key-sales-channel-section.tsx | 669 +++++++++--------- .../edit-api-key-form/edit-api-key-form.tsx | 18 +- 5 files changed, 364 insertions(+), 367 deletions(-) diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx index 02c713df61068..b6d1f43d99f44 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-add-sales-channels/api-key-management-add-sales-channels.tsx @@ -1,36 +1,37 @@ -import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react" +// Missing Sales Channel endpoint for API Key domain + import { useParams } from "react-router-dom" import { RouteFocusModal } from "../../../components/route-modal" -import { AddSalesChannelsToApiKeyForm } from "./components" export const ApiKeyManagementAddSalesChannels = () => { const { id } = useParams() - const { data, isLoading, isError, error } = useAdminCustomQuery( - `/api-keys/${id}`, - [adminPublishableApiKeysKeys.detailSalesChannels(id!)], - { - fields: "id,*sales_channels", - }, - { - keepPreviousData: true, - } - ) + // const { data, isLoading, isError, error } = useAdminCustomQuery( + // `/api-keys/${id}`, + // [adminPublishableApiKeysKeys.detailSalesChannels(id!)], + // { + // fields: "id,*sales_channels", + // }, + // { + // keepPreviousData: true, + // } + // ) - if (isError) { - throw error - } + // if (isError) { + // throw error + // } - const salesChannels = data?.api_key?.sales_channels + // const salesChannels = data?.api_key?.sales_channels return ( - {!isLoading && salesChannels && ( + {/* {!isLoading && salesChannels && ( sc.id)} /> - )} + )} */} + TODO ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx index 0713f3f05d2f0..6bb879a303f92 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx @@ -40,8 +40,8 @@ export const CreatePublishableApiKeyForm = ({ // @ts-ignore type is wrong compared to validation { title: values.title, type: "publishable" }, { - onSuccess: ({ api_key }) => { - handleSuccess(`/settings/api-key-management/${api_key.id}`) + onSuccess: ({ apiKey }) => { + handleSuccess(`/settings/api-key-management/${apiKey.id}`) }, } ) diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx index 0e7b94cc3d489..f00b36b2c6158 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx @@ -2,7 +2,6 @@ import { Outlet, useLoaderData, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" import { useApiKey } from "../../../hooks/api/api-keys" import { ApiKeyGeneralSection } from "./components/api-key-general-section" -import { ApiKeySalesChannelSection } from "./components/api-key-sales-channel-section" import { apiKeyLoader } from "./loader" export const ApiKeyManagementDetail = () => { @@ -26,7 +25,7 @@ export const ApiKeyManagementDetail = () => { return (
- + {/* */}
diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx index ba7f51d7120f1..416d21927f779 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/components/api-key-sales-channel-section/api-key-sales-channel-section.tsx @@ -1,359 +1,366 @@ -import { PencilSquare } from "@medusajs/icons" -import { SalesChannelDTO } from "@medusajs/types" -import { - Button, - Checkbox, - CommandBar, - Container, - Heading, - StatusBadge, - Table, - clx, - usePrompt, -} from "@medusajs/ui" -import { - PaginationState, - RowSelectionState, - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table" -import { adminPublishableApiKeysKeys, useAdminCustomPost } from "medusa-react" -import { useMemo, useState } from "react" -import { useTranslation } from "react-i18next" -import { Link, useNavigate } from "react-router-dom" -import { ActionMenu } from "../../../../../components/common/action-menu" -import { - NoRecords, - NoResults, -} from "../../../../../components/common/empty-table-content" -import { Query } from "../../../../../components/filtering/query" -import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" -import { useQueryParams } from "../../../../../hooks/use-query-params" -import { ExtendedApiKeyDTO } from "../../../../../types/api-responses" +// Api Key Sales Channel Endpoint is not part of v2 -type ApiKeySalesChannelSectionProps = { - apiKey: ExtendedApiKeyDTO -} +// import { PencilSquare, Trash } from "@medusajs/icons" +// import { SalesChannel } from "@medusajs/medusa" +// import { +// Button, +// Checkbox, +// CommandBar, +// Container, +// Heading, +// StatusBadge, +// Table, +// clx, +// usePrompt, +// } from "@medusajs/ui" +// import { +// PaginationState, +// RowSelectionState, +// createColumnHelper, +// flexRender, +// getCoreRowModel, +// useReactTable, +// } from "@tanstack/react-table" +// import { +// adminPublishableApiKeysKeys, +// useAdminCustomPost, +// useAdminRemovePublishableKeySalesChannelsBatch, +// } from "medusa-react" +// import { useMemo, useState } from "react" +// import { useTranslation } from "react-i18next" +// import { Link, useNavigate } from "react-router-dom" +// import { ActionMenu } from "../../../../../components/common/action-menu" +// import { +// NoRecords, +// NoResults, +// } from "../../../../../components/common/empty-table-content" +// import { Query } from "../../../../../components/filtering/query" +// import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination" +// import { useQueryParams } from "../../../../../hooks/use-query-params" +// import { ExtendedApiKeyDTO } from "../../../../../types/api-responses" +// import { SalesChannelDTO } from "@medusajs/types" -const PAGE_SIZE = 10 +// type ApiKeySalesChannelSectionProps = { +// apiKey: ExtendedApiKeyDTO +// } -export const ApiKeySalesChannelSection = ({ - apiKey, -}: ApiKeySalesChannelSectionProps) => { - const { t } = useTranslation() - const navigate = useNavigate() - const prompt = usePrompt() +// const PAGE_SIZE = 10 - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: PAGE_SIZE, - }) +// export const ApiKeySalesChannelSection = ({ +// apiKey, +// }: ApiKeySalesChannelSectionProps) => { +// const { t } = useTranslation() +// const navigate = useNavigate() +// const prompt = usePrompt() - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize] - ) +// const [{ pageIndex, pageSize }, setPagination] = useState({ +// pageIndex: 0, +// pageSize: PAGE_SIZE, +// }) - const [rowSelection, setRowSelection] = useState({}) +// const pagination = useMemo( +// () => ({ +// pageIndex, +// pageSize, +// }), +// [pageIndex, pageSize] +// ) - const params = useQueryParams(["q"]) +// const [rowSelection, setRowSelection] = useState({}) - const query = { - ...params, - fields: "id,*sales_channels", - } +// const params = useQueryParams(["q"]) - const salesChannels = apiKey?.sales_channels - const count = salesChannels?.length || 0 +// const query = { +// ...params, +// fields: "id,*sales_channels", +// } - const columns = useColumns() +// const salesChannels = apiKey?.sales_channels +// const count = salesChannels?.length || 0 - const table = useReactTable({ - data: salesChannels ?? [], - columns, - pageCount: Math.ceil(count / PAGE_SIZE), - state: { - pagination, - rowSelection, - }, - getRowId: (row) => row.id, - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - meta: { - apiKey: apiKey.id, - }, - }) +// const columns = useColumns() - const { mutateAsync } = useAdminCustomPost( - `/api-keys/${apiKey.id}/sales-channels/batch/remove`, - [adminPublishableApiKeysKeys.detailSalesChannels(apiKey.id)] - ) +// const table = useReactTable({ +// data: salesChannels ?? [], +// columns, +// pageCount: Math.ceil(count / PAGE_SIZE), +// state: { +// pagination, +// rowSelection, +// }, +// getRowId: (row) => row.id, +// onPaginationChange: setPagination, +// onRowSelectionChange: setRowSelection, +// getCoreRowModel: getCoreRowModel(), +// manualPagination: true, +// meta: { +// apiKey: apiKey.id, +// }, +// }) - const handleRemove = async () => { - const keys = Object.keys(rowSelection).filter((k) => rowSelection[k]) +// const { mutateAsync } = useAdminCustomPost( +// `/api-keys/${apiKey.id}/sales-channels/batch/remove`, +// [adminPublishableApiKeysKeys.detailSalesChannels(apiKey.id)] +// ) - const res = await prompt({ - title: t("general.areYouSure"), - description: t("apiKeyManagement.removeSalesChannelsWarning", { - count: keys.length, - }), - confirmText: t("actions.continue"), - cancelText: t("actions.cancel"), - }) +// const handleRemove = async () => { +// const keys = Object.keys(rowSelection).filter((k) => rowSelection[k]) - if (!res) { - return - } +// const res = await prompt({ +// title: t("general.areYouSure"), +// description: t("apiKeyManagement.removeSalesChannelsWarning", { +// count: keys.length, +// }), +// confirmText: t("actions.continue"), +// cancelText: t("actions.cancel"), +// }) - await mutateAsync( - { - sales_channel_ids: keys, - }, - { - onSuccess: () => { - setRowSelection({}) - }, - } - ) - } +// if (!res) { +// return +// } - const noRecords = !salesChannels?.length && !params.q +// await mutateAsync( +// { +// sales_channel_ids: keys, +// }, +// { +// onSuccess: () => { +// setRowSelection({}) +// }, +// } +// ) +// } - return ( - -
- {t("salesChannels.domain")} - -
- {!noRecords && ( -
-
-
- -
-
- )} - {noRecords ? ( - - ) : ( -
- {salesChannels?.length !== 0 ? ( -
- - {table.getHeaderGroups().map((headerGroup) => { - return ( - - {headerGroup.headers.map((header) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ) - })} - - ) - })} - - - {table.getRowModel().rows.map((row) => ( - - navigate(`/settings/sales-channels/${row.original.id}`) - } - > - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} - - ))} - -
- ) : ( - - )} - - - - - {t("general.countSelected", { - count: Object.keys(rowSelection).length, - })} - - - - - -
- )} -
- ) -} +// const noRecords = !salesChannels?.length && !params.q -const SalesChannelActions = ({ - salesChannel, - apiKey, -}: { - salesChannel: SalesChannelDTO - apiKey: string -}) => { - const { t } = useTranslation() - const prompt = usePrompt() +// return ( +// +//
+// {t("salesChannels.domain")} +// +//
+// {!noRecords && ( +//
+//
+//
+// +//
+//
+// )} +// {noRecords ? ( +// +// ) : ( +//
+// {salesChannels?.length !== 0 ? ( +// +// +// {table.getHeaderGroups().map((headerGroup) => { +// return ( +// +// {headerGroup.headers.map((header) => { +// return ( +// +// {flexRender( +// header.column.columnDef.header, +// header.getContext() +// )} +// +// ) +// })} +// +// ) +// })} +// +// +// {table.getRowModel().rows.map((row) => ( +// +// navigate(`/settings/sales-channels/${row.original.id}`) +// } +// > +// {row.getVisibleCells().map((cell) => ( +// +// {flexRender( +// cell.column.columnDef.cell, +// cell.getContext() +// )} +// +// ))} +// +// ))} +// +//
+// ) : ( +// +// )} +// +// +// +// +// {t("general.countSelected", { +// count: Object.keys(rowSelection).length, +// })} +// +// +// +// +// +//
+// )} +//
+// ) +// } - // const { mutateAsync } = useAdminRemovePublishableKeySalesChannelsBatch(apiKey) +// const SalesChannelActions = ({ +// salesChannel, +// apiKey, +// }: { +// salesChannel: SalesChannelDTO +// apiKey: string +// }) => { +// const { t } = useTranslation() +// const prompt = usePrompt() - // const handleDelete = async () => { - // const res = await prompt({ - // title: t("general.areYouSure"), - // description: t("apiKeyManagement.removeSalesChannelWarning"), - // confirmText: t("actions.delete"), - // cancelText: t("actions.cancel"), - // }) +// const { mutateAsync } = useAdminRemovePublishableKeySalesChannelsBatch(apiKey) - // if (!res) { - // return - // } +// const handleDelete = async () => { +// const res = await prompt({ +// title: t("general.areYouSure"), +// description: t("apiKeyManagement.removeSalesChannelWarning"), +// confirmText: t("actions.delete"), +// cancelText: t("actions.cancel"), +// }) - // await mutateAsync({ - // sales_channel_ids: [{ id: salesChannel.id }], - // }) - // } +// if (!res) { +// return +// } - return ( - , - label: t("actions.edit"), - to: `/settings/sales-channels/${salesChannel.id}/edit`, - }, - ], - }, - // { - // actions: [ - // { - // icon: , - // label: t("actions.delete"), - // onClick: handleDelete, - // }, - // ], - // }, - ]} - /> - ) -} +// await mutateAsync({ +// sales_channel_ids: [{ id: salesChannel.id }], +// }) +// } -const columnHelper = createColumnHelper() +// return ( +// , +// label: t("actions.edit"), +// to: `/settings/sales-channels/${salesChannel.id}/edit`, +// }, +// ], +// }, +// { +// actions: [ +// { +// icon: , +// label: t("actions.delete"), +// onClick: handleDelete, +// }, +// ], +// }, +// ]} +// /> +// ) +// } -const useColumns = () => { - const { t } = useTranslation() +// const columnHelper = createColumnHelper() - return useMemo( - () => [ - columnHelper.display({ - id: "select", - header: ({ table }) => { - return ( - - table.toggleAllPageRowsSelected(!!value) - } - /> - ) - }, - cell: ({ row }) => { - return ( - row.toggleSelected(!!value)} - onClick={(e) => { - e.stopPropagation() - }} - /> - ) - }, - }), - columnHelper.accessor("name", { - header: t("fields.name"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("description", { - header: t("fields.description"), - cell: ({ getValue }) => getValue(), - }), - columnHelper.accessor("is_disabled", { - header: t("fields.status"), - cell: ({ getValue }) => { - const value = getValue() - return ( -
- - {value ? t("general.disabled") : t("general.enabled")} - -
- ) - }, - }), - columnHelper.display({ - id: "actions", - cell: ({ row, table }) => { - const { apiKey } = table.options.meta as { - apiKey: string - } +// const useColumns = () => { +// const { t } = useTranslation() - return ( - - ) - }, - }), - ], - [t] - ) -} +// return useMemo( +// () => [ +// columnHelper.display({ +// id: "select", +// header: ({ table }) => { +// return ( +// +// table.toggleAllPageRowsSelected(!!value) +// } +// /> +// ) +// }, +// cell: ({ row }) => { +// return ( +// row.toggleSelected(!!value)} +// onClick={(e) => { +// e.stopPropagation() +// }} +// /> +// ) +// }, +// }), +// columnHelper.accessor("name", { +// header: t("fields.name"), +// cell: ({ getValue }) => getValue(), +// }), +// columnHelper.accessor("description", { +// header: t("fields.description"), +// cell: ({ getValue }) => getValue(), +// }), +// columnHelper.accessor("is_disabled", { +// header: t("fields.status"), +// cell: ({ getValue }) => { +// const value = getValue() +// return ( +//
+// +// {value ? t("general.disabled") : t("general.enabled")} +// +//
+// ) +// }, +// }), +// columnHelper.display({ +// id: "actions", +// cell: ({ row, table }) => { +// const { apiKey } = table.options.meta as { +// apiKey: string +// } + +// return ( +// +// ) +// }, +// }), +// ], +// [t] +// ) +// } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx index 3cab495052bfb..35dc124e58735 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/components/edit-api-key-form/edit-api-key-form.tsx @@ -1,16 +1,16 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Button, Input } from "@medusajs/ui" -import { adminPublishableApiKeysKeys, useAdminCustomPost } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" +import { ApiKeyDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" -import { ApiKeyDTO } from "@medusajs/types" +import { useUpdateApiKey } from "../../../../../hooks/api/api-keys" type EditApiKeyFormProps = { apiKey: ApiKeyDTO @@ -31,23 +31,13 @@ export const EditApiKeyForm = ({ apiKey }: EditApiKeyFormProps) => { resolver: zodResolver(EditApiKeySchema), }) - const { mutateAsync, isLoading } = useAdminCustomPost( - `/api-keys/${apiKey.id}`, - [ - adminPublishableApiKeysKeys.lists(), - adminPublishableApiKeysKeys.detail(apiKey.id), - adminPublishableApiKeysKeys.details(), - ] - ) + const { mutateAsync, isPending } = useUpdateApiKey(apiKey.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync(data, { onSuccess: () => { handleSuccess() }, - onError: (error) => { - console.log(error) - }, }) }) @@ -80,7 +70,7 @@ export const EditApiKeyForm = ({ apiKey }: EditApiKeyFormProps) => { {t("actions.cancel")} - From e8fc3de5d3c496510d545862cb1a1ba10e1d66f8 Mon Sep 17 00:00:00 2001 From: Kasper Date: Fri, 5 Apr 2024 17:44:27 +0200 Subject: [PATCH 7/7] fix naming --- .../admin-next/dashboard/src/types/api-responses.ts | 4 ++-- .../api-key-management-create.tsx | 11 +---------- .../create-publishable-api-key-form.tsx | 13 +++---------- .../api-key-management-detail.tsx | 8 ++++---- .../api-key-management-edit.tsx | 4 ++-- .../api-key-management-list-table.tsx | 5 ++--- 6 files changed, 14 insertions(+), 31 deletions(-) diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index 23d39d7c12765..848ca71c62407 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -68,8 +68,8 @@ export type RegionDeleteRes = DeleteRes export type ExtendedApiKeyDTO = ApiKeyDTO & { sales_channels: SalesChannelDTO[] | null } -export type ApiKeyRes = { apiKey: ExtendedApiKeyDTO } -export type ApiKeyListRes = { apiKeys: ExtendedApiKeyDTO[] } & ListRes +export type ApiKeyRes = { api_key: ExtendedApiKeyDTO } +export type ApiKeyListRes = { api_keys: ExtendedApiKeyDTO[] } & ListRes export type ApiKeyDeleteRes = DeleteRes // Sales Channels diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx index bdd0f20e92a09..e354f1834aaf3 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/api-key-management-create.tsx @@ -1,19 +1,10 @@ import { RouteFocusModal } from "../../../components/route-modal" -import { useMe } from "../../../hooks/api/users" import { CreatePublishableApiKeyForm } from "./components/create-publishable-api-key-form" export const ApiKeyManagementCreate = () => { - const { user, isLoading, isError, error } = useMe() - - const ready = !isLoading && !!user - - if (isError) { - throw error - } - return ( - {ready && } + ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx index 6bb879a303f92..b83277764558c 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-create/components/create-publishable-api-key-form/create-publishable-api-key-form.tsx @@ -4,7 +4,6 @@ import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" -import { UserDTO } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { RouteFocusModal, @@ -12,17 +11,11 @@ import { } from "../../../../../components/route-modal" import { useCreateApiKey } from "../../../../../hooks/api/api-keys" -type CreatePublishableApiKeyFormProps = { - user: UserDTO -} - const CreatePublishableApiKeySchema = zod.object({ title: zod.string().min(1), }) -export const CreatePublishableApiKeyForm = ({ - user, -}: CreatePublishableApiKeyFormProps) => { +export const CreatePublishableApiKeyForm = () => { const { t } = useTranslation() const { handleSuccess } = useRouteModal() @@ -40,8 +33,8 @@ export const CreatePublishableApiKeyForm = ({ // @ts-ignore type is wrong compared to validation { title: values.title, type: "publishable" }, { - onSuccess: ({ apiKey }) => { - handleSuccess(`/settings/api-key-management/${apiKey.id}`) + onSuccess: ({ api_key }) => { + handleSuccess(`/settings/api-key-management/${api_key.id}`) }, } ) diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx index f00b36b2c6158..caed18195b6bf 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-detail/api-key-management-detail.tsx @@ -10,11 +10,11 @@ export const ApiKeyManagementDetail = () => { > const { id } = useParams() - const { apiKey, isLoading, isError, error } = useApiKey(id!, undefined, { + const { api_key, isLoading, isError, error } = useApiKey(id!, undefined, { initialData: initialData, }) - if (isLoading || !apiKey) { + if (isLoading || !api_key) { return
Loading...
} @@ -24,9 +24,9 @@ export const ApiKeyManagementDetail = () => { return (
- + {/* */} - +
) diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx index c8ad349f8ef73..f6d4127d1af1d 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-edit/api-key-management-edit.tsx @@ -9,7 +9,7 @@ export const ApiKeyManagementEdit = () => { const { id } = useParams() const { t } = useTranslation() - const { apiKey, isLoading, isError, error } = useApiKey(id!) + const { api_key, isLoading, isError, error } = useApiKey(id!) if (isError) { throw error @@ -20,7 +20,7 @@ export const ApiKeyManagementEdit = () => { {t("apiKeyManagement.editKey")} - {!isLoading && !!apiKey && } + {!isLoading && !!api_key && } ) } diff --git a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx index 5d717b4b97c0e..226efeaeec5ea 100644 --- a/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/api-key-management/api-key-management-list/components/api-key-management-list-table/api-key-management-list-table.tsx @@ -30,8 +30,7 @@ export const ApiKeyManagementListTable = ({ "id,title,redacted,token,type,created_at,updated_at,revoked_at,last_used_at,created_by,revoked_by", } - // @ts-ignore - const { apiKeys, count, isLoading, isError, error } = useApiKeys(query, { + const { api_keys, count, isLoading, isError, error } = useApiKeys(query, { placeholderData: keepPreviousData, }) @@ -39,7 +38,7 @@ export const ApiKeyManagementListTable = ({ const columns = useApiKeyManagementTableColumns() const { table } = useDataTable({ - data: apiKeys || [], + data: api_keys || [], columns, count, enablePagination: true,