mirror of
https://github.com/m1ngsama/FUJI.git
synced 2025-12-24 10:51:27 +00:00
add event actions
This commit is contained in:
parent
bd38b8ed2b
commit
33d93d0347
5 changed files with 487 additions and 203 deletions
288
src/pages/repair/EventAction.tsx
Normal file
288
src/pages/repair/EventAction.tsx
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
import type { UserInfoResponse } from "@logto/browser"
|
||||
import type { PublicMember } from "../../store/member"
|
||||
import { EventStatus, type PublicEvent } from "../../types/event"
|
||||
import { saturdayApiBaseUrl } from "../../utils/client"
|
||||
import { Button, Form, Select, SelectItem, Textarea } from "@heroui/react"
|
||||
import { 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: PublicEvent) => 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"
|
||||
value={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: "",
|
||||
})
|
||||
|
||||
const onSubmit = async () => {
|
||||
props.onLoading("commit")
|
||||
const res = await fetch(`${saturdayApiBaseUrl}/member/events/${props.event.eventId}/commit`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${props.identityContext.token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
size: formData.size,
|
||||
problem: formData.description,
|
||||
}),
|
||||
}).then(res => res.json())
|
||||
props.onLoading()
|
||||
return props.onUpdated(res)
|
||||
}
|
||||
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>
|
||||
)
|
||||
}
|
||||
export const EventActionAlterCommit = (props: EventActionProps) => {
|
||||
const [formData, setFormData] = useState({
|
||||
size: props.event.size,
|
||||
description: props.event.problem,
|
||||
})
|
||||
|
||||
const onSubmit = async () => {
|
||||
props.onLoading("alterCommit")
|
||||
const res = await fetch(`${saturdayApiBaseUrl}/member/events/${props.event.eventId}/commit`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
Authorization: `Bearer ${props.identityContext.token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
size: formData.size,
|
||||
problem: formData.description,
|
||||
}),
|
||||
}).then(res => res.json())
|
||||
props.onLoading()
|
||||
return props.onUpdated(res)
|
||||
}
|
||||
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
|
||||
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 PublicEvent)
|
||||
}
|
||||
props.onLoading()
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<Button
|
||||
variant="flat"
|
||||
isLoading={props.isLoading === action.action}
|
||||
isDisabled={props.isLoading}
|
||||
onPress={() => onAction(action)}
|
||||
>
|
||||
{action.label ?? action.action}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (event.status == EventStatus.open) {
|
||||
actions.push({
|
||||
action: "accept",
|
||||
jsxHandler: makeCommonJsxHandler({
|
||||
action: "accept",
|
||||
label: "接受",
|
||||
handler: async () => {
|
||||
return await fetch(`${saturdayApiBaseUrl}/member/events/${event.eventId}/accept`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${identityContext.token}`,
|
||||
},
|
||||
}).then(res => res.json())
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
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 () => {
|
||||
return await fetch(`${saturdayApiBaseUrl}/member/events/${event.eventId}/accept`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: `Bearer ${identityContext.token}`,
|
||||
},
|
||||
}).then(res => res.json())
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
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",
|
||||
label: "关闭",
|
||||
handler: async () => {
|
||||
return await fetch(`${saturdayApiBaseUrl}/events/${event.eventId}/close`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${identityContext.token}`,
|
||||
},
|
||||
}).then(res => res.json())
|
||||
},
|
||||
}),
|
||||
})
|
||||
actions.push({
|
||||
action: "reject",
|
||||
jsxHandler: makeCommonJsxHandler({
|
||||
action: "rejectCommit",
|
||||
label: "退回",
|
||||
handler: async () => {
|
||||
return await fetch(`${saturdayApiBaseUrl}/events/${event.eventId}/commit`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: `Bearer ${identityContext.token}`,
|
||||
},
|
||||
}).then(res => res.json())
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from "react"
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react"
|
||||
import type { components } from "../../types/saturday"
|
||||
import { saturdayClient } from "../../utils/client"
|
||||
import { Textarea, Input, Chip } from "@heroui/react"
|
||||
|
|
@ -15,8 +15,6 @@ function EventLogItem(props: {
|
|||
}) {
|
||||
return (
|
||||
<div className="py-1 flex items-center">
|
||||
{/* <div className="mr-4 h-10 bg-red-400 flex flex-col items-center gap-2">
|
||||
</div> */}
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4">
|
||||
|
|
@ -55,9 +53,9 @@ export function EventStatusChip(props: {
|
|||
case EventStatus.open:
|
||||
return <Chip size={size}>未开始</Chip>
|
||||
case EventStatus.accepted:
|
||||
return <Chip size={size}>维修中</Chip>
|
||||
case EventStatus.committed:
|
||||
return <Chip size={size} color="primary">维修中</Chip>
|
||||
case EventStatus.committed:
|
||||
return <Chip size={size} color="secondary">待审核</Chip>
|
||||
case EventStatus.closed:
|
||||
return <Chip size={size} color="success">已完成</Chip>
|
||||
case EventStatus.cancelled:
|
||||
|
|
@ -95,11 +93,16 @@ const filterEventLog = (event: PublicEvent) => {
|
|||
}
|
||||
return filteredLogs
|
||||
}
|
||||
|
||||
export default function EventDetail(props: {
|
||||
export type EventDetailRef = {
|
||||
refresh: () => Promise<PublicEvent | undefined>
|
||||
}
|
||||
const EventDetail = forwardRef<EventDetailRef, {
|
||||
eventId?: number
|
||||
}) {
|
||||
onRefresh?: () => void
|
||||
action?: React.ReactNode
|
||||
}>((props, ref) => {
|
||||
const [event, setEvent] = useState<PublicEvent | undefined>()
|
||||
|
||||
const fetchAndSetEvent = async (eventId: number) => {
|
||||
const { data } = await saturdayClient.GET("/events/{EventId}", {
|
||||
params: {
|
||||
|
|
@ -109,21 +112,33 @@ export default function EventDetail(props: {
|
|||
},
|
||||
})
|
||||
setEvent(data)
|
||||
return data
|
||||
}
|
||||
useEffect(() => {
|
||||
|
||||
const refresh = async () => {
|
||||
const url = new URL(window.location.href)
|
||||
const eventId = props.eventId ?? url.searchParams.get("eventId")
|
||||
if (!eventId) {
|
||||
return
|
||||
console.log("refresh eventId", eventId)
|
||||
if (eventId) {
|
||||
return await fetchAndSetEvent(eventId as unknown as number)
|
||||
}
|
||||
fetchAndSetEvent(eventId as unknown as number)
|
||||
}
|
||||
|
||||
// 初次加载
|
||||
useEffect(() => {
|
||||
refresh()
|
||||
}, [])
|
||||
|
||||
// 暴露给父组件的方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
refresh,
|
||||
}))
|
||||
|
||||
return (
|
||||
event
|
||||
? (
|
||||
<section className="box-border mb-24">
|
||||
<div className="mt-8">
|
||||
<section className="box-border">
|
||||
<div className="">
|
||||
<h2 className="text-2xl font-bold">维修详情</h2>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>
|
||||
|
|
@ -132,7 +147,7 @@ export default function EventDetail(props: {
|
|||
<EventStatusChip status={event.status}></EventStatusChip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-8 flex flex-col gap-4">
|
||||
<div className="my-6 flex flex-col gap-4">
|
||||
<Textarea
|
||||
label="问题描述"
|
||||
readOnly
|
||||
|
|
@ -163,4 +178,6 @@ export default function EventDetail(props: {
|
|||
)
|
||||
: <div></div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default EventDetail
|
||||
|
|
|
|||
|
|
@ -20,18 +20,21 @@ import {
|
|||
DrawerBody,
|
||||
DrawerFooter,
|
||||
useDisclosure,
|
||||
Chip,
|
||||
} from "@heroui/react"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useAsyncList } from "@react-stately/data"
|
||||
import type { components } from "../../types/saturday"
|
||||
import { saturdayClient } from "../../utils/client"
|
||||
import EventDetail, { EventStatusChip } from "./EventDetail"
|
||||
import { saturdayApiBaseUrl, saturdayClient } from "../../utils/client"
|
||||
import EventDetail, { EventStatusChip, type EventDetailRef } from "./EventDetail"
|
||||
import dayjs from "dayjs"
|
||||
import { UserEventStatus } from "../../types/event"
|
||||
import { makeLogtoClient } from "../../utils/auth"
|
||||
import type { PublicMember } from "../../store/member"
|
||||
import type { UserInfoResponse } from "@logto/browser"
|
||||
import { getAvailableEventActions, type EventAction, type IdentityContext } from "./EventAction"
|
||||
|
||||
type PublicEvent = components["schemas"]["PublicEvent"]
|
||||
// type EventLog = components["schemas"]["EventLog"]
|
||||
|
||||
export const EyeIcon = (props) => {
|
||||
return (
|
||||
|
|
@ -63,106 +66,10 @@ export const EyeIcon = (props) => {
|
|||
)
|
||||
}
|
||||
|
||||
export const DeleteIcon = (props) => {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 20 20"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M17.5 4.98332C14.725 4.70832 11.9333 4.56665 9.15 4.56665C7.5 4.56665 5.85 4.64998 4.2 4.81665L2.5 4.98332"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d="M7.08331 4.14169L7.26665 3.05002C7.39998 2.25835 7.49998 1.66669 8.90831 1.66669H11.0916C12.5 1.66669 12.6083 2.29169 12.7333 3.05835L12.9166 4.14169"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d="M15.7084 7.61664L15.1667 16.0083C15.075 17.3166 15 18.3333 12.675 18.3333H7.32502C5.00002 18.3333 4.92502 17.3166 4.83335 16.0083L4.29169 7.61664"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d="M8.60834 13.75H11.3833"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d="M7.91669 10.4167H12.0834"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const EditIcon = (props) => {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 20 20"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M11.05 3.00002L4.20835 10.2417C3.95002 10.5167 3.70002 11.0584 3.65002 11.4334L3.34169 14.1334C3.23335 15.1084 3.93335 15.775 4.90002 15.6084L7.58335 15.15C7.95835 15.0834 8.48335 14.8084 8.74168 14.525L15.5834 7.28335C16.7667 6.03335 17.3 4.60835 15.4583 2.86668C13.625 1.14168 12.2334 1.75002 11.05 3.00002Z"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeMiterlimit={10}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d="M9.90833 4.20831C10.2667 6.50831 12.1333 8.26665 14.45 8.49998"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeMiterlimit={10}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d="M2.5 18.3333H17.5"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeMiterlimit={10}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
function CheckboxPopover(props: {
|
||||
value: string[]
|
||||
onValueChange: (value: string[]) => void
|
||||
}) {
|
||||
// const [selectedValues, setSelectedValues] = useState([])
|
||||
|
||||
// const handleSelectionChange = (values) => {
|
||||
// setSelectedValues(values)
|
||||
// }
|
||||
|
||||
return (
|
||||
<Popover placement="bottom">
|
||||
<PopoverTrigger>
|
||||
|
|
@ -196,23 +103,67 @@ function CheckboxPopover(props: {
|
|||
}
|
||||
|
||||
function TicketDetailDrawer(props: {
|
||||
eventId: number
|
||||
event: PublicEvent
|
||||
identity: IdentityContext
|
||||
isOpen: boolean
|
||||
onEventUpdated: (event: PublicEvent) => void
|
||||
onOpenChange: (isOpen: boolean) => void
|
||||
onClose: () => void
|
||||
onDelete: () => void
|
||||
onEdit: () => void
|
||||
}) {
|
||||
const { isOpen, onOpenChange, onClose } = props
|
||||
const [isLoading, setIsLoading] = useState("")
|
||||
|
||||
const eventDetailRef = useRef<EventDetailRef>(null)
|
||||
|
||||
const [availableActions, setAvailableActions] = useState<EventAction[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.event || !props.identity?.member || !props.identity?.userInfo?.roles) {
|
||||
return
|
||||
}
|
||||
setAvailableActions(getAvailableEventActions(props.event, props.identity))
|
||||
}, [props.event, props.identity])
|
||||
|
||||
const onEventUpdated = async (event: PublicEvent) => {
|
||||
props.onEventUpdated(event)
|
||||
const res = await eventDetailRef.current?.refresh()
|
||||
console.log("onEventUpdated", res)
|
||||
if (!res || !props.identity?.member || !props.identity?.userInfo?.roles) {
|
||||
return
|
||||
}
|
||||
setAvailableActions(getAvailableEventActions(res, props.identity))
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<h2 className="text-2xl font-bold">维修详情</h2>
|
||||
{isLoading}
|
||||
</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<EventDetail eventId={props.eventId}></EventDetail>
|
||||
<EventDetail ref={eventDetailRef} eventId={props.event?.eventId}></EventDetail>
|
||||
<div className="mb-12 flex flex-col gap-2">
|
||||
{
|
||||
availableActions?.map((action) => {
|
||||
return (
|
||||
<action.jsxHandler
|
||||
key={action.action}
|
||||
event={props.event}
|
||||
isLoading={isLoading}
|
||||
identityContext={props.identity}
|
||||
onUpdated={onEventUpdated}
|
||||
onLoading={(action) => {
|
||||
setIsLoading(action)
|
||||
}}
|
||||
>
|
||||
</action.jsxHandler>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</DrawerBody>
|
||||
<DrawerFooter>
|
||||
<Button variant="flat" onPress={onClose}>
|
||||
|
|
@ -232,11 +183,12 @@ export const validateRepairRole = (roles: string[]) => {
|
|||
export default function App() {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [page, setPage] = useState(1)
|
||||
const rowsPerPage = 15
|
||||
// const [events, setEvents] = useState<PublicEvent[]>([])
|
||||
const rowsPerPage = 10
|
||||
const [statusFilter, setStatusFilter] = useState<string[]>([])
|
||||
const { isOpen, onOpen, onOpenChange } = useDisclosure()
|
||||
// const [userInfo, setUserInfo] = useState<UserInfoResponse>()
|
||||
const [userInfo, setUserInfo] = useState<UserInfoResponse>()
|
||||
const [currentMember, setCurrentMember] = useState<PublicMember>()
|
||||
const [token, setToken] = useState<string>()
|
||||
|
||||
useEffect(() => {
|
||||
const check = async () => {
|
||||
|
|
@ -247,27 +199,25 @@ export default function App() {
|
|||
return
|
||||
}
|
||||
const res = await makeLogtoClient().getIdTokenClaims()
|
||||
const token = await makeLogtoClient().getAccessToken()
|
||||
setToken(token)
|
||||
const hasRole = validateRepairRole(res.roles)
|
||||
if (!hasRole) {
|
||||
window.location.href = `/repair/login-hint?redirectUrl=${adminPath}`
|
||||
return
|
||||
}
|
||||
// setUserInfo(res)
|
||||
setUserInfo(res)
|
||||
|
||||
const currentMember = await fetch(`${saturdayApiBaseUrl}/member`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}).then(res => res.json())
|
||||
setCurrentMember(currentMember)
|
||||
}
|
||||
check()
|
||||
}, [])
|
||||
// const fetchAndSetEvent = async () => {
|
||||
// const { data } = await saturdayClient.GET("/events", {
|
||||
// params: {
|
||||
// query: {
|
||||
// order: "DESC",
|
||||
// offset: 1,
|
||||
// limit: 1000,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// setEvents(data)
|
||||
// }
|
||||
|
||||
const list = useAsyncList<PublicEvent>({
|
||||
async load() {
|
||||
|
|
@ -321,11 +271,17 @@ export default function App() {
|
|||
const pages = useMemo(() => {
|
||||
return Math.ceil(filteredList.length / rowsPerPage)
|
||||
}, [filteredList, rowsPerPage])
|
||||
// useEffect(() => {
|
||||
// fetchAndSetEvent()
|
||||
// }, [])
|
||||
|
||||
const columns = [
|
||||
useEffect(() => {
|
||||
setPage(1)
|
||||
}, [statusFilter])
|
||||
|
||||
const columns: {
|
||||
key: string
|
||||
label: string
|
||||
allowSorting?: boolean
|
||||
content?: JSX.Element
|
||||
}[] = [
|
||||
{
|
||||
key: "eventId",
|
||||
label: "单号",
|
||||
|
|
@ -338,6 +294,10 @@ export default function App() {
|
|||
key: "model",
|
||||
label: "型号",
|
||||
},
|
||||
{
|
||||
key: "size",
|
||||
label: "工作量",
|
||||
},
|
||||
{
|
||||
key: "memberId",
|
||||
label: "处理人",
|
||||
|
|
@ -362,13 +322,13 @@ export default function App() {
|
|||
},
|
||||
]
|
||||
|
||||
const [activeEventId, setActiveEventId] = useState<number>()
|
||||
const onOpenEventDetail = (eventId: number) => {
|
||||
setActiveEventId(eventId)
|
||||
const [activeEvent, setActiveEvent] = useState<PublicEvent>()
|
||||
const onOpenEventDetail = (event: PublicEvent) => {
|
||||
setActiveEvent(event)
|
||||
onOpen()
|
||||
}
|
||||
|
||||
const renderCell = useCallback((event: PublicEvent, columnKey: string) => {
|
||||
const renderCell = useCallback((event: PublicEvent, columnKey: string | number) => {
|
||||
const cellValue = event[columnKey]
|
||||
|
||||
switch (columnKey) {
|
||||
|
|
@ -391,6 +351,10 @@ export default function App() {
|
|||
)
|
||||
: <></>
|
||||
)
|
||||
case "size":
|
||||
return (
|
||||
cellValue ? <Chip size="sm">{"size:" + cellValue}</Chip> : <></>
|
||||
)
|
||||
case "gmtCreate":
|
||||
return (
|
||||
<span>
|
||||
|
|
@ -400,10 +364,11 @@ export default function App() {
|
|||
case "status":
|
||||
return EventStatusChip({
|
||||
status: cellValue,
|
||||
size: "sm",
|
||||
})
|
||||
case "actions":
|
||||
return (
|
||||
<Button onPress={() => onOpenEventDetail(event.eventId)} size="sm" isIconOnly variant="light">
|
||||
<Button onPress={() => onOpenEventDetail(event)} size="sm" isIconOnly variant="light">
|
||||
<span className="text-lg text-default-400 cursor-pointer active:opacity-50">
|
||||
<EyeIcon />
|
||||
</span>
|
||||
|
|
@ -417,7 +382,7 @@ export default function App() {
|
|||
|
||||
return (
|
||||
<section className="box-border mb-24">
|
||||
<div className="section-content mt-8">
|
||||
<div className="section-content mt-6">
|
||||
<h2 className="text-2xl font-bold">维修管理</h2>
|
||||
</div>
|
||||
<div className="section-content my-8 flex flex-col gap-4">
|
||||
|
|
@ -452,7 +417,15 @@ export default function App() {
|
|||
</Table>
|
||||
</div>
|
||||
<TicketDetailDrawer
|
||||
eventId={activeEventId}
|
||||
event={activeEvent}
|
||||
onEventUpdated={list.reload}
|
||||
identity={
|
||||
{
|
||||
member: currentMember,
|
||||
userInfo: userInfo,
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
onClose={() => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { components } from "./saturday"
|
||||
|
||||
export interface Status {
|
||||
status: string
|
||||
text: string
|
||||
|
|
@ -76,3 +78,5 @@ export const UserEventAction: Action[] = [
|
|||
icon: "status_cancelled.svg",
|
||||
},
|
||||
]
|
||||
|
||||
export type PublicEvent = components["schemas"]["PublicEvent"]
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ import createClient from "openapi-fetch"
|
|||
import type { paths as saturdayPaths } from "../types/saturday"
|
||||
import { ApiClient } from "./active"
|
||||
|
||||
export const saturdayApiBaseUrl = import.meta.env.PROD ? "https://api.nbtca.space/v2/" : "/saturday"
|
||||
|
||||
export const saturdayClient = createClient<saturdayPaths>({
|
||||
baseUrl: import.meta.env.PROD ? "https://api.nbtca.space/v2/" : "/saturday",
|
||||
baseUrl: saturdayApiBaseUrl,
|
||||
})
|
||||
|
||||
export const activeClient = new ApiClient({
|
||||
|
|
|
|||
Loading…
Reference in a new issue