Commit b967acc7 authored by Yechang's avatar Yechang
Browse files

submissions fields

parent b61af28a
Loading
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
<script lang="ts">
	export let title: string;
	export let href: string;
</script>

<a {href} class="font-medium text-blue-600 hover:underline dark:text-blue-500">{title}</a>
+36 −0
Original line number Diff line number Diff line
<script lang="ts">
	import * as HoverCard from '$lib/components/ui/hover-card';
	import * as Avatar from '$lib/components/ui/avatar';

	export let image: string;
	export let nickname: string;
	export let email: string;
</script>

<HoverCard.Root>
	<!-- <HoverCard.Trigger
					href="https://github.com/sveltejs"
					target="_blank"
					rel="noreferrer noopener"
					class="rounded-sm underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-8 focus-visible:outline-black"
				> -->
	<HoverCard.Trigger>
		<slot />
	</HoverCard.Trigger>
	<HoverCard.Content class="w-80">
		<div class="flex justify-between space-x-4">
			<Avatar.Root>
				<Avatar.Image src={image} />
				<Avatar.Fallback>{nickname}</Avatar.Fallback>
			</Avatar.Root>
			<div class="space-y-1">
				<h4 class="text-sm font-semibold">{nickname}</h4>
				<p class="text-sm">{email}</p>
				<!-- <div class="flex items-center pt-2">
								<Calendar class="mr-2 h-4 w-4 opacity-70" />{' '}
								<span class="text-xs text-muted-foreground"> Joined September 2022 </span>
							</div> -->
			</div>
		</div>
	</HoverCard.Content>
</HoverCard.Root>
+1 −1
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@
				}}
			>
				<Download class="mr-2 h-4 w-4" />
				Create
				Export
			</Button>
		</div>
	</div>
+5 −1
Original line number Diff line number Diff line
import { redirect } from '@sveltejs/kit';
import type { PageParentData, PageServerLoad } from './$types';
import { getAssignmentsByClassId } from '$lib/server/db/assignment';
import _ from 'lodash';
import { db } from '$lib/server/db';

@@ -17,6 +16,11 @@ export const load = (async ({ parent }) => {
			problem: {
				classId: currentClass.id
			}
		},
		include: {
			problem: true,
			submitter: true,
			feedback: true
		}
	});
	return {
+183 −46
Original line number Diff line number Diff line
<script lang="ts">
	import { goto } from '$app/navigation';
	import type { PageData } from './$types';
	import Link from '$lib/components/link.svelte';

	import { createTable, DataBodyRow, Render, Subscribe } from 'svelte-headless-table';
	import { createRender, createTable, DataBodyRow, Render, Subscribe } from 'svelte-headless-table';

	import { readable } from 'svelte/store';
	import * as Table from '$lib/components/ui/table';
	import _ from 'lodash';
	import { Button } from '$lib/components/ui/button';
	import { Input } from '$lib/components/ui/input';
	import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
	import { CaretSort, ChevronDown } from 'svelte-radix';
	import { cn } from '$lib/utils';
	import {
		addHiddenColumns,
		addPagination,
		addSelectedRows,
		addSortBy,
		addTableFilter
	} from 'svelte-headless-table/plugins';
	import Card from '$lib/components/user/card.svelte';

	export let data: PageData;
	const table = createTable(readable(data.submissions));
	const table = createTable(readable(data.submissions), {
		sort: addSortBy({ disableMultiSort: true }),
		page: addPagination(),
		filter: addTableFilter({
			fn: ({ filterValue, value }) => value.includes(filterValue)
		}),
		// select: addSelectedRows(),
		hide: addHiddenColumns()
	});
	const columns = table.createColumns([
		table.column({
			accessor: 'id',
			header: 'ID'
		}),
		table.column({
			accessor: 'problemId',
			header: 'Problem'
			accessor: (item) => ({ id: item.id, name: item.name }),
			header: 'Name',
			cell: ({ value }) =>
				createRender(Link, {
					title: `${value.name}`,
					href: `/submission/${value.id}/`
				}),
		}),
		table.column({
			accessor: 'problem',
			header: 'Problem',
			cell: ({ value }) =>
				createRender(Link, {
					title: `${value.title}`,
					href: `/problem/${value.id}/`
				})
		}),
		table.column({
			accessor: 'submitter',
			header: 'Submitter',
			cell: ({ value }) => createRender(Card, value).slot(`${value.sustechId}`),
			plugins: {
				filter: {
					getFilterValue(value) {
						return value.sustechId;
					}
				}
			}
		}),
		table.column({
			accessor: 'name',
			header: 'Name'
			accessor: 'feedback',
			header: 'Score',
			cell: ({ value }) => (_.isNil(value) ? 'Pending' : value.score)
		}),
		table.column({
			accessor: ({ id }) => id,
@@ -29,8 +78,25 @@
		})
	]);

	const { headerRows, pageRows, tableAttrs, tableBodyAttrs } = table.createViewModel(columns);
	$: role = data.currentClass.role;
	const { headerRows, pageRows, tableAttrs, tableBodyAttrs, flatColumns, pluginStates, rows } =
		table.createViewModel(columns);

	const { sortKeys } = pluginStates.sort;

	const { hiddenColumnIds } = pluginStates.hide;
	const ids = flatColumns.map((c) => c.id);
	let hideForId = Object.fromEntries(ids.map((id) => [id, true]));

	$: $hiddenColumnIds = Object.entries(hideForId)
		.filter(([, hide]) => !hide)
		.map(([id]) => id);

	const { hasNextPage, hasPreviousPage, pageIndex } = pluginStates.page;
	const { filterValue } = pluginStates.filter;

	// const { selectedDataIds } = pluginStates.select;

	const hideableCols = ['status', 'email', 'amount'];
</script>

<div class="h-full px-4 py-6 lg:px-8">
@@ -38,6 +104,31 @@
		<h2 class="text-3xl font-bold tracking-tight">Submissions</h2>
	</div>
	<div class="space-y-4 py-6">
		<div class="w-full">
			<div class="mb-4 flex items-center gap-4">
				<Input
					class="max-w-sm"
					placeholder="Filter students..."
					type="text"
					bind:value={$filterValue}
				/>
				<!-- <DropdownMenu.Root>
					<DropdownMenu.Trigger asChild let:builder>
						<Button variant="outline" class="ml-auto" builders={[builder]}>
							Columns <ChevronDown class="ml-2 h-4 w-4" />
						</Button>
					</DropdownMenu.Trigger>
					<DropdownMenu.Content>
						{#each flatColumns as col}
							{#if hideableCols.includes(col.id)}
								<DropdownMenu.CheckboxItem bind:checked={hideForId[col.id]}>
									{col.header}
								</DropdownMenu.CheckboxItem>
							{/if}
						{/each}
					</DropdownMenu.Content>
				</DropdownMenu.Root> -->
			</div>
			<div class="rounded-md border">
				<Table.Root {...$tableAttrs}>
					<Table.Header>
@@ -45,9 +136,35 @@
							<Subscribe rowAttrs={headerRow.attrs()}>
								<Table.Row>
									{#each headerRow.cells as cell (cell.id)}
									<Subscribe attrs={cell.attrs()} let:attrs props={cell.props()}>
										<Table.Head {...attrs}>
										<Subscribe attrs={cell.attrs()} let:attrs props={cell.props()} let:props>
											<Table.Head {...attrs} class={cn('[&:has([role=checkbox])]:pl-3')}>
												{#if cell.id === 'amount'}
													<div class="text-right">
														<Render of={cell.render()} />
													</div>
												{:else if cell.id === 'feedback'}
													<Button variant="ghost" on:click={props.sort.toggle}>
														<Render of={cell.render()} />
														<CaretSort
															class={cn(
																$sortKeys[0]?.id === cell.id && 'text-foreground',
																'ml-2 h-4 w-4'
															)}
														/>
													</Button>
												{:else if cell.id === 'problem'}
													<Button variant="ghost" on:click={props.sort.toggle}>
														<Render of={cell.render()} />
														<CaretSort
															class={cn(
																$sortKeys[0]?.id === cell.id && 'text-foreground',
																'ml-2 h-4 w-4'
															)}
														/>
													</Button>
												{:else}
													<Render of={cell.render()} />
												{/if}
											</Table.Head>
										</Subscribe>
									{/each}
@@ -58,16 +175,18 @@
					<Table.Body {...$tableBodyAttrs}>
						{#each $pageRows as row (row.id)}
							<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
							<Table.Row
								{...rowAttrs}
								on:click={() => {
									goto(`/submission/${row instanceof DataBodyRow ? row.original.id : ''}`);
								}}
							>
								<Table.Row {...rowAttrs}>
									<!-- data-state={$selectedDataIds[row.id] && 'selected'}> -->
									{#each row.cells as cell (cell.id)}
										<Subscribe attrs={cell.attrs()} let:attrs>
										<Table.Cell {...attrs}>
											<Table.Cell class="[&:has([role=checkbox])]:pl-3" {...attrs}>
												{#if cell.id === 'amount'}
													<div class="text-right font-medium">
														<Render of={cell.render()} />
													</div>
												{:else}
													<Render of={cell.render()} />
												{/if}
											</Table.Cell>
										</Subscribe>
									{/each}
@@ -77,5 +196,23 @@
					</Table.Body>
				</Table.Root>
			</div>
			<div class="flex items-center justify-end space-x-2 py-4">
				<!-- <div class="flex-1 text-sm text-muted-foreground">
					{Object.keys($selectedDataIds).length} of {$rows.length} row(s) selected.
				</div> -->
				<Button
					variant="outline"
					size="sm"
					on:click={() => ($pageIndex = $pageIndex - 1)}
					disabled={!$hasPreviousPage}>Previous</Button
				>
				<Button
					variant="outline"
					size="sm"
					disabled={!$hasNextPage}
					on:click={() => ($pageIndex = $pageIndex + 1)}>Next</Button
				>
			</div>
		</div>
	</div>
</div>
Loading