FUJI/src/pages/repair/EventAction.tsx
2025-09-25 23:18:33 +08:00

339 lines
9.4 KiB
TypeScript

import type { UserInfoResponse } from "@logto/browser"
import type { PublicMember } from "../../store/member"
import { EventStatus, type PublicEvent, type RepairEvent } from "../../types/event"
import { saturdayClient } from "../../utils/client"
import { Button, Form, Select, SelectItem, Textarea } from "@heroui/react"
import { useEffect, useState } from "react"
export type IdentityContext = {
member: PublicMember
userInfo: UserInfoResponse
token: string
}
enum RepairRole {
repairAdmin = "repair admin",
repairMember = "repair member",
}
export type EventActionProps = {
event: PublicEvent
identityContext: IdentityContext
isLoading?: string
onUpdated: (event: RepairEvent) => void
onLoading: (loadingAction?: string) => void
}
const EventSizeOptions: {
size: string
description?: string
}[] = [
{ size: "xs", description: "无需工具,仅简单排查或软件层级操作" },
{ size: "s", description: "简单拆装部件,操作快,风险低" },
{ size: "m", description: "需基本工具、一定技术判断,时间较长" },
{ size: "l", description: "较复杂的拆装和测试流程,需熟练技能、多人协作可能" },
{ size: "xl", description: "工作量极大,涉及多个设备,需团队作业和详细记录" },
]
const EventActionCommitForm = (props: {
formData: {
size: string
description: string
}
setFormData: (data: {
size: string
description: string
}) => void
}) => {
const { formData, setFormData } = props
return (
<Form>
<Select
items={EventSizeOptions}
label="维修难度"
size="sm"
selectedKeys={formData.size ? [formData.size] : []}
onChange={(value) => {
setFormData({ ...formData, size: value.target.value.split(",")[0] })
}}
placeholder="请选择维修难度"
>
{
size => (
<SelectItem key={size.size} textValue={"size:" + size.size}>
<div className="flex gap-2 items-center">
<div className="flex flex-col">
<span className="text-small">{size.size}</span>
<span className="text-tiny text-default-400">{size.description}</span>
</div>
</div>
</SelectItem>
)
}
</Select>
<Textarea
label="维修描述"
placeholder="请输入维修描述"
errorMessage="维修描述不能为空"
required
name="description"
value={formData.description || ""}
onChange={(e) => {
setFormData({ ...formData, description: e.target.value })
}}
isRequired
rows={3}
/>
</Form>
)
}
export const EventActionCommit = (props: EventActionProps) => {
const [formData, setFormData] = useState({
size: "",
description: "",
})
useEffect(() => {
const description = props.event?.logs.findLast(v => v.action == "commit" || v.action == "alterCommit")?.description
setFormData({
size: props.event.size || "",
description: description || "",
})
}, [props.event])
const onSubmit = async () => {
props.onLoading("commit")
const { data } = await saturdayClient.POST("/member/events/{EventId}/commit", {
params: {
header: {
Authorization: `Bearer ${props.identityContext.token}`,
},
path: {
EventId: props.event.eventId,
},
},
body: {
size: formData.size,
content: formData.description,
},
})
props.onLoading()
return props.onUpdated(data)
}
return (
<div className="flex flex-col gap-4">
<EventActionCommitForm
formData={formData}
setFormData={setFormData}
>
</EventActionCommitForm>
<Button
variant="flat"
color="primary"
isLoading={props.isLoading === "commit"}
onPress={onSubmit}
>
</Button>
</div>
)
}
export const EventActionAlterCommit = (props: EventActionProps) => {
const [formData, setFormData] = useState({
size: "",
description: "",
})
useEffect(() => {
const description = props.event?.logs?.findLast(v => v.action == "commit" || v.action == "alterCommit")?.description
setFormData({
size: props.event.size || "",
description: description || "",
})
}, [props.event])
const onSubmit = async () => {
props.onLoading("alterCommit")
const { data } = await saturdayClient.PATCH("/member/events/{EventId}/commit", {
params: {
header: {
Authorization: `Bearer ${props.identityContext.token}`,
},
path: {
EventId: props.event.eventId,
},
},
body: {
size: formData.size,
content: formData.description,
},
})
props.onLoading()
return props.onUpdated(data)
}
return (
<div className="flex flex-col gap-4">
<EventActionCommitForm
formData={formData}
setFormData={setFormData}
>
</EventActionCommitForm>
<Button
variant="flat"
isLoading={props.isLoading === "commit"}
onPress={onSubmit}
>
</Button>
</div>
)
}
type CommonHandler = () => Promise<unknown>
type JsxHandler = (props: EventActionProps) => JSX.Element
export type EventAction = {
action: string
label?: string
color?: "default" | "primary" | "secondary" | "success" | "warning" | "danger"
variant?: "flat" | "solid" | "bordered" | "light" | "faded" | "shadow" | "ghost"
handler?: CommonHandler
jsxHandler: JsxHandler
}
export const getAvailableEventActions = (event: PublicEvent, identityContext: IdentityContext) => {
console.log("getting event actions", event, identityContext)
const actions: EventAction[] = []
const makeCommonJsxHandler = (action: Omit<EventAction, "jsxHandler">) => {
return (props: EventActionProps) => {
const onAction = async (action: {
action: string
handler?: CommonHandler
}) => {
props.onLoading(action.action)
if (action.handler) {
const res = await action.handler()
props.onUpdated(res as RepairEvent)
}
props.onLoading()
}
return (
<div className="flex flex-col">
<Button
isLoading={props.isLoading === action.action}
isDisabled={props.isLoading}
color={action.color || "default"}
variant={action.variant || "flat"}
onPress={() => onAction(action)}
>
{action.label ?? action.action}
</Button>
</div>
)
}
}
if (event.status == EventStatus.open) {
actions.push({
action: "accept",
jsxHandler: makeCommonJsxHandler({
action: "accept",
label: "接受",
variant: "solid",
color: "primary",
handler: async () => {
const { data } = await saturdayClient.POST("/member/events/{EventId}/accept", {
params: {
header: {
Authorization: `Bearer ${identityContext.token}`,
},
path: {
EventId: event.eventId,
},
},
})
return data
},
}),
})
}
else if (event.status == EventStatus.accepted && event.member?.memberId == identityContext.member.memberId) {
actions.push({
action: "commit",
jsxHandler: EventActionCommit,
})
actions.push({
action: "drop",
jsxHandler: makeCommonJsxHandler({
action: "drop",
label: "放弃",
handler: async () => {
const { data } = await saturdayClient.POST("/member/events/{EventId}/accept", {
params: {
header: {
Authorization: `Bearer ${identityContext.token}`,
},
path: {
EventId: event.eventId,
},
},
})
return data
},
}),
})
}
else if (event.status == EventStatus.committed) {
if (event.member?.memberId == identityContext.member.memberId) {
actions.push({
action: "alterCommit",
jsxHandler: EventActionAlterCommit,
})
}
if (identityContext.userInfo.roles.find(role => role.toLocaleLowerCase() == RepairRole.repairAdmin)) {
actions.push({
action: "close",
jsxHandler: makeCommonJsxHandler({
action: "close",
color: "success",
label: "完成",
handler: async () => {
const { data } = await saturdayClient.POST("/events/{EventId}/close", {
params: {
header: {
Authorization: `Bearer ${identityContext.token}`,
},
path: {
EventId: event.eventId,
},
},
})
return data
},
}),
})
actions.push({
action: "reject",
jsxHandler: makeCommonJsxHandler({
action: "rejectCommit",
color: "danger",
label: "退回",
handler: async () => {
const { data } = await saturdayClient.DELETE("/events/{EventId}/commit", {
params: {
header: {
Authorization: `Bearer ${identityContext.token}`,
},
path: {
EventId: event.eventId,
},
},
})
return data
},
}),
})
}
}
return actions
}