import { useEffect, useState } from "react"
import { makeLogtoClient } from "../../utils/auth"
import type { UserInfoResponse } from "@logto/browser"
import { Alert, Form, Input, Button, Textarea } from "@heroui/react"
import { saturdayClient } from "../../utils/client"
type TicketFormData = {
model?: string
phone?: string
qq?: string
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
}) {
return (
)
}
function TicketForm(props: {
userInfo?: UserInfoResponse
onSubmit: (form: TicketFormData) => Promise
}) {
const [submissionState, setSubmissionState] = useState("idle")
const [formData, setFormData] = useState({})
const [errors, setErrors] = useState([])
// 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) => {
e.preventDefault()
setErrors([])
setSubmissionState("submitting")
try {
await props.onSubmit(formData)
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 () => {
makeLogtoClient().signIn({
redirectUri: import.meta.env.PUBLIC_LOGTO_CALLBACK_URL,
postRedirectUri: window.location.pathname,
})
}
return (
{
props.userInfo?.sub
? <>>
:
}
预约维修
在你填写表单之后,我们会尽快联系你,确认维修时间和地点。
{/* Error Messages */}
{errors.length > 0 && (
{errors.map((error, index) => (
重试
)
: error.type === "auth"
? (
)
: null
}
>
{error.message}
))}
)}
{/* Success Message */}
{submissionState === "success" && (
提交成功!正在跳转到详情页面...
)}
)
}
export default function App() {
const [userInfo, setUserInfo] = useState()
useEffect(() => {
const check = async () => {
const createRepairPath = "/repair/create-ticket"
try {
const authenticated = await makeLogtoClient().isAuthenticated()
if (!authenticated) {
window.location.href = `/repair/login-hint?redirectUrl=${createRepairPath}`
return
}
const res = await makeLogtoClient().getIdTokenClaims()
setUserInfo(res)
}
catch (error) {
console.error("Error checking authentication:", error)
window.location.href = `/repair/login-hint?redirectUrl=${createRepairPath}`
}
}
check()
}, [])
const onSubmit = async (formData: TicketFormData) => {
const logtoToken = await makeLogtoClient().getAccessToken()
try {
const { data } = await saturdayClient.POST("/client/event", {
headers: {
Authorization: `Bearer ${logtoToken}`,
},
body: {
problem: formData.description,
model: formData.model,
phone: formData.phone,
qq: formData.qq,
},
})
// Update URL with eventId to persist the ticket status
if (data?.eventId) {
window.location.href = `/repair/ticket-detail?eventId=${data.eventId}`
}
}
catch (error) {
console.log(error)
}
}
return (
)
}