mirror of
https://github.com/m1ngsama/FUJI.git
synced 2025-12-24 10:51:27 +00:00
save
This commit is contained in:
parent
53d6723dfb
commit
7a33a00ab6
7 changed files with 154 additions and 26 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
|
@ -8,7 +8,10 @@
|
|||
],
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
"cSpell.words": [
|
||||
"ical",
|
||||
"Logto",
|
||||
"qrcode"
|
||||
"qrcode",
|
||||
"Subcomponents",
|
||||
"vevent"
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ export default defineConfig({
|
|||
syntaxHighlight: "shiki",
|
||||
shikiConfig: {
|
||||
themes: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark',
|
||||
light: "github-light",
|
||||
dark: "github-dark",
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
integrations: [
|
||||
vue(),
|
||||
|
|
@ -39,7 +39,10 @@ export default defineConfig({
|
|||
target: "http://localhost:4000",
|
||||
rewrite: path => path.replace(/^\/saturday/, ""),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
// optimizeDeps: {
|
||||
// exclude: ["dayjs"],
|
||||
// },
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@
|
|||
"Rehype",
|
||||
"rehypePlugins",
|
||||
"shiki",
|
||||
"tseslint"
|
||||
"tseslint",
|
||||
"ical",
|
||||
"ICAL",
|
||||
"vevents",
|
||||
"getAllSubcomponents"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -24,12 +24,13 @@
|
|||
"@fullcalendar/react": "^6.1.17",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroui/react": "2.7.6",
|
||||
"@internationalized/date": "^3.8.0",
|
||||
"@logto/browser": "^2.2.18",
|
||||
"@stylistic/eslint-plugin": "^2.13.0",
|
||||
"astro": "^4.16.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"framer-motion": "^11.18.2",
|
||||
"ical.js": "^1.5.0",
|
||||
"ical.js": "^2.1.0",
|
||||
"md5": "^2.3.0",
|
||||
"openapi-fetch": "^0.12.5",
|
||||
"qrcode": "^1.5.4",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ importers:
|
|||
version: 6.1.17(@fullcalendar/core@6.1.17)
|
||||
'@fullcalendar/icalendar':
|
||||
specifier: ^6.1.17
|
||||
version: 6.1.17(@fullcalendar/core@6.1.17)(ical.js@1.5.0)
|
||||
version: 6.1.17(@fullcalendar/core@6.1.17)(ical.js@2.1.0)
|
||||
'@fullcalendar/react':
|
||||
specifier: ^6.1.17
|
||||
version: 6.1.17(@fullcalendar/core@6.1.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -38,6 +38,9 @@ importers:
|
|||
'@heroui/react':
|
||||
specifier: 2.7.6
|
||||
version: 2.7.6(@types/react@18.3.20)(framer-motion@11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.17)
|
||||
'@internationalized/date':
|
||||
specifier: ^3.8.0
|
||||
version: 3.8.0
|
||||
'@logto/browser':
|
||||
specifier: ^2.2.18
|
||||
version: 2.2.18
|
||||
|
|
@ -54,8 +57,8 @@ importers:
|
|||
specifier: ^11.18.2
|
||||
version: 11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
ical.js:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
md5:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
|
|
@ -3748,8 +3751,8 @@ packages:
|
|||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
ical.js@1.5.0:
|
||||
resolution: {integrity: sha512-7ZxMkogUkkaCx810yp0ZGKvq1ZpRgJeornPttpoxe6nYZ3NLesZe1wWMXDdwTkj/b5NtXT+Y16Aakph/ao98ZQ==}
|
||||
ical.js@2.1.0:
|
||||
resolution: {integrity: sha512-BOVfrH55xQ6kpS3muGvIXIg2l7p+eoe12/oS7R5yrO3TL/j/bLsR0PR+tYQESFbyTbvGgPHn9zQ6tI4FWyuSaQ==}
|
||||
|
||||
ignore@5.3.2:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
|
|
@ -6190,10 +6193,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@fullcalendar/core': 6.1.17
|
||||
|
||||
'@fullcalendar/icalendar@6.1.17(@fullcalendar/core@6.1.17)(ical.js@1.5.0)':
|
||||
'@fullcalendar/icalendar@6.1.17(@fullcalendar/core@6.1.17)(ical.js@2.1.0)':
|
||||
dependencies:
|
||||
'@fullcalendar/core': 6.1.17
|
||||
ical.js: 1.5.0
|
||||
ical.js: 2.1.0
|
||||
|
||||
'@fullcalendar/react@6.1.17(@fullcalendar/core@6.1.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
|
|
@ -7509,7 +7512,7 @@ snapshots:
|
|||
|
||||
'@react-aria/calendar@3.7.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.0
|
||||
'@react-aria/i18n': 3.12.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-aria/interactions': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-aria/live-announcer': 3.4.2
|
||||
|
|
@ -7561,7 +7564,7 @@ snapshots:
|
|||
|
||||
'@react-aria/datepicker@3.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.0
|
||||
'@internationalized/number': 3.6.1
|
||||
'@internationalized/string': 3.2.6
|
||||
'@react-aria/focus': 3.20.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -8102,7 +8105,7 @@ snapshots:
|
|||
|
||||
'@react-stately/calendar@3.7.1(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.0
|
||||
'@react-stately/utils': 3.10.5(react@18.3.1)
|
||||
'@react-types/calendar': 3.6.1(react@18.3.1)
|
||||
'@react-types/shared': 3.28.0(react@18.3.1)
|
||||
|
|
@ -8145,7 +8148,7 @@ snapshots:
|
|||
|
||||
'@react-stately/datepicker@3.13.0(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.0
|
||||
'@internationalized/string': 3.2.6
|
||||
'@react-stately/form': 3.1.3(react@18.3.1)
|
||||
'@react-stately/overlays': 3.6.15(react@18.3.1)
|
||||
|
|
@ -8381,7 +8384,7 @@ snapshots:
|
|||
|
||||
'@react-types/calendar@3.6.1(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.0
|
||||
'@react-types/shared': 3.28.0(react@18.3.1)
|
||||
react: 18.3.1
|
||||
|
||||
|
|
@ -8408,7 +8411,7 @@ snapshots:
|
|||
|
||||
'@react-types/datepicker@3.11.0(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.0
|
||||
'@react-types/calendar': 3.7.0(react@18.3.1)
|
||||
'@react-types/overlays': 3.8.14(react@18.3.1)
|
||||
'@react-types/shared': 3.29.0(react@18.3.1)
|
||||
|
|
@ -10447,7 +10450,7 @@ snapshots:
|
|||
|
||||
husky@9.1.7: {}
|
||||
|
||||
ical.js@1.5.0: {}
|
||||
ical.js@2.1.0: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
|
|
|
|||
114
src/pages/calendar/Schedule.tsx
Normal file
114
src/pages/calendar/Schedule.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import ICAL from "ical.js"
|
||||
import dayjs from "dayjs"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Calendar } from "@heroui/react"
|
||||
import { today, getLocalTimeZone } from "@internationalized/date"
|
||||
|
||||
type ScheduleEvent = {
|
||||
start: Date
|
||||
end: Date
|
||||
summary: string
|
||||
description: string
|
||||
recurrenceId?: string
|
||||
}
|
||||
|
||||
const parseCal = async (): Promise<ICAL.Component> => {
|
||||
const res = await fetch("https://ical.nbtca.space/").then(res => res.text())
|
||||
const jcalData = ICAL.parse(res)
|
||||
return new ICAL.Component(jcalData)
|
||||
}
|
||||
|
||||
const expandEventOccurrences = (
|
||||
event: ICAL.Event,
|
||||
rangeEnd: ICAL.Time,
|
||||
): ScheduleEvent[] => {
|
||||
const occurrences: ScheduleEvent[] = []
|
||||
const iterator = event.iterator()
|
||||
let next: ICAL.Time | null = null
|
||||
|
||||
while ((next = iterator.next())) {
|
||||
if (next.compare(event.startDate) < 0) continue
|
||||
if (next.compare(rangeEnd) > 0) break
|
||||
const details = event.getOccurrenceDetails(next)
|
||||
occurrences.push({
|
||||
start: details.startDate.toJSDate(),
|
||||
end: details.endDate.toJSDate(),
|
||||
summary: event.summary,
|
||||
description: event.description,
|
||||
recurrenceId: event.recurrenceId,
|
||||
})
|
||||
}
|
||||
|
||||
return occurrences
|
||||
}
|
||||
|
||||
const extractScheduleEvents = (icalComp: ICAL.Component): ScheduleEvent[] => {
|
||||
const vevents = icalComp.getAllSubcomponents("vevent")
|
||||
const rangeEnd = ICAL.Time.fromDateString("2026-01-01")
|
||||
|
||||
return vevents
|
||||
.flatMap<ScheduleEvent>((vevent) => {
|
||||
const event = new ICAL.Event(vevent)
|
||||
if (event.iterator().complete) {
|
||||
return [{
|
||||
start: event.startDate.toJSDate(),
|
||||
end: event.endDate.toJSDate(),
|
||||
summary: event.summary,
|
||||
description: event.description,
|
||||
recurrenceId: "123",
|
||||
}]
|
||||
}
|
||||
return expandEventOccurrences(event, rangeEnd)
|
||||
})
|
||||
.sort((a, b) => b.start.getTime() - a.start.getTime())
|
||||
}
|
||||
|
||||
const formatTimePair = (s: Date, e: Date): string => {
|
||||
const start = dayjs(s)
|
||||
const end = dayjs(e)
|
||||
if (start.isSame(end, "day")) {
|
||||
return `${start.format("YYYY-MM-DD HH:mm")} - ${end.format("HH:mm")}`
|
||||
}
|
||||
return `${start.format("YYYY-MM-DD")} - ${end.format("YYYY-MM-DD")}`
|
||||
}
|
||||
|
||||
export default function Schedule() {
|
||||
const [scheduledEvents, setScheduledEvents] = useState<ScheduleEvent[]>([])
|
||||
|
||||
const defaultDate = today(getLocalTimeZone())
|
||||
const [focusedDate, setFocusedDate] = useState(defaultDate)
|
||||
|
||||
useEffect(() => {
|
||||
parseCal().then((icalComp) => {
|
||||
setScheduledEvents(extractScheduleEvents(icalComp))
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section className="w-full flex flex-col ">
|
||||
<div className="section-content my-8 text-2xl font-bold">日程</div>
|
||||
<div className="section-content grid grid-cols-5">
|
||||
<div className="flex flex-col col-span-3">
|
||||
{scheduledEvents.map((event, index) => (
|
||||
<div key={index} className="p-4 m-2 border rounded-lg shadow">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-xl font-semibold">{event.summary}</div>
|
||||
<span>{ formatTimePair(event.start, event.end) }</span>
|
||||
</div>
|
||||
<p>{event.description}</p>
|
||||
<p>{event.recurrenceId}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Calendar
|
||||
aria-label="Date (Controlled Focused Value)"
|
||||
focusedValue={focusedDate}
|
||||
value={defaultDate}
|
||||
onFocusChange={setFocusedDate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
import FullCalendar from "./Calendar"
|
||||
import Schedule from "./Schedule"
|
||||
import BaseLayout from "../../layouts/BaseLayout.astro"
|
||||
---
|
||||
|
||||
<BaseLayout primaryTitle="日历">
|
||||
<FullCalendar client:load />
|
||||
<Schedule client:load />
|
||||
</BaseLayout>
|
||||
|
|
|
|||
Loading…
Reference in a new issue