mirror of
https://github.com/m1ngsama/FUJI.git
synced 2025-12-24 10:51:27 +00:00
add error handling
This commit is contained in:
parent
42df93b9e6
commit
094d6b17e4
2 changed files with 136 additions and 7 deletions
|
|
@ -11,6 +11,14 @@ type TicketFormData = {
|
|||
description?: string
|
||||
}
|
||||
|
||||
type FormError = {
|
||||
field?: string
|
||||
message: string
|
||||
type: "validation" | "network" | "server" | "auth"
|
||||
}
|
||||
|
||||
type SubmissionState = "idle" | "submitting" | "success" | "error" | "retrying"
|
||||
|
||||
function LoginHintAlert(props: {
|
||||
onLogin: () => void
|
||||
}) {
|
||||
|
|
@ -36,13 +44,86 @@ function TicketForm(props: {
|
|||
userInfo?: UserInfoResponse
|
||||
onSubmit: (form: TicketFormData) => Promise<void>
|
||||
}) {
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
const [submissionState, setSubmissionState] = useState<SubmissionState>("idle")
|
||||
const [formData, setFormData] = useState<TicketFormData>({})
|
||||
const [errors, setErrors] = useState<FormError[]>([])
|
||||
|
||||
// Form persistence
|
||||
useEffect(() => {
|
||||
const savedData = localStorage.getItem("ticket-form-draft")
|
||||
if (savedData) {
|
||||
try {
|
||||
const parsed = JSON.parse(savedData)
|
||||
setFormData(parsed)
|
||||
}
|
||||
catch (error) {
|
||||
console.warn("Failed to parse saved form data:", error)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(formData).length > 0) {
|
||||
localStorage.setItem("ticket-form-draft", JSON.stringify(formData))
|
||||
}
|
||||
}, [formData])
|
||||
|
||||
const clearFormData = () => {
|
||||
localStorage.removeItem("ticket-form-draft")
|
||||
setFormData({})
|
||||
}
|
||||
|
||||
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
setLoading(true)
|
||||
setErrors([])
|
||||
|
||||
setSubmissionState("submitting")
|
||||
|
||||
try {
|
||||
await props.onSubmit(formData)
|
||||
setLoading(false)
|
||||
setSubmissionState("success")
|
||||
clearFormData()
|
||||
|
||||
// Show success message briefly before redirect
|
||||
setTimeout(() => {
|
||||
// The redirect will be handled by the parent component
|
||||
}, 1500)
|
||||
}
|
||||
catch (error) {
|
||||
setSubmissionState("error")
|
||||
|
||||
// Handle different types of errors
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes("auth") || error.message.includes("token")) {
|
||||
setErrors([{ message: "登录状态已过期,请重新登录", type: "auth" }])
|
||||
}
|
||||
else if (error.message.includes("network") || error.message.includes("fetch")) {
|
||||
setErrors([{ message: "网络连接失败,请检查网络后重试", type: "network" }])
|
||||
}
|
||||
else {
|
||||
setErrors([{ message: "提交失败,请稍后重试", type: "server" }])
|
||||
}
|
||||
}
|
||||
else {
|
||||
setErrors([{ message: "提交失败,请稍后重试", type: "server" }])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleRetry = async () => {
|
||||
setSubmissionState("retrying")
|
||||
setErrors([])
|
||||
|
||||
try {
|
||||
await props.onSubmit(formData)
|
||||
setSubmissionState("success")
|
||||
clearFormData()
|
||||
}
|
||||
catch (error) {
|
||||
setSubmissionState("error")
|
||||
setErrors([{ message: "重试失败,请稍后再试", type: "server" }])
|
||||
console.error("Retry submission error:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const onLogin = async () => {
|
||||
|
|
@ -66,6 +147,54 @@ function TicketForm(props: {
|
|||
</div>
|
||||
</div>
|
||||
<div className="section-content w-full">
|
||||
{/* Error Messages */}
|
||||
{errors.length > 0 && (
|
||||
<div className="mb-4 space-y-2">
|
||||
{errors.map((error, index) => (
|
||||
<Alert
|
||||
key={index}
|
||||
color={error.type === "validation" ? "warning" : "danger"}
|
||||
className="flex items-center justify-between"
|
||||
endContent={
|
||||
error.type === "network" || error.type === "server"
|
||||
? (
|
||||
<Button
|
||||
size="sm"
|
||||
color="danger"
|
||||
variant="flat"
|
||||
onPress={handleRetry}
|
||||
isLoading={submissionState === "retrying"}
|
||||
>
|
||||
重试
|
||||
</Button>
|
||||
)
|
||||
: error.type === "auth"
|
||||
? (
|
||||
<Button
|
||||
size="sm"
|
||||
color="warning"
|
||||
variant="flat"
|
||||
onPress={onLogin}
|
||||
>
|
||||
重新登录
|
||||
</Button>
|
||||
)
|
||||
: null
|
||||
}
|
||||
>
|
||||
{error.message}
|
||||
</Alert>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Success Message */}
|
||||
{submissionState === "success" && (
|
||||
<Alert color="success" className="mb-4">
|
||||
提交成功!正在跳转到详情页面...
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Form
|
||||
className="w-full flex flex-col gap-4"
|
||||
onSubmit={onSubmit}
|
||||
|
|
@ -130,7 +259,7 @@ function TicketForm(props: {
|
|||
setFormData({ ...formData, qq: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<Button type="submit" color="primary" className="my-4 w-full" isLoading={loading}>
|
||||
<Button type="submit" color="primary" className="my-4 w-full" isLoading={submissionState == "submitting"}>
|
||||
提交
|
||||
</Button>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { saturdayClient } from "../../utils/client"
|
|||
import { makeLogtoClient } from "../../utils/auth"
|
||||
import RepairHistoryCard from "./RepairHistoryCard"
|
||||
import type { components } from "../../types/saturday"
|
||||
import { LogtoError } from "@logto/browser"
|
||||
import { LogtoRequestError } from "@logto/browser"
|
||||
|
||||
type PublicEvent = components["schemas"]["PublicEvent"]
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export default function UserRepairHistory({ onCreateNew, onLogin }: UserRepairHi
|
|||
setTotalPages(Math.ceil(totalItems / itemsPerPage))
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof LogtoError) {
|
||||
if (err instanceof LogtoRequestError) {
|
||||
onLogin()
|
||||
}
|
||||
console.error("Error fetching user events:", err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue