Display task status in TaskDisplay
This commit is contained in:
parent
b20594d6b9
commit
af57e530f8
6 changed files with 88 additions and 20 deletions
|
@ -6,6 +6,7 @@
|
||||||
import type { TasksFile, TaskDescriptor } from "./task-loader";
|
import type { TasksFile, TaskDescriptor } from "./task-loader";
|
||||||
import { createNodesAndEdges } from "./graph-types";
|
import { createNodesAndEdges } from "./graph-types";
|
||||||
import { taskForce } from "./task-force";
|
import { taskForce } from "./task-force";
|
||||||
|
import { taskStatuses } from './task-status-cache'
|
||||||
import { grabTaskStates, isLoggedIn } from "./ksp-task-grabber";
|
import { grabTaskStates, isLoggedIn } from "./ksp-task-grabber";
|
||||||
import type { TaskStatus } from "./ksp-task-grabber"
|
import type { TaskStatus } from "./ksp-task-grabber"
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@
|
||||||
let clientHeight: number;
|
let clientHeight: number;
|
||||||
let clientWidth: number;
|
let clientWidth: number;
|
||||||
let svgElement: SVGElement;
|
let svgElement: SVGElement;
|
||||||
let taskStatuses = new Map<string, TaskStatus>();
|
|
||||||
|
|
||||||
// this prevents svelte from updating nodes and edges
|
// this prevents svelte from updating nodes and edges
|
||||||
// when we update nodes and edges
|
// when we update nodes and edges
|
||||||
|
@ -94,17 +94,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (isLoggedIn()) {
|
|
||||||
const cachedTaskStatuses = localStorage.getItem("taskStatuses-cache")
|
|
||||||
if (cachedTaskStatuses) {
|
|
||||||
try { taskStatuses = new Map(JSON.parse(cachedTaskStatuses)) } catch(e) { console.warn(e) }
|
|
||||||
}
|
|
||||||
grabTaskStates(tasks.tasks.map(t => t.id)).then(t => {
|
|
||||||
taskStatuses = t
|
|
||||||
localStorage.setItem("taskStatuses-cache", JSON.stringify(Array.from(t.entries())))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// start simulation and center view on create
|
// start simulation and center view on create
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// set center of the SVG at (0,0)
|
// set center of the SVG at (0,0)
|
||||||
|
@ -155,7 +144,7 @@
|
||||||
on:click={nodeClick(task.task)}
|
on:click={nodeClick(task.task)}
|
||||||
on:hoveringChange={nodeHover(task.task)}
|
on:hoveringChange={nodeHover(task.task)}
|
||||||
on:positionChange={() => { tasks = tasks; }}
|
on:positionChange={() => { tasks = tasks; }}
|
||||||
status={taskStatuses.get(task.id)}
|
status={$taskStatuses.get(task.id)}
|
||||||
draggingEnabled={nodeDraggingEnabled}
|
draggingEnabled={nodeDraggingEnabled}
|
||||||
on:dblclick={nodeDoubleClick(task.task)} />
|
on:dblclick={nodeDoubleClick(task.task)} />
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -83,11 +83,23 @@
|
||||||
fill: #69b3a2
|
fill: #69b3a2
|
||||||
}
|
}
|
||||||
.submitted ellipse {
|
.submitted ellipse {
|
||||||
fill: red
|
fill: red; /* TODO */
|
||||||
|
}
|
||||||
|
.solved ellipse {
|
||||||
|
fill: green; /* TODO */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<g on:mouseenter={enter} on:mouseleave={leave} on:click={click} on:mousedown={dragStart} on:mouseup={dragStop} on:mousemove={drag} class={status && status.submitted ? "submitted" : ""} on:dblclick={dblclick}>
|
<g on:mouseenter={enter}
|
||||||
|
on:mouseleave={leave}
|
||||||
|
on:click={click}
|
||||||
|
on:mousedown={dragStart}
|
||||||
|
on:mouseup={dragStop}
|
||||||
|
on:mousemove={drag}
|
||||||
|
on:dblclick={dblclick}
|
||||||
|
class={status == null ? "" :
|
||||||
|
status.solved ? "solved" :
|
||||||
|
status.submitted ? "submitted" : ""}>
|
||||||
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
|
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
|
||||||
<text
|
<text
|
||||||
bind:this={text_element}
|
bind:this={text_element}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<script type="ts">
|
<script type="ts">
|
||||||
import { grabAssignment, grabSolution } from "./ksp-task-grabber";
|
import { grabAssignment, grabSolution } from "./ksp-task-grabber";
|
||||||
|
import type { TaskStatus } from "./ksp-task-grabber";
|
||||||
import { nonNull } from './helpers'
|
import { nonNull } from './helpers'
|
||||||
import App from "./App.svelte";
|
import App from "./App.svelte";
|
||||||
|
import { taskStatuses } from "./task-status-cache";
|
||||||
export let taskId: string | null | undefined
|
export let taskId: string | null | undefined
|
||||||
|
|
||||||
export let showSolution: boolean = false
|
export let showSolution: boolean = false
|
||||||
|
@ -9,11 +11,26 @@ import App from "./App.svelte";
|
||||||
taskId
|
taskId
|
||||||
showSolution = false
|
showSolution = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let status: TaskStatus | undefined
|
||||||
|
$: if (taskId) status = $taskStatuses.get(taskId)
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.header div {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.header .status {
|
||||||
|
text-align: right;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -21,7 +38,22 @@ import App from "./App.svelte";
|
||||||
{#await grabAssignment(nonNull(taskId))}
|
{#await grabAssignment(nonNull(taskId))}
|
||||||
Načítám úlohu
|
Načítám úlohu
|
||||||
{:then task}
|
{:then task}
|
||||||
{@html task.titleHtml}
|
<div class="header">
|
||||||
|
<div class="title"><h3>{task.name}</h3></div>
|
||||||
|
|
||||||
|
<div class="status">
|
||||||
|
<p>
|
||||||
|
{task.id} | {task.points} bodů
|
||||||
|
{#if status && status.submitted}
|
||||||
|
{#if nonNull(status).solved}
|
||||||
|
| Vyřešeno 🥳
|
||||||
|
{:else}
|
||||||
|
| odevzdáno za {nonNull(status).points} bod{ "ů yyy"[nonNull(status).points] ?? "ů" }
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{@html task.description}
|
{@html task.description}
|
||||||
<div class="clearfloat" />
|
<div class="clearfloat" />
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { TasksFile } from "./task-loader";
|
import type { TasksFile } from "./task-loader";
|
||||||
|
import { refresh } from './task-status-cache'
|
||||||
|
|
||||||
export let promise: Promise<TasksFile>;
|
export let promise: Promise<TasksFile>;
|
||||||
|
|
||||||
|
@ -8,6 +9,7 @@
|
||||||
let err: any | null = null;
|
let err: any | null = null;
|
||||||
promise.then(
|
promise.then(
|
||||||
(d) => {
|
(d) => {
|
||||||
|
refresh(d.tasks.map(t => t.id))
|
||||||
data = d;
|
data = d;
|
||||||
},
|
},
|
||||||
(e) => {
|
(e) => {
|
||||||
|
|
|
@ -19,6 +19,7 @@ export type TaskStatus = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
submitted: boolean
|
submitted: boolean
|
||||||
|
solved: boolean
|
||||||
points: number
|
points: number
|
||||||
maxPoints: number
|
maxPoints: number
|
||||||
type: string
|
type: string
|
||||||
|
@ -80,15 +81,17 @@ function parseTask(startElementId: string, doc: HTMLDocument): TaskAssignmentDat
|
||||||
|
|
||||||
let e = titleElement
|
let e = titleElement
|
||||||
|
|
||||||
const titleMatch = /(\d-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?/.exec(e.innerText.trim())
|
const titleMatch = /^(\d+-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?$/.exec(e.innerText.trim())
|
||||||
if (!titleMatch) {
|
if (!titleMatch) {
|
||||||
var [_, id, name, __, points] = ["", startElementId, "Neznámé jméno úlohy", "", ""]
|
var [_, id, name, __, points] = ["", startElementId, "Neznámé jméno úlohy", "", ""]
|
||||||
} else {
|
} else {
|
||||||
var [_, id, name, __, points] = titleMatch
|
var [_, id, name, __, points] = titleMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e = e.nextElementSibling as HTMLElement
|
||||||
|
|
||||||
while (e.nextElementSibling &&
|
while (e.nextElementSibling &&
|
||||||
e.nextElementSibling?.tagName.toLowerCase() == "hr")
|
e.tagName.toLowerCase() == "hr")
|
||||||
e = e.nextElementSibling as HTMLElement
|
e = e.nextElementSibling as HTMLElement
|
||||||
|
|
||||||
while (!e.classList.contains("story") &&
|
while (!e.classList.contains("story") &&
|
||||||
|
@ -124,11 +127,12 @@ function parseTaskStatuses(doc: HTMLDocument): TaskStatus[] {
|
||||||
const type = r.cells[1].innerText.trim()
|
const type = r.cells[1].innerText.trim()
|
||||||
const name = r.cells[2].innerText.trim()
|
const name = r.cells[2].innerText.trim()
|
||||||
const pointsStr = r.cells[4].innerText.trim()
|
const pointsStr = r.cells[4].innerText.trim()
|
||||||
const pointsMatch = /(–|\d+) *\/ *(\d+)/.exec(pointsStr)
|
const pointsMatch = /((–|\.|\d)+) *\/ *(\d+)/.exec(pointsStr)
|
||||||
if (!pointsMatch) throw new Error()
|
if (!pointsMatch) throw new Error()
|
||||||
const points = +pointsMatch[1]
|
const points = +pointsMatch[1]
|
||||||
const maxPoints = +pointsMatch[2]
|
const maxPoints = +pointsMatch[2]
|
||||||
return { id, name, submitted, type, points, maxPoints }
|
const solved = r.classList.contains("zs-submitted")
|
||||||
|
return { id, name, submitted, type, points, maxPoints, solved }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
frontend/src/task-status-cache.ts
Normal file
29
frontend/src/task-status-cache.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { grabTaskStates, isLoggedIn} from "./ksp-task-grabber"
|
||||||
|
import type { TaskStatus } from "./ksp-task-grabber"
|
||||||
|
import { readable } from 'svelte/store';
|
||||||
|
|
||||||
|
let writeFn: (value: Map<string, TaskStatus>) => void = null!;
|
||||||
|
let lastVal = new Map<string, TaskStatus>()
|
||||||
|
if (isLoggedIn()) {
|
||||||
|
const cachedTaskStatuses = localStorage.getItem("taskStatuses-cache")
|
||||||
|
if (cachedTaskStatuses) {
|
||||||
|
lastVal = new Map(JSON.parse(cachedTaskStatuses))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const taskStatuses = readable(lastVal, write => {
|
||||||
|
writeFn = v => { lastVal = v; write(v); }
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(isLoggedIn())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function refresh(ids: string[]) {
|
||||||
|
if (!isLoggedIn()) return;
|
||||||
|
|
||||||
|
grabTaskStates(ids).then(t => {
|
||||||
|
const tt = Array.from(t.entries())
|
||||||
|
writeFn(new Map(Array.from(lastVal.entries()).concat(tt)))
|
||||||
|
localStorage.setItem("taskStatuses-cache", JSON.stringify(tt))
|
||||||
|
})
|
||||||
|
}
|
Reference in a new issue