fix(admin-next, utils, medusa): Handle db errors properly and fix form (#7416)
* fix(admin-next, utils, medusa): Handle db errors properly and fix form * fix(admin-next, utils, medusa): Handle db errors properly and fix form * fix(admin-next, utils, medusa): Handle db errors properly and fix form * fix(admin-next, utils, medusa): Handle db errors properly and fix form
This commit is contained in:
1 parent
e01472aae6
commit
37ae87fe9b
6 files changed
+47
-14
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
import { FetchError } from "@medusajs/js-sdk"
|
||||||
|
|
||||||
|
export const isFetchError = (error: any): error is FetchError => {
|
||||||
|
return error instanceof FetchError
|
||||||
|
}
|
||||||
+24
-9
@@ -2,6 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
|||||||
import { ArrowPath, Trash } from "@medusajs/icons"
|
import { ArrowPath, Trash } from "@medusajs/icons"
|
||||||
import { InviteDTO } from "@medusajs/types"
|
import { InviteDTO } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
Container,
|
Container,
|
||||||
Heading,
|
Heading,
|
||||||
@@ -29,6 +30,7 @@ import {
|
|||||||
} from "../../../../../hooks/api/invites"
|
} from "../../../../../hooks/api/invites"
|
||||||
import { DataTable } from "../../../../../components/table/data-table"
|
import { DataTable } from "../../../../../components/table/data-table"
|
||||||
import { useUserInviteTableQuery } from "../../../../../hooks/table/query/use-user-invite-table-query"
|
import { useUserInviteTableQuery } from "../../../../../hooks/table/query/use-user-invite-table-query"
|
||||||
|
import { isFetchError } from "../../../../../lib/is-fetch-error.ts"
|
||||||
|
|
||||||
const InviteUserSchema = zod.object({
|
const InviteUserSchema = zod.object({
|
||||||
email: zod.string().email(),
|
email: zod.string().email(),
|
||||||
@@ -74,16 +76,18 @@ export const InviteUserForm = () => {
|
|||||||
const { mutateAsync, isPending } = useCreateInvite()
|
const { mutateAsync, isPending } = useCreateInvite()
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
await mutateAsync(
|
try {
|
||||||
{
|
await mutateAsync({ email: values.email })
|
||||||
email: values.email,
|
form.reset()
|
||||||
},
|
} catch (error) {
|
||||||
{
|
if (isFetchError(error) && error.status === 400) {
|
||||||
onSuccess: () => {
|
form.setError("root", {
|
||||||
form.reset()
|
type: "manual",
|
||||||
},
|
message: error.message,
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
@@ -106,6 +110,17 @@ export const InviteUserForm = () => {
|
|||||||
{t("users.inviteUserHint")}
|
{t("users.inviteUserHint")}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{form.formState.errors.root && (
|
||||||
|
<Alert
|
||||||
|
variant="error"
|
||||||
|
dismissible={false}
|
||||||
|
className="text-balance"
|
||||||
|
>
|
||||||
|
{form.formState.errors.root.message}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col gap-y-4">
|
<div className="flex flex-col gap-y-4">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<Form.Field
|
<Form.Field
|
||||||
|
|||||||
@@ -45,8 +45,12 @@ const normalizeRequest = (
|
|||||||
|
|
||||||
const normalizeResponse = async (resp: Response, reqHeaders: Headers) => {
|
const normalizeResponse = async (resp: Response, reqHeaders: Headers) => {
|
||||||
if (resp.status >= 300) {
|
if (resp.status >= 300) {
|
||||||
const error = new FetchError(resp.statusText, resp.status)
|
const jsonError = await resp.json().catch(() => ({})) as { message?: string }
|
||||||
throw error
|
throw new FetchError(
|
||||||
|
jsonError.message ?? resp.statusText,
|
||||||
|
resp.statusText,
|
||||||
|
resp.status
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we requested JSON, we try to parse the response. Otherwise, we return the raw response.
|
// If we requested JSON, we try to parse the response. Otherwise, we return the raw response.
|
||||||
@@ -56,9 +60,11 @@ const normalizeResponse = async (resp: Response, reqHeaders: Headers) => {
|
|||||||
|
|
||||||
export class FetchError extends Error {
|
export class FetchError extends Error {
|
||||||
status: number | undefined
|
status: number | undefined
|
||||||
|
statusText: string | undefined
|
||||||
|
|
||||||
constructor(message: string, status?: number) {
|
constructor(message: string, statusText?: string, status?: number) {
|
||||||
super(message)
|
super(message)
|
||||||
|
this.statusText = statusText
|
||||||
this.status = status
|
this.status = status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ class Medusa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default Medusa
|
export default Medusa
|
||||||
|
export { FetchError } from "./client"
|
||||||
@@ -74,7 +74,7 @@ const getConstraintInfo = (err: any) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const [keys, values] = detail.match(/\([^\(.]*\)/g) || []
|
const [keys, values] = detail.match(/\([^\(]*\)/g) || []
|
||||||
|
|
||||||
if (!keys || !values) {
|
if (!keys || !values) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -45,9 +45,15 @@ export const POST = async (
|
|||||||
input: {
|
input: {
|
||||||
invites: [req.validatedBody],
|
invites: [req.validatedBody],
|
||||||
},
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, errors } = await workflow.run(input)
|
||||||
|
|
||||||
|
if (errors?.length) {
|
||||||
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const { result } = await workflow.run(input)
|
|
||||||
const invite = await refetchInvite(
|
const invite = await refetchInvite(
|
||||||
result[0].id,
|
result[0].id,
|
||||||
req.scope,
|
req.scope,
|
||||||
|
|||||||
Reference in new issue
Block a user