-
Notifications
You must be signed in to change notification settings - Fork 10
feat(clubInfo): add clubInfo related pages #566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
at-wr
wants to merge
29
commits into
Computerization:next
Choose a base branch
from
at-wr:club-info-dev-1
base: next
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,597
−1,156
Open
Changes from 16 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
5bc7f0a
feat(clubInfo): check if the user is president
at-wr 6b7d175
feat(clubInfo): add component `ViewClubInfo`
at-wr 09557f2
feat(clubInfo): add edit page for club details
at-wr 9ad63f7
feat(clubInfo): add clubInfo section for /cas/clubs/[id]
at-wr 8bdaa8e
fix(ESLint): fix style/indent
at-wr 5ea0d8d
fix(LeaveRequest): fix excessive stack depth
at-wr 4c8f3fe
fix(LeaveRequest): fix excessive stack depth
at-wr 8f5f1f8
feat(clubInfo): add edit and ViewClubInfo components to club page
at-wr 3fb2009
fix(clubInfo): permission error
at-wr dfb6d45
feat(clubInfo): judge if the supervisor have nickname
at-wr 1e9569f
fix(clubInfo): fix privilege api method
at-wr a9f1cef
feat(clubInfo): show 404 when `isPresident` is false
at-wr 4565e08
fix(clubInfo): let title
at-wr d1cf356
feat(clubInfo): only show `ViewClubInfo` if the club still exists
at-wr cc3342c
style(clubInfo): unify club information space style
at-wr 87a4d64
style(clubInfo): unify club information space style
at-wr 3fd4fde
refactor: use `user/all_clubs` for getting editable clubs
qwerzl 0603640
refactor: use `user/all_clubs` for getting editable clubs
qwerzl 16d7593
feat(clubInfo): add dialog for editing club info
at-wr cb967b0
feat(clubInfo): display QR Code in ViewClubInfo
at-wr 4b45f36
feat: add some packages
at-wr 30b3c14
fix: remove test condition
at-wr ae3bf1a
feat(club): show club name in title
at-wr cfc3b3f
chore: update pnpm-lock
at-wr 699bfba
feat: teacher hover card
at-wr 4be6f0a
style: add underline when hovered
at-wr 45e408f
style: add hover placeholder for club members
at-wr cb53a39
feat: add found time for club info
at-wr eeedbdf
feat: add Open Graph meta data
at-wr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<script setup lang="ts"> | ||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '~/components/ui/card' | ||
|
||
const props = defineProps({ | ||
club: { | ||
type: Number, | ||
required: true, | ||
}, | ||
}) | ||
|
||
definePageMeta({ | ||
middleware: ['auth'], | ||
}) | ||
|
||
const { data } = await useAsyncData('allInfo', () => { | ||
return $fetch('/api/cas/info/get', { | ||
headers: useRequestHeaders(), | ||
method: 'GET', | ||
body: { | ||
club: props.club, | ||
}, | ||
}) | ||
}) | ||
|
||
let noGroup = false | ||
|
||
if (!data.value) | ||
noGroup = true | ||
</script> | ||
|
||
<template> | ||
<Card class="w-full"> | ||
<CardHeader> | ||
<CardTitle class="flex items-center h-min gap-x-1"> | ||
社团群聊 | ||
</CardTitle> | ||
<CardDescription class="flex items-center"> | ||
<Icon name="material-symbols:info-outline" /> | ||
<div class="ml-1"> | ||
Club Group | ||
</div> | ||
</CardDescription> | ||
</CardHeader> | ||
<CardContent v-if="!noGroup"> | ||
<div>微信群聊链接: {{ data }}</div> | ||
</CardContent> | ||
<CardContent v-if="noGroup"> | ||
<div class="text-sm italic text-muted-foreground text-center w-full my-2"> | ||
暂无内容 ╥﹏╥... | ||
</div> | ||
</CardContent> | ||
</Card> | ||
</template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
<script setup lang="ts"> | ||
import { useRoute } from 'vue-router' | ||
import { format } from 'date-fns' | ||
import { useForm } from 'vee-validate' | ||
import { toTypedSchema } from '@vee-validate/zod' | ||
import * as z from 'zod' | ||
import Calendar from '../../../../components/ui/calendar/Calendar.vue' | ||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '~/components/ui/card' | ||
import { Button } from '~/components/ui/button' | ||
import { cn } from '~/lib/utils' | ||
import { Textarea } from '~/components/ui/textarea' | ||
import { useToast } from '~/components/ui/toast/use-toast' | ||
import { Toaster } from '~/components/ui/toast' | ||
import type { Club, Clubs } from '~/types/clubs' | ||
|
||
const emit = defineEmits(['refresh']) | ||
const { data } = await useFetch<Clubs>('/api/club/all_details') | ||
const clubs = data.value! | ||
const route = useRoute() | ||
const id = route.params.id // Fetch current Club ID via route params | ||
|
||
// Filter clubs based on C_GroupsID and include information at the same level as groups | ||
const filteredClubs = Object.values(clubs).flatMap(clubCategory => | ||
clubCategory.filter((club: Club) => | ||
club.groups.some(group => group.C_GroupsID === id), | ||
).map((club: Club) => ({ | ||
...club, // Spread to include all same-level information | ||
groups: club.groups.filter(group => group.C_GroupsID === id), // Filter groups to only include those that match the ID | ||
})), | ||
) as Club[] | ||
|
||
const { toast } = useToast() | ||
|
||
definePageMeta({ | ||
middleware: ['auth'], | ||
}) | ||
|
||
const isLoading = ref(false) | ||
|
||
// Check if the Club have Group Information | ||
const { data: groupValue } = await useAsyncData('allInfo', () => $fetch('/api/cas/info/get', { headers: useRequestHeaders(), method: 'GET', body: { club: id } })) | ||
const noGroup = !groupValue.value | ||
|
||
const formSchema = toTypedSchema(z.object({ | ||
clubId: z.number(), | ||
wechatGroupUrl: z.string().startsWith('https://weixin.qq.com/g/', { message: 'WeChat Info URL required' }), | ||
wechatGroupExpiration: z.date(), | ||
})) | ||
|
||
const { handleSubmit, resetForm } = useForm({ | ||
validationSchema: formSchema, | ||
}) | ||
|
||
const onSubmit = handleSubmit(async (values) => { | ||
isLoading.value = true | ||
values.clubId = Number(id) | ||
const apiUrl = noGroup ? '/api/cas/info/new' : '/api/cas/info/update' | ||
const { error } = await useFetch(apiUrl, { | ||
headers: useRequestHeaders(), | ||
method: 'post', | ||
server: false, | ||
body: values, | ||
}) | ||
if (error.value) { | ||
toast({ | ||
title: '错误', | ||
description: '请稍后再试', | ||
variant: 'destructive', | ||
}) | ||
} | ||
isLoading.value = false | ||
emit('refresh') | ||
resetForm() | ||
}) | ||
|
||
// Get privilege information | ||
const { data: privilegeData } = await useFetch<{ isPresident: boolean }>('/api/cas/info/privilege', { | ||
method: 'GET', | ||
body: { | ||
clubId: Number(id), | ||
}, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}) | ||
|
||
const isPresident = privilegeData.value?.isPresident || false | ||
|
||
let title | ||
|
||
if (isPresident) | ||
title = 'Edit Club Info | Enspire' | ||
else | ||
title = '404 Not Found | Enspire' | ||
|
||
useHead({ | ||
title, | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div v-if="isPresident"> | ||
<Card class="w-full h-min mt-2"> | ||
<CardHeader> | ||
<CardTitle v-if="noGroup" class="flex items-center h-min gap-x-1"> | ||
添加 {{ filteredClubs[0].groups[0].C_NameC }} 群聊信息 | ||
</CardTitle> | ||
<CardTitle v-if="!noGroup" class="flex items-center h-min gap-x-1"> | ||
编辑 {{ filteredClubs[0].groups[0].C_NameC }} 群聊信息 | ||
</CardTitle> | ||
<CardDescription class="flex items-center"> | ||
<Icon name="material-symbols:info-outline" /> | ||
<div class="ml-1"> | ||
{{ filteredClubs[0].groups[0].C_NameE }} Group Info | ||
</div> | ||
</CardDescription> | ||
</CardHeader> | ||
<CardContent> | ||
<form class="space-y-6" @submit="onSubmit"> | ||
<FormField v-slot="{ componentField, value }" name="wechatGroupExpiration"> | ||
<FormItem class="flex flex-col"> | ||
<FormLabel>失效日期 Expire Date</FormLabel> | ||
<Popover> | ||
<PopoverTrigger as-child> | ||
<FormControl> | ||
<Button | ||
:class="cn( | ||
'w-full ps-3 text-start font-normal', | ||
!value && 'text-muted-foreground', | ||
)" variant="outline" | ||
:disabled="isLoading" | ||
> | ||
<span>{{ value ? format(value, "PPP") : "选择日期..." }}</span> | ||
<Icon class="ms-auto opacity-50" name="material-symbols:calendar-today-outline" /> | ||
</Button> | ||
</FormControl> | ||
</PopoverTrigger> | ||
<PopoverContent class="p-0"> | ||
<Calendar :min-date="new Date()" v-bind="componentField" /> | ||
</PopoverContent> | ||
</Popover> | ||
<FormMessage /> | ||
</FormItem> | ||
</FormField> | ||
|
||
<FormField v-slot="{ componentField }" name="wechatGroupUrl"> | ||
<FormItem> | ||
<FormLabel>WeChat Group URL</FormLabel> | ||
<FormControl> | ||
<Textarea | ||
class="resize-none" | ||
placeholder="WeChat Group URL" | ||
v-bind="componentField" | ||
:disabled="isLoading" | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
</FormField> | ||
|
||
<Button :disabled="isLoading" type="submit"> | ||
<Icon v-if="isLoading" class="mr-2" name="svg-spinners:180-ring-with-bg" /> | ||
提交 | ||
</Button> | ||
</form> | ||
</CardContent> | ||
</Card> | ||
<Toaster /> | ||
</div> | ||
<div v-else> | ||
<div class="flex flex-col justify-center h-1/2 text-center"> | ||
<h3 class="font-bold text-xl"> | ||
You shouldn't be here... ヾ(≧へ≦)〃 | ||
</h3> | ||
<br> | ||
<NuxtLink class="w-full mt-2" to="/"> | ||
<Button>回到主页</Button> | ||
</NuxtLink> | ||
</div> | ||
</div> | ||
</template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { PrismaClient } from '@prisma/client' | ||
import * as z from 'zod' | ||
|
||
const prisma = new PrismaClient() | ||
|
||
const requestSchema = z.object({ | ||
clubId: z.number(), | ||
}) | ||
|
||
export default eventHandler(async (event) => { | ||
const { auth } = event.context | ||
|
||
if (!auth.userId) { | ||
setResponseStatus(event, 403) | ||
return | ||
} | ||
|
||
const tsimsStudentId = (await prisma.user.findUnique({ | ||
where: { | ||
id: auth.userId, | ||
}, | ||
}))!.tsimsStudentId | ||
|
||
const requestBody = await readValidatedBody(event, body => requestSchema.parse(body)) | ||
|
||
const club = (await prisma.club.findFirst({ | ||
where: { | ||
id: requestBody.clubId, | ||
presidentByTsimsStudentId: tsimsStudentId, | ||
}, | ||
})) | ||
|
||
return !!club | ||
}) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.