fix(admin-ui): show failure reason for batch jobs (#3526)

* fix: display error messages for batch jobs

* feat: add changesets

* feat: tooltip size, load more jobs

---------

Co-authored-by: fPolic <frane@medusajs.com>
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Frane Polić
2023-03-21 16:13:39 +01:00
committed by GitHub
parent 7f2223b650
commit f831b7db37
9 changed files with 117 additions and 47 deletions

View File

@@ -10,6 +10,7 @@ export type TooltipProps = RadixTooltip.TooltipContentProps &
content: React.ReactNode
side?: "bottom" | "left" | "top" | "right"
onClick?: React.ButtonHTMLAttributes<HTMLButtonElement>["onClick"]
maxWidth?: number
}
const Tooltip = ({
@@ -19,6 +20,7 @@ const Tooltip = ({
defaultOpen,
onOpenChange,
delayDuration,
maxWidth = 220,
className,
side,
onClick,
@@ -43,10 +45,10 @@ const Tooltip = ({
"inter-small-semibold text-grey-50",
"bg-grey-0 shadow-dropdown rounded-rounded py-2 px-3",
"border-grey-20 border border-solid",
"max-w-[220px]",
className
)}
{...props}
style={{ ...props.style, maxWidth }}
>
{content}
</RadixTooltip.Content>

View File

@@ -1,13 +1,25 @@
import { ReactNode } from "react"
import clsx from "clsx"
import Tooltip from "../../atoms/tooltip"
type Props = {
fileName: string
fileSize?: string
errorMessage?: string
hasError?: boolean
icon?: ReactNode
onClick?: () => void
}
const BatchJobFileCard = ({ fileName, fileSize, icon, onClick }: Props) => {
const BatchJobFileCard = ({
fileName,
fileSize,
icon,
onClick,
hasError,
errorMessage,
}: Props) => {
const preparedOnClick = onClick ?? (() => void 0)
return (
@@ -27,9 +39,26 @@ const BatchJobFileCard = ({ fileName, fileSize, icon, onClick }: Props) => {
{fileName}
</div>
{!!fileSize && (
<div className="text-grey-40 inter-small-regular">{fileSize}</div>
)}
<Tooltip
side="top"
open={hasError ? undefined : false}
maxWidth={320}
content={
hasError && errorMessage ? (
<span className="font-normal text-rose-500">{errorMessage}</span>
) : null
}
>
{!!fileSize && (
<div
className={clsx("text-grey-40 inter-small-regular", {
"text-rose-500": hasError,
})}
>
{fileSize}
</div>
)}
</Tooltip>
</div>
</div>
)

View File

@@ -19,6 +19,7 @@ import MedusaIcon from "../../fundamentals/icons/medusa-icon"
import { ActivityCard } from "../../molecules/activity-card"
import BatchJobFileCard from "../../molecules/batch-job-file-card"
import { batchJobDescriptionBuilder, BatchJobOperation } from "./utils"
import CrossIcon from "../../fundamentals/icons/cross-icon"
/**
* Retrieve a batch job and refresh the data depending on the last batch job status
@@ -97,6 +98,8 @@ const BatchJobActivityCard = (props: { batchJob: BatchJob }) => {
batchJob.status !== "failed" &&
batchJob.status !== "canceled"
const hasError = batchJob.status === "failed"
const canDownload =
batchJob.status === "completed" && batchJob.result?.file_key
@@ -157,7 +160,11 @@ const BatchJobActivityCard = (props: { batchJob: BatchJob }) => {
const icon =
batchJob.status !== "completed" && batchJob.status !== "canceled" ? (
<Spinner size={"medium"} variant={"secondary"} />
batchJob.status === "failed" ? (
<CrossIcon size={18} />
) : (
<Spinner size={"medium"} variant={"secondary"} />
)
) : (
<FileIcon
className={clsx({
@@ -175,7 +182,7 @@ const BatchJobActivityCard = (props: { batchJob: BatchJob }) => {
preprocessing: `Preparing ${operation.toLowerCase()}...`,
processing: `Processing ${operation.toLowerCase()}...`,
completed: `Successful ${operation.toLowerCase()}`,
failed: `Failed batch ${operation.toLowerCase()} job`,
failed: `Job failed`,
canceled: `Canceled batch ${operation.toLowerCase()} job`,
}[batchJob.status]
@@ -185,6 +192,8 @@ const BatchJobActivityCard = (props: { batchJob: BatchJob }) => {
fileName={fileName}
icon={icon}
fileSize={fileSize}
hasError={hasError}
errorMessage={batchJob?.result?.errors?.join(" \n")}
/>
)
}

View File

@@ -1,4 +1,5 @@
import { ReactNode, useState } from "react"
import clsx from "clsx"
import Button from "../../fundamentals/button"
import CheckCircleIcon from "../../fundamentals/icons/check-circle-icon"
@@ -8,12 +9,13 @@ import FileIcon from "../../fundamentals/icons/file-icon"
import Modal from "../../molecules/modal"
import TrashIcon from "../../fundamentals/icons/trash-icon"
import WarningCircleIcon from "../../fundamentals/icons/warning-circle"
import XCircleIcon from "../../fundamentals/icons/x-circle-icon"
import clsx from "clsx"
import Tooltip from "../../atoms/tooltip"
type FileSummaryProps = {
name: string
size: number
hasError?: boolean
errorMessage?: string
action: ReactNode
progress?: number
status?: string
@@ -23,7 +25,7 @@ type FileSummaryProps = {
* Render an upload file summary (& upload progress).
*/
function FileSummary(props: FileSummaryProps) {
const { action, name, progress, size, status } = props
const { action, name, progress, size, status, hasError, errorMessage } = props
const formattedSize =
size / 1024 < 10
@@ -32,32 +34,46 @@ function FileSummary(props: FileSummaryProps) {
return (
<div className="relative">
<div
style={{ width: `${progress}%` }}
className="bg-grey-5 transition-width absolute h-full duration-150 ease-in-out"
/>
<div className="border-1 relative mt-6 flex items-center rounded-xl border">
<div className="m-4">
<FileIcon size={30} fill={progress ? "#9CA3AF" : "#2DD4BF"} />
</div>
<div className="my-6 flex-1">
<div className="text-small text-grey-90 leading-5">{name}</div>
<div className="text-xsmall text-grey-50 leading-4">
{status || formattedSize}
<Tooltip
side="top"
maxWidth={320}
open={hasError ? undefined : false}
content={
hasError && errorMessage ? (
<span className="font-normal text-rose-500">{errorMessage}</span>
) : null
}
>
<div
style={{ width: `${progress}%` }}
className="bg-grey-5 transition-width absolute h-full duration-150 ease-in-out"
/>
<div className="border-1 relative mt-6 flex items-center rounded-xl border">
<div className="m-4">
<FileIcon size={30} fill={progress ? "#9CA3AF" : "#2DD4BF"} />
</div>
</div>
<div className="m-6">{action}</div>
</div>
<div className="my-6 flex-1">
<div className="text-small text-grey-90 leading-5">{name}</div>
<div
className={clsx("text-xsmall text-grey-50 leading-4", {
"text-rose-500": hasError,
})}
>
{status || formattedSize}
</div>
</div>
<div className="m-6">{action}</div>
</div>
</Tooltip>
</div>
)
}
type UploadSummaryProps = {
creations: number
updates: number
rejections?: number
creations?: number
updates?: number
type: string
}
@@ -65,25 +81,18 @@ type UploadSummaryProps = {
* Render a batch update request summary.
*/
function UploadSummary(props: UploadSummaryProps) {
const { creations, updates, rejections, type } = props
const { creations, updates, type } = props
return (
<div className="flex gap-6">
<div className="text-small text-grey-90 flex items-center">
<CheckCircleIcon color="#9CA3AF" className="mr-2" />
<span className="font-semibold"> {creations}&nbsp;</span> new {type}
<span className="font-semibold"> {creations || 0}&nbsp;</span> new{" "}
{type}
</div>
<div className="text-small text-grey-90 flex items-center">
<WarningCircleIcon fill="#9CA3AF" className="mr-2" />
<span className="font-semibold">{updates || 0}&nbsp;</span> updates
</div>
{updates && (
<div className="text-small text-grey-90 flex items-center">
<WarningCircleIcon fill="#9CA3AF" className="mr-2" />
<span className="font-semibold">{updates}&nbsp;</span> updates
</div>
)}
{rejections && (
<div className="text-small text-grey-90 flex items-center">
<XCircleIcon color="#9CA3AF" className="mr-2" />
<span className="font-semibold">{rejections}&nbsp;</span> rejections
</div>
)}
</div>
)
}
@@ -155,6 +164,8 @@ function DropArea(props: DropAreaProps) {
type UploadModalProps = {
type: string
status?: string
hasError?: boolean
errorMessage?: string
fileTitle: string
description1Text: string
description2Title: string
@@ -184,8 +195,9 @@ function UploadModal(props: UploadModalProps) {
onSubmit,
onFileRemove,
templateLink,
progress,
summary,
hasError,
errorMessage,
status,
type,
} = props
@@ -237,6 +249,8 @@ function UploadModal(props: UploadModalProps) {
size={size!}
name={name!}
status={status}
hasError={hasError}
errorMessage={errorMessage}
// progress={progress}
// TODO: change this to actual progress once this we can track upload
progress={100}
@@ -284,7 +298,7 @@ function UploadModal(props: UploadModalProps) {
<Button
size="small"
disabled={!canImport}
disabled={!canImport || hasError}
variant="primary"
className="text-small"
onClick={onSubmit}