Skip to content

Commit 2fab8f2

Browse files
authored
feat(ActivityRecord): Checkbox, Counter, DataTable sort as descending date and minor fixes (#697)
* feat(ActivityRecord): replace Multiselect with Checkbox with word and mamber counter * feat(ActivityRecord): add refresh watcher to CustomCASRecordViewMyActivityRecords component * docs(changesets): update * feat(DataTable): sort by date descending order & add page size * docs(changesets): update
1 parent 2bd4e7e commit 2fab8f2

File tree

7 files changed

+83
-32
lines changed

7 files changed

+83
-32
lines changed

.changeset/afraid-apricots-wash.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"enspire": minor
3+
---
4+
5+
Add Word Counter and Member Counter

.changeset/chatty-yaks-begin.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"enspire": patch
3+
---
4+
5+
Sort by date descending order & add page size in DataTable

.changeset/clean-pets-fail.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"enspire": patch
3+
---
4+
5+
Add refresh watcher to CustomCASRecordViewMyActivityRecords component

.changeset/strong-beans-explode.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"enspire": minor
3+
---
4+
5+
Replace Multiselect with Checkbox on NewActivityRecord

app/components/custom/CAS/Record/NewActivityRecord.vue

+48-28
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { AllClubs } from '@@/types/api/user/all_clubs'
33
import { Button } from '@/components/ui/button'
44
import { Calendar } from '@/components/ui/calendar'
55
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
6-
import { Multiselect } from '@/components/ui/multiselect'
6+
import { Checkbox } from '@/components/ui/checkbox'
77
import {
88
NumberField,
99
NumberFieldContent,
@@ -39,12 +39,12 @@ const formSchema = toTypedSchema(z.object({
3939
club: z.string(),
4040
date: z
4141
.string()
42-
.refine(v => v, { message: 'A date of birth is required.' }),
43-
text: z.string().min(10).max(300),
42+
.refine(v => v, { message: '日期为必填项' }),
43+
text: z.string().min(10, "字数不得少于 10 字符").max(300, "字数不得大于 300 字符"),
4444
members: z.array(z.string().uuid()),
45-
cTime: z.number().min(0).max(5),
46-
aTime: z.number().min(0).max(5),
47-
sTime: z.number().min(0).max(5),
45+
cTime: z.number().min(0).max(5, "时长不得大于 5 小时"),
46+
aTime: z.number().min(0).max(5, "时长不得大于 5 小时"),
47+
sTime: z.number().min(0).max(5, "时长不得大于 5 小时"),
4848
}))
4949
5050
const { data, suspense } = useQuery<AllClubs>({
@@ -68,6 +68,17 @@ const { handleSubmit, resetForm, setFieldValue, values } = useForm({
6868
},
6969
})
7070
71+
const selectedMembers = ref<string[]>([])
72+
73+
const handleCheckboxChange = (memberId: string, checked: boolean) => {
74+
if (checked) {
75+
selectedMembers.value.push(memberId)
76+
} else {
77+
selectedMembers.value = selectedMembers.value.filter(id => id !== memberId)
78+
}
79+
setFieldValue('members', selectedMembers.value)
80+
}
81+
7182
const calendarPlaceholder = ref()
7283
const calendarValue = computed({
7384
get: () => values.date ? parseDate(values.date) : undefined,
@@ -148,7 +159,7 @@ const onSubmit = handleSubmit(async (values) => {
148159
!calendarValue && 'text-muted-foreground',
149160
)"
150161
>
151-
<span>{{ calendarValue ? df.format(toDate(calendarValue)) : "Pick a date" }}</span>
162+
<span>{{ calendarValue ? df.format(toDate(calendarValue)) : "请选择日期..." }}</span>
152163
<CalendarIcon class="ms-auto h-4 w-4 opacity-50" />
153164
</Button>
154165
<input hidden>
@@ -212,13 +223,16 @@ const onSubmit = handleSubmit(async (values) => {
212223
</FormField>
213224
</div>
214225

215-
<FormField v-slot="{ componentField }" name="text">
226+
<FormField v-slot="{ componentField, value }" name="text">
216227
<FormItem>
217-
<FormLabel>活动概要</FormLabel>
228+
<div class="flex items-center justify-between">
229+
<FormLabel>活动概要</FormLabel>
230+
<span class="text-sm text-muted-foreground">{{ value?.length || 0 }}/300</span>
231+
</div>
218232
<FormControl>
219233
<Textarea
220234
class="resize-none"
221-
placeholder="详细记录,最多一百字..."
235+
placeholder="详细记录,最多三百字..."
222236
v-bind="componentField"
223237
:disabled="isLoading"
224238
/>
@@ -229,24 +243,30 @@ const onSubmit = handleSubmit(async (values) => {
229243

230244
<FormField v-slot="{ componentField }" name="members">
231245
<FormItem>
232-
<FormLabel>参与者</FormLabel>
246+
<div class="flex items-center justify-between">
247+
<FormLabel>参与者 <span class="text-sm text-muted-foreground">({{ selectedMembers.length }}/{{ (([...data.president, ...data.vice].find(club => club.id === Number(selectedClub))?.memberships) ?? []).length }})</span></FormLabel>
248+
</div>
233249
<FormControl>
234-
<Multiselect
235-
v-if="data"
236-
:options="((
237-
[...data.president, ...data.vice].find(
238-
(club) => club.id === Number(selectedClub),
239-
)
240-
)?.memberships)?.map((membership) => ({
241-
label: membership.name,
242-
value: membership.id,
243-
})) ?? []"
244-
placeholder="Select options"
245-
variant="inverted"
246-
v-bind="componentField"
247-
:animation="2"
248-
:max-count="3"
249-
/>
250+
<div class="flex flex-wrap gap-x-2 gap-y-2">
251+
<div
252+
v-for="member in (
253+
[...data.president, ...data.vice].find(
254+
club => club.id === Number(selectedClub)
255+
)?.memberships
256+
) ?? []"
257+
:key="member.id"
258+
class="flex items-center space-x-1"
259+
>
260+
<Checkbox
261+
:id="member.id"
262+
:value="member.id"
263+
:checked="selectedMembers.includes(member.id)"
264+
@update:checked="(checked) => handleCheckboxChange(member.id, checked)"
265+
class="w-3.5 h-3.5"
266+
/>
267+
<Label :for="member.id">{{ member.name }}</Label>
268+
</div>
269+
</div>
250270
</FormControl>
251271
<FormMessage />
252272
</FormItem>
@@ -260,4 +280,4 @@ const onSubmit = handleSubmit(async (values) => {
260280
</CardContent>
261281
</Card>
262282
<Toaster />
263-
</template>
283+
</template>

app/components/custom/CAS/Record/view-activity-records/DataTable.vue

+10-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ type ActivityRecordtWithClubInfo = ActivityRecord & {
3535
}
3636
3737
const isDialogOpen = ref(false)
38-
const sorting = ref<SortingState>([])
38+
const sorting = ref<SortingState>([
39+
{
40+
id: 'date', // Match this with your date column's accessorKey
41+
desc: true // true for descending order (newest first)
42+
}
43+
])
3944
const columnFilters = ref<ColumnFiltersState>([])
4045
const currentRequestInDialog = ref<ActivityRecordtWithClubInfo>()
4146
@@ -52,12 +57,15 @@ const table = useVueTable({
5257
get sorting() { return sorting.value },
5358
get columnFilters() { return columnFilters.value },
5459
},
60+
initialState: {
61+
sorting: sorting.value
62+
}
5563
})
5664
5765
const isLoading = ref(false)
5866
const deleteDialogOpen = ref(false)
5967
60-
table.setPageSize(5)
68+
table.setPageSize(20)
6169
6270
async function submitDeletion(id: string) {
6371
isLoading.value = true

app/pages/activity/view.vue

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
<script lang="ts" setup>
2+
import { ref } from 'vue'
3+
import CustomCASRecordViewMyActivityRecords from '@/components/custom/CAS/Record/ViewMyActivityRecords.vue'
4+
25
definePageMeta({
36
middleware: ['auth'],
47
breadcrumb: '浏览',
@@ -7,11 +10,11 @@ definePageMeta({
710
useHead({
811
title: 'Activities | Enspire',
912
})
10-
</script>
13+
</script>
1114

1215
<template>
1316
<Suspense>
14-
<CustomCASRecordViewMyActivityRecords />
17+
<CustomCASRecordViewMyActivityRecords :refreshWatcher="ref(false)" />
1518
<template #fallback>
1619
<Card class="w-full">
1720
<CardHeader>

0 commit comments

Comments
 (0)