Merge pull request #104 from wen-templari/use-openapi-fetch

Migrate to openapi-fetch for API client
This commit is contained in:
clas 2025-09-29 22:56:18 +08:00 committed by GitHub
commit b0969760b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1589 additions and 212 deletions

View file

@ -1,7 +1,7 @@
import type { UserInfoResponse } from "@logto/browser" import type { UserInfoResponse } from "@logto/browser"
import type { PublicMember } from "../../store/member" import type { PublicMember } from "../../store/member"
import { EventStatus, type PublicEvent } from "../../types/event" import { EventStatus, type PublicEvent, type RepairEvent } from "../../types/event"
import { saturdayApiBaseUrl } from "../../utils/client" import { saturdayClient } from "../../utils/client"
import { Button, Form, Select, SelectItem, Textarea } from "@heroui/react" import { Button, Form, Select, SelectItem, Textarea } from "@heroui/react"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
@ -20,7 +20,7 @@ export type EventActionProps = {
event: PublicEvent event: PublicEvent
identityContext: IdentityContext identityContext: IdentityContext
isLoading?: string isLoading?: string
onUpdated: (event: PublicEvent) => void onUpdated: (event: RepairEvent) => void
onLoading: (loadingAction?: string) => void onLoading: (loadingAction?: string) => void
} }
@ -104,19 +104,22 @@ export const EventActionCommit = (props: EventActionProps) => {
const onSubmit = async () => { const onSubmit = async () => {
props.onLoading("commit") props.onLoading("commit")
const res = await fetch(`${saturdayApiBaseUrl}/member/events/${props.event.eventId}/commit`, { const { data } = await saturdayClient.POST("/member/events/{EventId}/commit", {
method: "POST", params: {
headers: { header: {
Authorization: `Bearer ${props.identityContext.token}`, Authorization: `Bearer ${props.identityContext.token}`,
ContentType: "application/json",
}, },
body: JSON.stringify({ path: {
EventId: props.event.eventId,
},
},
body: {
size: formData.size, size: formData.size,
content: formData.description, content: formData.description,
}), },
}).then(res => res.json()) })
props.onLoading() props.onLoading()
return props.onUpdated(res) return props.onUpdated(data)
} }
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
@ -151,19 +154,22 @@ export const EventActionAlterCommit = (props: EventActionProps) => {
const onSubmit = async () => { const onSubmit = async () => {
props.onLoading("alterCommit") props.onLoading("alterCommit")
const res = await fetch(`${saturdayApiBaseUrl}/member/events/${props.event.eventId}/commit`, { const { data } = await saturdayClient.PATCH("/member/events/{EventId}/commit", {
method: "PATCH", params: {
headers: { header: {
Authorization: `Bearer ${props.identityContext.token}`, Authorization: `Bearer ${props.identityContext.token}`,
ContentType: "application/json",
}, },
body: JSON.stringify({ path: {
EventId: props.event.eventId,
},
},
body: {
size: formData.size, size: formData.size,
content: formData.description, content: formData.description,
}), },
}).then(res => res.json()) })
props.onLoading() props.onLoading()
return props.onUpdated(res) return props.onUpdated(data)
} }
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
@ -206,7 +212,7 @@ export const getAvailableEventActions = (event: PublicEvent, identityContext: Id
props.onLoading(action.action) props.onLoading(action.action)
if (action.handler) { if (action.handler) {
const res = await action.handler() const res = await action.handler()
props.onUpdated(res as PublicEvent) props.onUpdated(res as RepairEvent)
} }
props.onLoading() props.onLoading()
} }
@ -235,12 +241,17 @@ export const getAvailableEventActions = (event: PublicEvent, identityContext: Id
variant: "solid", variant: "solid",
color: "primary", color: "primary",
handler: async () => { handler: async () => {
return await fetch(`${saturdayApiBaseUrl}/member/events/${event.eventId}/accept`, { const { data } = await saturdayClient.POST("/member/events/{EventId}/accept", {
method: "POST", params: {
headers: { header: {
Authorization: `Bearer ${identityContext.token}`, Authorization: `Bearer ${identityContext.token}`,
}, },
}).then(res => res.json()) path: {
EventId: event.eventId,
},
},
})
return data
}, },
}), }),
}) })
@ -256,12 +267,17 @@ export const getAvailableEventActions = (event: PublicEvent, identityContext: Id
action: "drop", action: "drop",
label: "放弃", label: "放弃",
handler: async () => { handler: async () => {
return await fetch(`${saturdayApiBaseUrl}/member/events/${event.eventId}/accept`, { const { data } = await saturdayClient.POST("/member/events/{EventId}/accept", {
method: "DELETE", params: {
headers: { header: {
Authorization: `Bearer ${identityContext.token}`, Authorization: `Bearer ${identityContext.token}`,
}, },
}).then(res => res.json()) path: {
EventId: event.eventId,
},
},
})
return data
}, },
}), }),
}) })
@ -281,12 +297,17 @@ export const getAvailableEventActions = (event: PublicEvent, identityContext: Id
color: "success", color: "success",
label: "完成", label: "完成",
handler: async () => { handler: async () => {
return await fetch(`${saturdayApiBaseUrl}/events/${event.eventId}/close`, { const { data } = await saturdayClient.POST("/events/{EventId}/close", {
method: "POST", params: {
headers: { header: {
Authorization: `Bearer ${identityContext.token}`, Authorization: `Bearer ${identityContext.token}`,
}, },
}).then(res => res.json()) path: {
EventId: event.eventId,
},
},
})
return data
}, },
}), }),
}) })
@ -297,12 +318,17 @@ export const getAvailableEventActions = (event: PublicEvent, identityContext: Id
color: "danger", color: "danger",
label: "退回", label: "退回",
handler: async () => { handler: async () => {
return await fetch(`${saturdayApiBaseUrl}/events/${event.eventId}/commit`, { const { data } = await saturdayClient.DELETE("/events/{EventId}/commit", {
method: "DELETE", params: {
headers: { header: {
Authorization: `Bearer ${identityContext.token}`, Authorization: `Bearer ${identityContext.token}`,
}, },
}).then(res => res.json()) path: {
EventId: event.eventId,
},
},
})
return data
}, },
}), }),
}) })

View file

@ -9,7 +9,7 @@ import {
DateRangePicker, DateRangePicker,
} from "@heroui/react" } from "@heroui/react"
import { parseDate } from "@internationalized/date" import { parseDate } from "@internationalized/date"
import { saturdayApiBaseUrl } from "../../utils/client" import { saturdayClient } from "../../utils/client"
import { makeLogtoClient } from "../../utils/auth" import { makeLogtoClient } from "../../utils/auth"
import dayjs from "dayjs" import dayjs from "dayjs"
@ -29,15 +29,18 @@ export function ExportExcelModal() {
setLoading(true) setLoading(true)
try { try {
const start = dateRange.start.toString() // Format: 'YYYY-MM-DD'
const end = dateRange.end.toString()
const url = `${saturdayApiBaseUrl}/events/xlsx?start_time=${start}&end_time=${end}`
const token = await makeLogtoClient().getAccessToken() const token = await makeLogtoClient().getAccessToken()
const response = await fetch(url, { const { response } = await saturdayClient.GET("/events/xlsx", {
headers: { params: {
header: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
query: {
start_time: dateRange.start.toString(),
end_time: dateRange.end.toString(),
},
},
parseAs: "stream",
}) })
if (!response.ok) throw new Error("Download failed") if (!response.ok) throw new Error("Download failed")

View file

@ -25,10 +25,10 @@ import {
import { useCallback, useEffect, useMemo, useRef, useState } from "react" import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { useAsyncList } from "@react-stately/data" import { useAsyncList } from "@react-stately/data"
import type { components } from "../../types/saturday" import type { components } from "../../types/saturday"
import { saturdayApiBaseUrl, saturdayClient } from "../../utils/client" import { saturdayClient } from "../../utils/client"
import EventDetail, { EventStatusChip, type EventDetailRef } from "./EventDetail" import EventDetail, { EventStatusChip, type EventDetailRef } from "./EventDetail"
import dayjs from "dayjs" import dayjs from "dayjs"
import { EventStatus, UserEventStatus } from "../../types/event" import { EventStatus, UserEventStatus, type RepairEvent } from "../../types/event"
import { makeLogtoClient } from "../../utils/auth" import { makeLogtoClient } from "../../utils/auth"
import type { PublicMember } from "../../store/member" import type { PublicMember } from "../../store/member"
import type { UserInfoResponse } from "@logto/browser" import type { UserInfoResponse } from "@logto/browser"
@ -107,7 +107,7 @@ function TicketDetailDrawer(props: {
event: PublicEvent event: PublicEvent
identity: IdentityContext identity: IdentityContext
isOpen: boolean isOpen: boolean
onEventUpdated: (event: PublicEvent) => void onEventUpdated: (event: RepairEvent) => void
onOpenChange: (isOpen: boolean) => void onOpenChange: (isOpen: boolean) => void
onClose: () => void onClose: () => void
onDelete: () => void onDelete: () => void
@ -127,7 +127,7 @@ function TicketDetailDrawer(props: {
setAvailableActions(getAvailableEventActions(props.event, props.identity)) setAvailableActions(getAvailableEventActions(props.event, props.identity))
}, [props.event, props.identity]) }, [props.event, props.identity])
const onEventUpdated = async (event: PublicEvent) => { const onEventUpdated = async (event: RepairEvent) => {
props.onEventUpdated(event) props.onEventUpdated(event)
const res = await eventDetailRef.current?.refresh() const res = await eventDetailRef.current?.refresh()
console.log("onEventUpdated", res) console.log("onEventUpdated", res)
@ -216,13 +216,14 @@ export default function App() {
} }
setUserInfo(res) setUserInfo(res)
const currentMember = await fetch(`${saturdayApiBaseUrl}/member`, { const { data } = await saturdayClient.GET("/member", {
method: "GET", params: {
headers: { header: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}).then(res => res.json()) },
setCurrentMember(currentMember) })
setCurrentMember(data)
} }
check() check()
}, []) }, [])

View file

@ -157,7 +157,7 @@ function TicketFormCreated(props: {
}) })
return ( return (
<section className="box-border w-full mt-8"> <section className="box-border w-full mt-8">
<div className="section-content"> <div className="section-content mb-4">
<Alert hideIcon color="success" variant="faded"> <Alert hideIcon color="success" variant="faded">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="h-28 lg:h-40 aspect-square"> <div className="h-28 lg:h-40 aspect-square">
@ -181,8 +181,10 @@ function TicketFormCreated(props: {
</div> </div>
</Alert> </Alert>
</div> </div>
<div className="section-content"> <div className="section-content mb-4">
<EventDetail eventId={props.event?.eventId}></EventDetail> <EventDetail eventId={props.event?.eventId}>
{() => <></>}
</EventDetail>
</div> </div>
</section> </section>
) )
@ -204,23 +206,55 @@ export default function App() {
setUserInfo(res) setUserInfo(res)
} }
check() check()
// Check for eventId in URL query parameters
const urlParams = new URLSearchParams(window.location.search)
const eventId = urlParams.get("eventId")
if (eventId) {
// Fetch event data from the eventId
const fetchEvent = async () => {
try {
const { data } = await saturdayClient.GET("/events/{EventId}", {
params: {
path: {
EventId: parseInt(eventId, 10),
},
},
})
if (data) {
setEvent(data as PublicEvent)
}
}
catch (error) {
console.log("Error fetching event:", error)
}
}
fetchEvent()
}
}, []) }, [])
const onSubmit = async (formData: TicketFormData) => { const onSubmit = async (formData: TicketFormData) => {
const logtoToken = await makeLogtoClient().getAccessToken() const logtoToken = await makeLogtoClient().getAccessToken()
try { try {
const res = await saturdayClient.POST("/client/event", { const { data } = await saturdayClient.POST("/client/event", {
headers: { headers: {
Authorization: `Bearer ${logtoToken}`, Authorization: `Bearer ${logtoToken}`,
}, },
body: { body: {
Problem: formData.description, problem: formData.description,
model: formData.model, model: formData.model,
phone: formData.phone, phone: formData.phone,
qq: formData.qq, qq: formData.qq,
}, },
}) })
setEvent(res.data as unknown as PublicEvent) setEvent(data as unknown as PublicEvent)
// Update URL with eventId to persist the ticket status
if (data?.eventId) {
const currentUrl = new URL(window.location.href)
currentUrl.searchParams.set("eventId", data.eventId.toString())
window.history.pushState({}, "", currentUrl.toString())
}
} }
catch (error) { catch (error) {
console.log(error) console.log(error)

View file

@ -80,3 +80,4 @@ export const UserEventAction: Action[] = [
] ]
export type PublicEvent = components["schemas"]["PublicEvent"] export type PublicEvent = components["schemas"]["PublicEvent"]
export type RepairEvent = components["schemas"]["Event"]

1458
src/types/saturday.d.ts vendored

File diff suppressed because it is too large Load diff