Skip to content

Commit 8b4cffd

Browse files
authored
feat: complete implementation of classroom reservation (#574)
* refactor(minor): changed date and time format in formData * feat: reservation record to database * feat: managing reservations * feat: full features of reservation
1 parent 0ec7079 commit 8b4cffd

28 files changed

+17754
-146
lines changed

.npmrc

Lines changed: 0 additions & 1 deletion
This file was deleted.

components/custom/enum2str.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const enums = {
2+
days: {
3+
values: [
4+
'SUNDAY',
5+
'MONDAY',
6+
'TUESDAY',
7+
'WEDNESDAY',
8+
'THURSDAY',
9+
'FRIDAY',
10+
'SATURDAY',
11+
],
12+
map: {
13+
SUNDAY: '周日',
14+
MONDAY: '周一',
15+
TUESDAY: '周二',
16+
WEDNESDAY: '周三',
17+
THURSDAY: '周四',
18+
FRIDAY: '周五',
19+
SATURDAY: '周六',
20+
},
21+
},
22+
periods: {
23+
values: [
24+
'MORNING',
25+
'ONE',
26+
'TWO',
27+
'THREE',
28+
'FOUR',
29+
'NOON',
30+
'FIVE',
31+
'SIX',
32+
'SEVEN',
33+
'EIGHT',
34+
'NINE',
35+
'AFTERCLASS',
36+
],
37+
map: {
38+
MORNING: '早读',
39+
ONE: '第 1 节课',
40+
TWO: '第 2 节课',
41+
THREE: '第 3 节课',
42+
FOUR: '第 4 节课',
43+
NOON: '午休',
44+
FIVE: '第 5 节课',
45+
SIX: '第 6 节课',
46+
SEVEN: '第 7 节课',
47+
EIGHT: '第 8 节课',
48+
NINE: '第 9 节课',
49+
AFTERCLASS: '放学后',
50+
},
51+
},
52+
}
53+
54+
function time2period(hhmm: number, day: string) {
55+
const timePeriods = [
56+
{ name: 'ONE', start: 815, end: 855 },
57+
{ name: 'TWO', start: 855, end: 945 },
58+
{ name: 'THREE', start: 945, end: 1035 },
59+
{ name: 'FOUR', start: 1035, end: 1125 },
60+
{ name: 'NOON', start: 1125, end: 1230 },
61+
{ name: 'FIVE', start: 1230, end: 1310 },
62+
{ name: 'SIX', start: 1310, end: 1400 },
63+
{ name: 'SEVEN', start: 1400, end: 1450 },
64+
{ name: 'EIGHT', start: 1450, end: 1540 },
65+
{ name: 'NINE', start: 1540, end: 1630 },
66+
]
67+
if (hhmm < timePeriods[0].start) {
68+
return 'MORNING'
69+
}
70+
else if (day === 'FRIDAY' && hhmm >= timePeriods[8].end) {
71+
return 'AFTERCLASS'
72+
}
73+
else if (hhmm >= timePeriods[9].end) {
74+
return 'AFTERCLASS'
75+
}
76+
else {
77+
for (const period of timePeriods) {
78+
if (period.start <= hhmm && hhmm < period.end) {
79+
return period.name
80+
}
81+
}
82+
}
83+
}
84+
85+
export { enums, time2period }

components/custom/sidebar.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ import { Button } from '@/components/ui/button'
4646
预约教室
4747
</Button>
4848
</NuxtLink>
49+
<NuxtLink to="/manage/manage">
50+
<Button :variant="$route.name === 'manage-manage' ? 'secondary' : 'ghost'" class="w-full justify-start">
51+
<Icon class="mr-2 h-4 w-4" name="material-symbols:calendar-today-outline" />
52+
管理预约
53+
</Button>
54+
</NuxtLink>
55+
<NuxtLink to="/manage/statuses">
56+
<Button :variant="$route.name === 'manage-statuses' ? 'secondary' : 'ghost'" class="w-full justify-start">
57+
<Icon class="mr-2 h-4 w-4" name="material-symbols:calendar-today-outline" />
58+
教室状态
59+
</Button>
60+
</NuxtLink>
4961
<NuxtLink to="/manage/record">
5062
<Button :variant="$route.name === 'manage-record' ? 'secondary' : 'ghost'" class="w-full justify-start mt-1">
5163
<Icon class="mr-2 h-4 w-4" name="charm:tick-double" />

components/ui/alert/Alert.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { type AlertVariants, alertVariants } from '.'
4+
import { cn } from '@/lib/utils'
5+
6+
const props = defineProps<{
7+
class?: HTMLAttributes['class']
8+
variant?: AlertVariants['variant']
9+
}>()
10+
</script>
11+
12+
<template>
13+
<div :class="cn(alertVariants({ variant }), props.class)" role="alert">
14+
<slot />
15+
</div>
16+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div :class="cn('text-sm [&_p]:leading-relaxed', props.class)">
12+
<slot />
13+
</div>
14+
</template>

components/ui/alert/AlertTitle.vue

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@/lib/utils'
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class']
7+
}>()
8+
</script>
9+
10+
<template>
11+
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
12+
<slot />
13+
</h5>
14+
</template>

components/ui/alert/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { type VariantProps, cva } from 'class-variance-authority'
2+
3+
export { default as Alert } from './Alert.vue'
4+
export { default as AlertTitle } from './AlertTitle.vue'
5+
export { default as AlertDescription } from './AlertDescription.vue'
6+
7+
export const alertVariants = cva(
8+
'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
9+
{
10+
variants: {
11+
variant: {
12+
default: 'bg-background text-foreground',
13+
destructive:
14+
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
15+
},
16+
},
17+
defaultVariants: {
18+
variant: 'default',
19+
},
20+
},
21+
)
22+
23+
export type AlertVariants = VariantProps<typeof alertVariants>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import { HoverCardRoot, type HoverCardRootEmits, type HoverCardRootProps, useForwardPropsEmits } from 'radix-vue'
3+
4+
const props = defineProps<HoverCardRootProps>()
5+
const emits = defineEmits<HoverCardRootEmits>()
6+
7+
const forwarded = useForwardPropsEmits(props, emits)
8+
</script>
9+
10+
<template>
11+
<HoverCardRoot v-bind="forwarded">
12+
<slot />
13+
</HoverCardRoot>
14+
</template>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script setup lang="ts">
2+
import { type HTMLAttributes, computed } from 'vue'
3+
import {
4+
HoverCardContent,
5+
type HoverCardContentProps,
6+
HoverCardPortal,
7+
useForwardProps,
8+
} from 'radix-vue'
9+
import { cn } from '@/lib/utils'
10+
11+
const props = withDefaults(
12+
defineProps<HoverCardContentProps & { class?: HTMLAttributes['class'] }>(),
13+
{
14+
sideOffset: 4,
15+
},
16+
)
17+
18+
const delegatedProps = computed(() => {
19+
const { class: _, ...delegated } = props
20+
21+
return delegated
22+
})
23+
24+
const forwardedProps = useForwardProps(delegatedProps)
25+
</script>
26+
27+
<template>
28+
<HoverCardPortal>
29+
<HoverCardContent
30+
v-bind="forwardedProps"
31+
:class="
32+
cn(
33+
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
34+
props.class,
35+
)
36+
"
37+
>
38+
<slot />
39+
</HoverCardContent>
40+
</HoverCardPortal>
41+
</template>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script setup lang="ts">
2+
import { HoverCardTrigger, type HoverCardTriggerProps } from 'radix-vue'
3+
4+
const props = defineProps<HoverCardTriggerProps>()
5+
</script>
6+
7+
<template>
8+
<HoverCardTrigger v-bind="props">
9+
<slot />
10+
</HoverCardTrigger>
11+
</template>

0 commit comments

Comments
 (0)