Added labels for students, and one more checklist item.
This commit is contained in:
		
							parent
							
								
									338d861906
								
							
						
					
					
						commit
						b1f9cfa710
					
				| 
						 | 
					@ -23,6 +23,14 @@ CREATE TABLE classroom_compliance_student (
 | 
				
			||||||
	removed BOOLEAN NOT NULL DEFAULT FALSE
 | 
						removed BOOLEAN NOT NULL DEFAULT FALSE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE TABLE classroom_compliance_student_label (
 | 
				
			||||||
 | 
						student_id BIGINT NOT NULL
 | 
				
			||||||
 | 
							REFERENCES classroom_compliance_student(id)
 | 
				
			||||||
 | 
								ON UPDATE CASCADE ON DELETE CASCADE,
 | 
				
			||||||
 | 
						label VARCHAR(255) NOT NULL,
 | 
				
			||||||
 | 
						PRIMARY KEY (student_id, label)
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE classroom_compliance_entry (
 | 
					CREATE TABLE classroom_compliance_entry (
 | 
				
			||||||
	id BIGSERIAL PRIMARY KEY,
 | 
						id BIGSERIAL PRIMARY KEY,
 | 
				
			||||||
	class_id BIGINT NOT NULL
 | 
						class_id BIGINT NOT NULL
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,8 @@ void registerApiEndpoints(PathHandler handler) {
 | 
				
			||||||
    handler.addMapping(Method.PUT, STUDENT_PATH ~ "/class", &moveStudentToOtherClass);
 | 
					    handler.addMapping(Method.PUT, STUDENT_PATH ~ "/class", &moveStudentToOtherClass);
 | 
				
			||||||
    handler.addMapping(Method.GET, STUDENT_PATH ~ "/entries", &getStudentEntries);
 | 
					    handler.addMapping(Method.GET, STUDENT_PATH ~ "/entries", &getStudentEntries);
 | 
				
			||||||
    handler.addMapping(Method.GET, STUDENT_PATH ~ "/overview", &getStudentOverview);
 | 
					    handler.addMapping(Method.GET, STUDENT_PATH ~ "/overview", &getStudentOverview);
 | 
				
			||||||
 | 
					    handler.addMapping(Method.GET, STUDENT_PATH ~ "/labels", &getStudentLabels);
 | 
				
			||||||
 | 
					    handler.addMapping(Method.POST, STUDENT_PATH ~ "/labels", &updateStudentLabels);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handler.addMapping(Method.GET, CLASS_PATH ~ "/entries", &getEntries);
 | 
					    handler.addMapping(Method.GET, CLASS_PATH ~ "/entries", &getEntries);
 | 
				
			||||||
    handler.addMapping(Method.POST, CLASS_PATH ~ "/entries", &saveEntries);
 | 
					    handler.addMapping(Method.POST, CLASS_PATH ~ "/entries", &saveEntries);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -279,3 +279,37 @@ void moveStudentToOtherClass(ref HttpRequestContext ctx) {
 | 
				
			||||||
    conn.commit();
 | 
					    conn.commit();
 | 
				
			||||||
    // We just return 200 OK, no response body.
 | 
					    // We just return 200 OK, no response body.
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void getStudentLabels(ref HttpRequestContext ctx) {
 | 
				
			||||||
 | 
					    Connection conn = getDb();
 | 
				
			||||||
 | 
					    scope(exit) conn.close();
 | 
				
			||||||
 | 
					    User user = getUserOrThrow(ctx, conn);
 | 
				
			||||||
 | 
					    auto student = getStudentOrThrow(ctx, conn, user);
 | 
				
			||||||
 | 
					    string[] labels = findAll(
 | 
				
			||||||
 | 
					        conn,
 | 
				
			||||||
 | 
					        "SELECT label FROM classroom_compliance_student_label WHERE student_id = ? ORDER BY label ASC",
 | 
				
			||||||
 | 
					        (r) => r.getString(1),
 | 
				
			||||||
 | 
					        student.id
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    writeJsonBody(ctx, labels);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void updateStudentLabels(ref HttpRequestContext ctx) {
 | 
				
			||||||
 | 
					    Connection conn = getDb();
 | 
				
			||||||
 | 
					    scope(exit) conn.close();
 | 
				
			||||||
 | 
					    User user = getUserOrThrow(ctx, conn);
 | 
				
			||||||
 | 
					    auto student = getStudentOrThrow(ctx, conn, user);
 | 
				
			||||||
 | 
					    string[] labels = readJsonPayload!(string[])(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn.setAutoCommit(false);
 | 
				
			||||||
 | 
					    update(conn, "DELETE FROM classroom_compliance_student_label WHERE student_id = ?", student.id);
 | 
				
			||||||
 | 
					    PreparedStatement ps = conn.prepareStatement(
 | 
				
			||||||
 | 
					        "INSERT INTO classroom_compliance_student_label (student_id, label) VALUES (?, ?)"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    foreach (label; labels) {
 | 
				
			||||||
 | 
					        ps.setUlong(1, student.id);
 | 
				
			||||||
 | 
					        ps.setString(2, label);
 | 
				
			||||||
 | 
					        ps.executeUpdate();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    conn.commit();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -245,4 +245,15 @@ export class ClassroomComplianceAPIClient extends APIClient {
 | 
				
			||||||
  ): APIResponse<StudentStatisticsOverview> {
 | 
					  ): APIResponse<StudentStatisticsOverview> {
 | 
				
			||||||
    return super.get(`/classes/${classId}/students/${studentId}/overview`)
 | 
					    return super.get(`/classes/${classId}/students/${studentId}/overview`)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getStudentLabels(classId: number, studentId: number): APIResponse<string[]> {
 | 
				
			||||||
 | 
					    return super.get(`/classes/${classId}/students/${studentId}/labels`)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updateStudentLabels(classId: number, studentId: number, labels: string[]): APIResponse<void> {
 | 
				
			||||||
 | 
					    return super.postWithNoExpectedResponse(
 | 
				
			||||||
 | 
					      `/classes/${classId}/students/${studentId}/labels`,
 | 
				
			||||||
 | 
					      labels,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -308,8 +308,9 @@ defineExpose({
 | 
				
			||||||
      <tbody>
 | 
					      <tbody>
 | 
				
			||||||
        <tr v-for="(student, idx) in getVisibleStudents()" :key="student.id" style="height: 2em;">
 | 
					        <tr v-for="(student, idx) in getVisibleStudents()" :key="student.id" style="height: 2em;">
 | 
				
			||||||
          <td v-if="selectedView !== TableView.WHITEBOARD" style="text-align: right; padding-right: 0.5em;">{{ idx + 1
 | 
					          <td v-if="selectedView !== TableView.WHITEBOARD" style="text-align: right; padding-right: 0.5em;">{{ idx + 1
 | 
				
			||||||
          }}.</td>
 | 
					            }}.</td>
 | 
				
			||||||
          <StudentNameCell :student="student" :class-id="classId" />
 | 
					          <StudentNameCell :student="student" :class-id="classId"
 | 
				
			||||||
 | 
					            :show-labels="selectedView !== TableView.WHITEBOARD" />
 | 
				
			||||||
          <td v-if="assignedDesks" v-text="student.deskNumber"></td>
 | 
					          <td v-if="assignedDesks" v-text="student.deskNumber"></td>
 | 
				
			||||||
          <EntryTableCell v-for="(entry, date) in getVisibleStudentEntries(student)" :key="date"
 | 
					          <EntryTableCell v-for="(entry, date) in getVisibleStudentEntries(student)" :key="date"
 | 
				
			||||||
            v-model="student.entries[date]" :date-str="date" :last-save-state-timestamp="lastSaveStateTimestamp"
 | 
					            v-model="student.entries[date]" :date-str="date" :last-save-state-timestamp="lastSaveStateTimestamp"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,8 @@ const router = useRouter()
 | 
				
			||||||
const cls: Ref<Class | null> = ref(null)
 | 
					const cls: Ref<Class | null> = ref(null)
 | 
				
			||||||
const student: Ref<Student | null> = ref(null)
 | 
					const student: Ref<Student | null> = ref(null)
 | 
				
			||||||
const entries: Ref<Entry[]> = ref([])
 | 
					const entries: Ref<Entry[]> = ref([])
 | 
				
			||||||
 | 
					const labels: Ref<string[]> = ref([])
 | 
				
			||||||
 | 
					const newLabel = ref('')
 | 
				
			||||||
const statistics: Ref<StudentStatisticsOverview | null> = ref(null)
 | 
					const statistics: Ref<StudentStatisticsOverview | null> = ref(null)
 | 
				
			||||||
// Filtered set of entries for "last week", used in the week overview dialog.
 | 
					// Filtered set of entries for "last week", used in the week overview dialog.
 | 
				
			||||||
const lastWeeksEntries = computed(() => {
 | 
					const lastWeeksEntries = computed(() => {
 | 
				
			||||||
| 
						 | 
					@ -50,6 +52,8 @@ const lastWeeksEntries = computed(() => {
 | 
				
			||||||
const apiClient = new ClassroomComplianceAPIClient(authStore)
 | 
					const apiClient = new ClassroomComplianceAPIClient(authStore)
 | 
				
			||||||
const deleteConfirmDialog = useTemplateRef('deleteConfirmDialog')
 | 
					const deleteConfirmDialog = useTemplateRef('deleteConfirmDialog')
 | 
				
			||||||
const weekOverviewDialog = useTemplateRef('weekOverviewDialog')
 | 
					const weekOverviewDialog = useTemplateRef('weekOverviewDialog')
 | 
				
			||||||
 | 
					const labelsDialog = useTemplateRef('labelsDialog')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  const classIdNumber = parseInt(props.classId, 10)
 | 
					  const classIdNumber = parseInt(props.classId, 10)
 | 
				
			||||||
  cls.value = await apiClient.getClass(classIdNumber).handleErrorsWithAlert()
 | 
					  cls.value = await apiClient.getClass(classIdNumber).handleErrorsWithAlert()
 | 
				
			||||||
| 
						 | 
					@ -78,8 +82,23 @@ onMounted(async () => {
 | 
				
			||||||
        console.warn('Failed to get student statistics: ', result.message)
 | 
					        console.warn('Failed to get student statistics: ', result.message)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					  fetchLabels()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function fetchLabels() {
 | 
				
			||||||
 | 
					  if (!cls.value || !student.value) {
 | 
				
			||||||
 | 
					    labels.value = []
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const result = await apiClient.getStudentLabels(cls.value.id, student.value.id).result
 | 
				
			||||||
 | 
					  if (result instanceof APIError) {
 | 
				
			||||||
 | 
					    console.error("Failed to get labels.", result.message)
 | 
				
			||||||
 | 
					    labels.value = []
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    labels.value = result
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function deleteThisStudent() {
 | 
					async function deleteThisStudent() {
 | 
				
			||||||
  if (!cls.value || !student.value || cls.value.archived) return
 | 
					  if (!cls.value || !student.value || cls.value.archived) return
 | 
				
			||||||
  const choice = await deleteConfirmDialog.value?.show()
 | 
					  const choice = await deleteConfirmDialog.value?.show()
 | 
				
			||||||
| 
						 | 
					@ -93,6 +112,27 @@ function getFormattedDate(entry: Entry) {
 | 
				
			||||||
  const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
 | 
					  const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
 | 
				
			||||||
  return days[d.getDay()] + ', ' + d.toLocaleDateString()
 | 
					  return days[d.getDay()] + ', ' + d.toLocaleDateString()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function addLabel() {
 | 
				
			||||||
 | 
					  if (!cls.value || !student.value || cls.value.archived) return
 | 
				
			||||||
 | 
					  const newLabels = [...labels.value, newLabel.value.trim()]
 | 
				
			||||||
 | 
					  const success = await apiClient.updateStudentLabels(cls.value.id, student.value.id, newLabels)
 | 
				
			||||||
 | 
					    .handleErrorsWithAlertNoBody()
 | 
				
			||||||
 | 
					  if (success) {
 | 
				
			||||||
 | 
					    newLabel.value = ''
 | 
				
			||||||
 | 
					    await fetchLabels()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function deleteLabel(label: string) {
 | 
				
			||||||
 | 
					  if (!cls.value || !student.value || cls.value.archived) return
 | 
				
			||||||
 | 
					  const newLabels = labels.value.filter(s => s !== label)
 | 
				
			||||||
 | 
					  const success = await apiClient.updateStudentLabels(cls.value.id, student.value.id, newLabels)
 | 
				
			||||||
 | 
					    .handleErrorsWithAlertNoBody()
 | 
				
			||||||
 | 
					  if (success) {
 | 
				
			||||||
 | 
					    await fetchLabels()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div v-if="student" class="centered-content">
 | 
					  <div v-if="student" class="centered-content">
 | 
				
			||||||
| 
						 | 
					@ -110,6 +150,7 @@ function getFormattedDate(entry: Entry) {
 | 
				
			||||||
      <button type="button" @click="deleteThisStudent" :disabled="cls?.archived">Delete</button>
 | 
					      <button type="button" @click="deleteThisStudent" :disabled="cls?.archived">Delete</button>
 | 
				
			||||||
      <button type="button" @click="weekOverviewDialog?.showModal()" :disabled="lastWeeksEntries.length < 1">Week
 | 
					      <button type="button" @click="weekOverviewDialog?.showModal()" :disabled="lastWeeksEntries.length < 1">Week
 | 
				
			||||||
        overview</button>
 | 
					        overview</button>
 | 
				
			||||||
 | 
					      <button type="button" @click="labelsDialog?.showModal()">Labels</button>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <table class="student-properties-table">
 | 
					    <table class="student-properties-table">
 | 
				
			||||||
| 
						 | 
					@ -154,6 +195,7 @@ function getFormattedDate(entry: Entry) {
 | 
				
			||||||
      <p>This <strong>cannot</strong> be undone!</p>
 | 
					      <p>This <strong>cannot</strong> be undone!</p>
 | 
				
			||||||
    </ConfirmDialog>
 | 
					    </ConfirmDialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Dialog that, when opened, shows an overview of the student's activity for the current week. -->
 | 
				
			||||||
    <dialog ref="weekOverviewDialog" method="dialog" class="weekly-overview-dialog">
 | 
					    <dialog ref="weekOverviewDialog" method="dialog" class="weekly-overview-dialog">
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <h2>This week's overview for <span v-text="student.name"></span></h2>
 | 
					        <h2>This week's overview for <span v-text="student.name"></span></h2>
 | 
				
			||||||
| 
						 | 
					@ -196,6 +238,26 @@ function getFormattedDate(entry: Entry) {
 | 
				
			||||||
        <button @click.prevent="weekOverviewDialog?.close()">Close</button>
 | 
					        <button @click.prevent="weekOverviewDialog?.close()">Close</button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </dialog>
 | 
					    </dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Dialog for editing a student's labels. -->
 | 
				
			||||||
 | 
					    <dialog ref="labelsDialog" method="dialog">
 | 
				
			||||||
 | 
					      <div>
 | 
				
			||||||
 | 
					        <h2>Labels</h2>
 | 
				
			||||||
 | 
					        <div v-for="label in labels" :key="label"
 | 
				
			||||||
 | 
					          style="display: flex; flex-direction: row; justify-content: space-between; margin: 0.75rem 0;">
 | 
				
			||||||
 | 
					          <span>{{ label }}</span>
 | 
				
			||||||
 | 
					          <span style="cursor: pointer;" @click="deleteLabel(label)">❌</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          <input type="text" v-model="newLabel" />
 | 
				
			||||||
 | 
					          <button type="button" :disabled="newLabel.trim().length === 0 || cls?.archived"
 | 
				
			||||||
 | 
					            @click="addLabel()">Add</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div>
 | 
				
			||||||
 | 
					        <button type="button" @click="labelsDialog?.close()">Close</button>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </dialog>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,8 @@ const COMMENT_CHECKLIST_ITEMS = {
 | 
				
			||||||
    "Out of Uniform",
 | 
					    "Out of Uniform",
 | 
				
			||||||
    "Use of wireless tech",
 | 
					    "Use of wireless tech",
 | 
				
			||||||
    "10+ minute bathroom pass",
 | 
					    "10+ minute bathroom pass",
 | 
				
			||||||
    "Not having laptop"
 | 
					    "Not having laptop",
 | 
				
			||||||
 | 
					    "Not in assigned seat"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "Behavior": [
 | 
					  "Behavior": [
 | 
				
			||||||
    "Talking out of turn",
 | 
					    "Talking out of turn",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,28 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { EntriesResponseStudent } from '@/api/classroom_compliance';
 | 
					import { APIError } from '@/api/base';
 | 
				
			||||||
defineProps<{
 | 
					import { ClassroomComplianceAPIClient, type EntriesResponseStudent } from '@/api/classroom_compliance';
 | 
				
			||||||
 | 
					import { useAuthStore } from '@/stores/auth';
 | 
				
			||||||
 | 
					import { onMounted, ref, type Ref } from 'vue';
 | 
				
			||||||
 | 
					const props = defineProps<{
 | 
				
			||||||
  student: EntriesResponseStudent,
 | 
					  student: EntriesResponseStudent,
 | 
				
			||||||
  classId: number,
 | 
					  classId: number,
 | 
				
			||||||
 | 
					  showLabels: boolean
 | 
				
			||||||
}>()
 | 
					}>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const authStore = useAuthStore()
 | 
				
			||||||
 | 
					const labels: Ref<string[]> = ref([])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  const api = new ClassroomComplianceAPIClient(authStore)
 | 
				
			||||||
 | 
					  api.getStudentLabels(props.classId, props.student.id).result
 | 
				
			||||||
 | 
					    .then(values => {
 | 
				
			||||||
 | 
					      if (values instanceof APIError) {
 | 
				
			||||||
 | 
					        console.error("Failed to get labels for student", props.student)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        labels.value = values
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <td class="student-name-cell" :class="{ 'student-removed': student.removed }">
 | 
					  <td class="student-name-cell" :class="{ 'student-removed': student.removed }">
 | 
				
			||||||
| 
						 | 
					@ -11,6 +30,11 @@ defineProps<{
 | 
				
			||||||
      class="student-link">
 | 
					      class="student-link">
 | 
				
			||||||
      {{ student.name }}
 | 
					      {{ student.name }}
 | 
				
			||||||
    </RouterLink>
 | 
					    </RouterLink>
 | 
				
			||||||
 | 
					    <span v-if="showLabels">
 | 
				
			||||||
 | 
					      <span v-for="label in labels" :key="label" class="student-name-cell-label">
 | 
				
			||||||
 | 
					        {{ label }}
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
    <div v-if="classId !== student.classId" class="other-class-text">
 | 
					    <div v-if="classId !== student.classId" class="other-class-text">
 | 
				
			||||||
      <RouterLink :to="'/classroom-compliance/classes/' + student.classId">In another class</RouterLink>
 | 
					      <RouterLink :to="'/classroom-compliance/classes/' + student.classId">In another class</RouterLink>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
| 
						 | 
					@ -18,10 +42,19 @@ defineProps<{
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
.student-name-cell {
 | 
					.student-name-cell {
 | 
				
			||||||
  padding-left: 0.5em;
 | 
					  padding-left: 0.25rem;
 | 
				
			||||||
  text-align: left;
 | 
					  text-align: left;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.student-name-cell-label {
 | 
				
			||||||
 | 
					  font-size: 10px;
 | 
				
			||||||
 | 
					  background-color: rgb(121, 99, 3);
 | 
				
			||||||
 | 
					  border: 1px solid gray;
 | 
				
			||||||
 | 
					  border-radius: 0.25rem;
 | 
				
			||||||
 | 
					  padding: 0.05rem 0.2rem;
 | 
				
			||||||
 | 
					  margin: 0 0.1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.student-link {
 | 
					.student-link {
 | 
				
			||||||
  text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
  color: inherit;
 | 
					  color: inherit;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue