Compare commits

..

No commits in common. "490806aaee4e03da8ea94e212d66e17d1510f701" and "49a167fb6862abda24830827adecd0cb17a14920" have entirely different histories.

9 changed files with 29 additions and 79 deletions

View File

@ -46,38 +46,10 @@ void getClasses(ref HttpRequestContext ctx) {
Connection conn = getDb(); Connection conn = getDb();
scope(exit) conn.close(); scope(exit) conn.close();
User user = getUserOrThrow(ctx, conn); User user = getUserOrThrow(ctx, conn);
const query = " auto classes = findAll(
SELECT
c.id, c.number, c.school_year,
COUNT(DISTINCT s.id) AS student_count,
COUNT(DISTINCT e.id) AS entry_count,
MAX(e.date) AS last_entry_date
FROM classroom_compliance_class c
LEFT JOIN classroom_compliance_student s ON c.id = s.class_id
LEFT JOIN classroom_compliance_entry e ON c.id = e.class_id
WHERE c.user_id = ?
GROUP BY c.id
ORDER BY school_year DESC, number ASC
";
struct ClassResponse {
ulong id;
ushort number;
string schoolYear;
uint studentCount;
uint entryCount;
string lastEntryDate;
}
ClassResponse[] classes = findAll(
conn, conn,
query, "SELECT * FROM classroom_compliance_class WHERE user_id = ? ORDER BY school_year DESC, number ASC",
r => ClassResponse( &ClassroomComplianceClass.parse,
r.getUlong(1),
r.getUshort(2),
r.getString(3),
r.getUint(4),
r.getUint(5),
r.getDate(6).toISOExtString()
),
user.id user.id
); );
writeJsonBody(ctx, classes); writeJsonBody(ctx, classes);

View File

@ -16,15 +16,6 @@ export interface Class {
schoolYear: string schoolYear: string
} }
export interface ClassesResponseClass {
id: number
number: number
schoolYear: string
studentCount: number
entryCount: number
lastEntryDate: string
}
export interface Student { export interface Student {
id: number id: number
name: string name: string
@ -109,7 +100,7 @@ export class ClassroomComplianceAPIClient extends APIClient {
return super.post('/classes', { number: number, schoolYear: schoolYear }) return super.post('/classes', { number: number, schoolYear: schoolYear })
} }
getClasses(): APIResponse<ClassesResponseClass[]> { getClasses(): APIResponse<Class[]> {
return super.get('/classes') return super.get('/classes')
} }

View File

@ -1,20 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { type ClassesResponseClass } from '@/api/classroom_compliance' import { type Class } from '@/api/classroom_compliance'
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
defineProps<{ defineProps<{
cls: ClassesResponseClass cls: Class
}>() }>()
const router = useRouter() const router = useRouter()
</script> </script>
<template> <template>
<div class="class-item" @click="router.push(`/classroom-compliance/classes/${cls.id}`)"> <div class="class-item" @click="router.push(`/classroom-compliance/classes/${cls.id}`)">
<h3>Class <span v-text="cls.number"></span></h3> <h3>Class <span v-text="cls.number"></span></h3>
<p> <p v-text="cls.schoolYear"></p>
{{ cls.studentCount }} students,
{{ cls.entryCount }} entries.
<span v-if="cls.entryCount > 0">Last entry from {{ cls.lastEntryDate }}.</span>
</p>
</div> </div>
</template> </template>
<style scoped> <style scoped>

View File

@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ClassroomComplianceAPIClient, type ClassesResponseClass } from '@/api/classroom_compliance' import { ClassroomComplianceAPIClient, type Class } from '@/api/classroom_compliance'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { type Ref, ref, onMounted } from 'vue' import { type Ref, ref, onMounted } from 'vue'
import ClassItem from '@/apps/classroom_compliance/ClassItem.vue' import ClassItem from '@/apps/classroom_compliance/ClassItem.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const classes: Ref<ClassesResponseClass[]> = ref([]) const classes: Ref<Class[]> = ref([])
const authStore = useAuthStore() const authStore = useAuthStore()
const router = useRouter() const router = useRouter()

View File

@ -8,10 +8,10 @@ import {
} from '@/api/classroom_compliance' } from '@/api/classroom_compliance'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { computed, onMounted, ref, watch, type Ref } from 'vue' import { computed, onMounted, ref, watch, type Ref } from 'vue'
import EntryTableCell from '@/apps/classroom_compliance/entries_table/EntryTableCell.vue' import EntryTableCell from '@/apps/classroom_compliance/EntryTableCell.vue'
import StudentScoreCell from '@/apps/classroom_compliance/entries_table/StudentScoreCell.vue' import { RouterLink } from 'vue-router'
import DateHeaderCell from '@/apps/classroom_compliance/entries_table/DateHeaderCell.vue' import StudentScoreCell from '@/apps/classroom_compliance/StudentScoreCell.vue'
import StudentNameCell from '@/apps/classroom_compliance/entries_table/StudentNameCell.vue' import DateHeaderCell from '@/apps/classroom_compliance/DateHeaderCell.vue'
const authStore = useAuthStore() const authStore = useAuthStore()
const props = defineProps<{ const props = defineProps<{
@ -232,7 +232,13 @@ function addAllEntriesForDate(dateStr: string) {
</thead> </thead>
<tbody> <tbody>
<tr v-for="student in students" :key="student.id"> <tr v-for="student in students" :key="student.id">
<StudentNameCell :student="student" :class-id="classId" /> <!-- Student's name: -->
<td :class="{ 'student-removed': student.removed }">
<RouterLink :to="'/classroom-compliance/classes/' + classId + '/students/' + student.id"
class="student-link">
<span v-text="student.name"></span>
</RouterLink>
</td>
<!-- Desk Number: --> <!-- Desk Number: -->
<td v-if="assignedDesks" v-text="student.deskNumber"></td> <td v-if="assignedDesks" v-text="student.deskNumber"></td>
<!-- A cell for each entry in the table's date range: --> <!-- A cell for each entry in the table's date range: -->
@ -263,4 +269,13 @@ function addAllEntriesForDate(dateStr: string) {
border: 1px solid black; border: 1px solid black;
border-collapse: collapse; border-collapse: collapse;
} }
.student-link {
text-decoration: none;
color: inherit;
}
.student-removed {
text-decoration: line-through;
}
</style> </style>

View File

@ -1,24 +0,0 @@
<script setup lang="ts">
import type { EntriesResponseStudent } from '@/api/classroom_compliance';
defineProps<{
student: EntriesResponseStudent,
classId: number
}>()
</script>
<template>
<td :class="{ 'student-removed': student.removed }">
<RouterLink :to="'/classroom-compliance/classes/' + classId + '/students/' + student.id" class="student-link">
<span v-text="student.name"></span>
</RouterLink>
</td>
</template>
<style scoped>
.student-link {
text-decoration: none;
color: inherit;
}
.student-removed {
text-decoration: line-through;
}
</style>