ui: Working on works
This commit is contained in:
parent
b9acaa798b
commit
197c23736d
@ -10,7 +10,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed ui/build/* ui/build/_app/* ui/build/_app/assets/pages/* ui/build/_app/pages/* ui/build/_app/pages/grades/* ui/build/_app/pages/surveys/* ui/build/_app/pages/surveys/_sid_/* ui/build/_app/pages/surveys/_sid_/responses/* ui/build/_app/pages/users/* ui/build/_app/pages/users/_uid_/* ui/build/_app/pages/users/_uid_/surveys/*
|
//go:embed ui/build/* ui/build/_app/* ui/build/_app/assets/pages/* ui/build/_app/pages/* ui/build/_app/pages/grades/* ui/build/_app/pages/surveys/* ui/build/_app/pages/surveys/_sid_/* ui/build/_app/pages/surveys/_sid_/responses/* ui/build/_app/pages/users/* ui/build/_app/pages/users/_uid_/* ui/build/_app/pages/users/_uid_/surveys/* ui/build/_app/pages/works/_wid_/* ui/build/_app/pages/works/*
|
||||||
var _assets embed.FS
|
var _assets embed.FS
|
||||||
|
|
||||||
var Assets http.FileSystem
|
var Assets http.FileSystem
|
||||||
|
@ -59,6 +59,8 @@ func init() {
|
|||||||
Router().GET("/surveys/*_", serveOrReverse("/"))
|
Router().GET("/surveys/*_", serveOrReverse("/"))
|
||||||
Router().GET("/users", serveOrReverse("/"))
|
Router().GET("/users", serveOrReverse("/"))
|
||||||
Router().GET("/users/*_", serveOrReverse("/"))
|
Router().GET("/users/*_", serveOrReverse("/"))
|
||||||
|
Router().GET("/works", serveOrReverse("/"))
|
||||||
|
Router().GET("/works/*_", serveOrReverse("/"))
|
||||||
Router().GET("/css/*_", serveOrReverse(""))
|
Router().GET("/css/*_", serveOrReverse(""))
|
||||||
Router().GET("/fonts/*_", serveOrReverse(""))
|
Router().GET("/fonts/*_", serveOrReverse(""))
|
||||||
Router().GET("/img/*_", serveOrReverse(""))
|
Router().GET("/img/*_", serveOrReverse(""))
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
import { getSurveys } from '../lib/surveys';
|
import { getSurveys } from '../lib/surveys';
|
||||||
import { getScore } from '../lib/users';
|
import { getScore } from '../lib/users';
|
||||||
|
|
||||||
let req_surveys = getSurveys();
|
export let allworks = false;
|
||||||
|
|
||||||
|
let req_surveys = getSurveys(allworks);
|
||||||
export let direct = null;
|
export let direct = null;
|
||||||
|
|
||||||
req_surveys.then((surveys) => {
|
req_surveys.then((surveys) => {
|
||||||
@ -17,6 +19,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function gotoSurvey(survey) {
|
||||||
|
if (survey.kind === "w") {
|
||||||
|
goto(`works/${survey.id}`);
|
||||||
|
} else if (survey.direct != null) {
|
||||||
|
goto(`surveys/${survey.id}/live`);
|
||||||
|
} else if ($user.is_admin) {
|
||||||
|
goto(`surveys/${survey.id}/responses`);
|
||||||
|
} else {
|
||||||
|
goto(`surveys/${survey.id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<table class="table table-striped table-hover mb-0">
|
<table class="table table-striped table-hover mb-0">
|
||||||
@ -38,7 +52,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{:then surveys}
|
{:then surveys}
|
||||||
<tbody style="cursor: pointer;">
|
<tbody style="cursor: pointer;">
|
||||||
{#each surveys as survey, sid (survey.id)}
|
{#each surveys as survey, sid (survey.kind + survey.id)}
|
||||||
{#if (survey.shown || survey.direct != null || ($user && $user.is_admin)) && (!$user || (!$user.was_admin || $user.promo == survey.promo) || $user.is_admin)}
|
{#if (survey.shown || survey.direct != null || ($user && $user.is_admin)) && (!$user || (!$user.was_admin || $user.promo == survey.promo) || $user.is_admin)}
|
||||||
{#if $user && $user.is_admin && (sid == 0 || surveys[sid-1].promo != survey.promo)}
|
{#if $user && $user.is_admin && (sid == 0 || surveys[sid-1].promo != survey.promo)}
|
||||||
<tr class="bg-info text-light">
|
<tr class="bg-info text-light">
|
||||||
@ -47,7 +61,7 @@
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
{/if}
|
{/if}
|
||||||
<tr on:click={e => goto(survey.direct != null ?`surveys/${survey.id}/live`:$user.is_admin?`surveys/${survey.id}/responses`:`surveys/${survey.id}`)}>
|
<tr on:click={e => gotoSurvey(survey)}>
|
||||||
<td>
|
<td>
|
||||||
{#if !survey.shown}<i class="bi bi-eye-slash-fill" title="Ce questionnaire n'est pas affiché aux étudiants"></i>{/if}
|
{#if !survey.shown}<i class="bi bi-eye-slash-fill" title="Ce questionnaire n'est pas affiché aux étudiants"></i>{/if}
|
||||||
{survey.title}
|
{survey.title}
|
||||||
@ -55,12 +69,12 @@
|
|||||||
<SurveyBadge {survey} class="float-end" />
|
<SurveyBadge {survey} class="float-end" />
|
||||||
</td>
|
</td>
|
||||||
{#if survey.startAvailability() > Date.now()}
|
{#if survey.startAvailability() > Date.now()}
|
||||||
<td>
|
<td title="Disponible à partir du {survey.start_availability}">
|
||||||
<DateFormat date={survey.start_availability} dateStyle="medium" timeStyle="medium" />
|
<DateFormat date={survey.start_availability} dateStyle="medium" timeStyle="medium" />
|
||||||
<i class="bi bi-arrow-bar-right"></i>
|
<i class="bi bi-arrow-bar-right"></i>
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td>
|
<td title="Sera fermé le {survey.start_availability}">
|
||||||
<i class="bi bi-arrow-bar-left"></i>
|
<i class="bi bi-arrow-bar-left"></i>
|
||||||
<DateFormat date={survey.end_availability} dateStyle="medium" timeStyle="medium" />
|
<DateFormat date={survey.end_availability} dateStyle="medium" timeStyle="medium" />
|
||||||
</td>
|
</td>
|
||||||
|
135
ui/src/components/WorkAdmin.svelte
Normal file
135
ui/src/components/WorkAdmin.svelte
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
import { ToastsStore } from '../stores/toasts';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
export let work = null;
|
||||||
|
|
||||||
|
function saveWork() {
|
||||||
|
work.save().then((response) => {
|
||||||
|
dispatch('saved', response);
|
||||||
|
}, (error) => {
|
||||||
|
ToastsStore.addErrorToast({
|
||||||
|
msg: error.errmsg,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteWork() {
|
||||||
|
work.delete().then((response) => {
|
||||||
|
goto(`works`);
|
||||||
|
}, (error) => {
|
||||||
|
ToastsStore.addErrorToast({
|
||||||
|
msg: error.errmsg,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function duplicateWork() {
|
||||||
|
work.duplicate().then((response) => {
|
||||||
|
goto(`works/${response.id}`);
|
||||||
|
}).catch((error) => {
|
||||||
|
ToastsStore.addErrorToast({
|
||||||
|
msg: error.errmsg,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={saveWork}>
|
||||||
|
|
||||||
|
{#if work.id}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="title" class="col-form-label col-form-label-sm">Identifiant du travail</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text" class="form-control-plaintext form-control-sm" id="title" value={work.id}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="title" class="col-form-label col-form-label-sm">Titre du travail</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text" class="form-control form-control-sm" id="title" bind:value={work.title}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="promo" class="col-form-label col-form-label-sm">Promo</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8 col-md-4 col-lg-2">
|
||||||
|
<input type="number" step="1" min="0" max="2068" class="form-control form-control-sm" id="promo" bind:value={work.promo}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="group" class="col-form-label col-form-label-sm">Restreindre au groupe</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8 col-md-4 col-lg-2">
|
||||||
|
<input class="form-control form-control-sm" id="group" bind:value={work.group}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="submissionurl" class="col-form-label col-form-label-sm">URL validation la soumission</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8 col-md-4 col-lg-2">
|
||||||
|
<input class="form-control form-control-sm" id="submissionurl" bind:value={work.submission_url}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="start_availability" class="col-form-label col-form-label-sm">Date de début</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text" class="form-control form-control-sm" id="start_availability" bind:value={work.start_availability}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 text-sm-end">
|
||||||
|
<label for="end_availability" class="col-form-label col-form-label-sm">Date de fin</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<input type="text" class="form-control form-control-sm" id="end_availability" bind:value={work.end_availability}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row row-cols-3 mx-1 my-2">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="shown" bind:checked={work.shown}>
|
||||||
|
<label class="form-check-label" for="shown">
|
||||||
|
Afficher le travail
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="corrected" bind:checked={work.corrected}>
|
||||||
|
<label class="form-check-label" for="corrected">
|
||||||
|
Marqué comme corrigé
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||||
|
{#if work.id}
|
||||||
|
<button type="button" class="btn btn-danger" on:click={deleteWork}>Supprimer</button>
|
||||||
|
<button type="button" class="btn btn-secondary" on:click={duplicateWork}>Dupliquer avec ces nouveaux paramètres</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
@ -1,8 +1,10 @@
|
|||||||
import { getQuestions } from './questions';
|
import { getQuestions } from './questions';
|
||||||
import { Response } from './response';
|
import { Response } from './response';
|
||||||
|
import { Work } from './works';
|
||||||
|
|
||||||
export class Survey {
|
export class Survey {
|
||||||
constructor(res) {
|
constructor(res) {
|
||||||
|
this.kind = "s";
|
||||||
if (res) {
|
if (res) {
|
||||||
this.update(res);
|
this.update(res);
|
||||||
}
|
}
|
||||||
@ -126,10 +128,19 @@ export class Survey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSurveys() {
|
export async function getSurveys(allworks) {
|
||||||
const res = await fetch(`api/surveys`, {headers: {'Accept': 'application/json'}})
|
const res = await fetch(allworks?`api/all_works`:`api/surveys`, {headers: {'Accept': 'application/json'}})
|
||||||
if (res.status == 200) {
|
if (res.status == 200) {
|
||||||
return (await res.json()).map((s) => new Survey(s));
|
if (allworks) {
|
||||||
|
return (await res.json()).map((s) => {
|
||||||
|
if (s.kind == "survey")
|
||||||
|
return new Survey(s);
|
||||||
|
else
|
||||||
|
return new Work(s);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return (await res.json()).map((s) => new Survey(s));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error((await res.json()).errmsg);
|
throw new Error((await res.json()).errmsg);
|
||||||
}
|
}
|
||||||
|
108
ui/src/lib/works.js
Normal file
108
ui/src/lib/works.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
export class Work {
|
||||||
|
constructor(res) {
|
||||||
|
this.kind = "w";
|
||||||
|
if (res) {
|
||||||
|
this.update(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update({ id, title, promo, group, shown, submission_url, corrected, start_availability, end_availability }) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.promo = promo;
|
||||||
|
this.group = group;
|
||||||
|
this.shown = shown;
|
||||||
|
this.submission_url = submission_url;
|
||||||
|
this.corrected = corrected;
|
||||||
|
if (this.start_availability != start_availability) {
|
||||||
|
this.start_availability = start_availability;
|
||||||
|
delete this.__start_availability;
|
||||||
|
}
|
||||||
|
if (this.end_availability != end_availability) {
|
||||||
|
this.end_availability = end_availability;
|
||||||
|
delete this.__end_availability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startAvailability() {
|
||||||
|
if (!this.__start_availability) {
|
||||||
|
this.__start_availability = new Date(this.start_availability)
|
||||||
|
}
|
||||||
|
return this.__start_availability
|
||||||
|
}
|
||||||
|
|
||||||
|
endAvailability() {
|
||||||
|
if (!this.__end_availability) {
|
||||||
|
this.__end_availability = new Date(this.end_availability)
|
||||||
|
}
|
||||||
|
return this.__end_availability
|
||||||
|
}
|
||||||
|
|
||||||
|
isFinished() {
|
||||||
|
return this.endAvailability() < new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
const res = await fetch(this.id?`api/works/${this.id}`:'api/works', {
|
||||||
|
method: this.id?'PUT':'POST',
|
||||||
|
headers: {'Accept': 'application/json'},
|
||||||
|
body: JSON.stringify(this),
|
||||||
|
});
|
||||||
|
if (res.status == 200) {
|
||||||
|
const data = await res.json()
|
||||||
|
this.update(data);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error((await res.json()).errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async duplicate() {
|
||||||
|
if (this.id) {
|
||||||
|
const oldSurveyId = this.id;
|
||||||
|
delete this.id;
|
||||||
|
const res = await fetch(`api/works`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Accept': 'application/json'},
|
||||||
|
body: JSON.stringify(this),
|
||||||
|
});
|
||||||
|
if (res.status == 200) {
|
||||||
|
return await res.json();
|
||||||
|
} else {
|
||||||
|
throw new Error((await res.json()).errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete() {
|
||||||
|
if (this.id) {
|
||||||
|
const res = await fetch(`api/works/${this.id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {'Accept': 'application/json'},
|
||||||
|
});
|
||||||
|
if (res.status == 200) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error((await res.json()).errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWorks() {
|
||||||
|
const res = await fetch(`api/works`, {headers: {'Accept': 'application/json'}})
|
||||||
|
if (res.status == 200) {
|
||||||
|
return (await res.json()).map((s) => new Work(s));
|
||||||
|
} else {
|
||||||
|
throw new Error((await res.json()).errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWork(wid) {
|
||||||
|
const res = await fetch(`api/works/${wid}`, {headers: {'Accept': 'application/json'}})
|
||||||
|
if (res.status == 200) {
|
||||||
|
return new Work(await res.json());
|
||||||
|
} else {
|
||||||
|
throw new Error((await res.json()).errmsg);
|
||||||
|
}
|
||||||
|
}
|
@ -89,6 +89,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{#if $user && $user.is_admin}
|
{#if $user && $user.is_admin}
|
||||||
|
<li class="nav-item"><a class="nav-link" class:active={rroute === 'works'} href="works">Travaux</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" class:active={rroute === 'users'} href="users">Étudiants</a></li>
|
<li class="nav-item"><a class="nav-link" class:active={rroute === 'users'} href="users">Étudiants</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
<li class="nav-item"><a class="nav-link" href="virli" target="_self">VIRLI</a></li>
|
<li class="nav-item"><a class="nav-link" href="virli" target="_self">VIRLI</a></li>
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
Vous devez <a href="auth/CRI" target="_self">vous identifier</a> pour accéder au contenu.
|
Vous devez <a href="auth/CRI" target="_self">vous identifier</a> pour accéder au contenu.
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
<SurveyList bind:direct={direct} />
|
<SurveyList allworks bind:direct={direct} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
39
ui/src/routes/works/[wid]/__layout.svelte
Normal file
39
ui/src/routes/works/[wid]/__layout.svelte
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script context="module">
|
||||||
|
import { getWork } from '../../../lib/works';
|
||||||
|
|
||||||
|
export async function load({ params, stuff }) {
|
||||||
|
const work = getWork(params.wid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
work,
|
||||||
|
},
|
||||||
|
stuff: {
|
||||||
|
...stuff,
|
||||||
|
work,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
export let work;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await work}
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||||
|
<span>Chargement du rendu …</span>
|
||||||
|
</div>
|
||||||
|
{:then}
|
||||||
|
<slot></slot>
|
||||||
|
{:catch error}
|
||||||
|
<div class="text-center">
|
||||||
|
<h2>
|
||||||
|
<a href="works/" class="text-muted" style="text-decoration: none"><</a>
|
||||||
|
Travail introuvable
|
||||||
|
</h2>
|
||||||
|
<span>{error}</span>
|
||||||
|
</div>
|
||||||
|
{/await}
|
34
ui/src/routes/works/[wid]/index.svelte
Normal file
34
ui/src/routes/works/[wid]/index.svelte
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script context="module">
|
||||||
|
import { getWork } from '../../../lib/works';
|
||||||
|
|
||||||
|
export async function load({ params, stuff }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
work: stuff.work,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
import { user } from '../../../stores/user';
|
||||||
|
import SurveyBadge from '../../../components/SurveyBadge.svelte';
|
||||||
|
import WorkAdmin from '../../../components/WorkAdmin.svelte';
|
||||||
|
|
||||||
|
export let work = null;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await work then w}
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<h2>
|
||||||
|
<a href="works/" class="text-muted" style="text-decoration: none"><</a>
|
||||||
|
{w.title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if $user && $user.is_admin}
|
||||||
|
<WorkAdmin work={w} on:saved={() => edit = false} />
|
||||||
|
{/if}
|
||||||
|
{/await}
|
99
ui/src/routes/works/index.svelte
Normal file
99
ui/src/routes/works/index.svelte
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<script>
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
import { user } from '../../stores/user';
|
||||||
|
import DateFormat from '../../components/DateFormat.svelte';
|
||||||
|
import SurveyBadge from '../../components/SurveyBadge.svelte';
|
||||||
|
import { getWorks } from '../../lib/works';
|
||||||
|
import { getPromos } from '../../lib/users';
|
||||||
|
import { getScore } from '../../lib/users';
|
||||||
|
|
||||||
|
let filterPromo = "";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $user && $user.is_admin}
|
||||||
|
<a href="works/new" class="btn btn-primary ml-1 float-end" title="Ajouter un travail">
|
||||||
|
<i class="bi bi-plus"></i>
|
||||||
|
</a>
|
||||||
|
{#await getPromos() then promos}
|
||||||
|
<div class="float-end me-2">
|
||||||
|
<select class="form-select" bind:value={filterPromo}>
|
||||||
|
<option value="">-</option>
|
||||||
|
{#each promos as promo, pid (pid)}
|
||||||
|
<option value={promo}>{promo}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
<h2>
|
||||||
|
Travaux
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{#await getWorks()}
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border text-danger mx-3" role="status"></div>
|
||||||
|
<span>Chargement des travaux …</span>
|
||||||
|
</div>
|
||||||
|
{:then works}
|
||||||
|
<table class="table table-sm table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Intitulé</th>
|
||||||
|
<th>Date</th>
|
||||||
|
{#if $user}
|
||||||
|
<th>Score</th>
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody style="cursor: pointer;">
|
||||||
|
{#each works as work, wid (work.id)}
|
||||||
|
{#if (work.shown || ($user && $user.is_admin)) && (!$user || (!$user.was_admin || $user.promo == work.promo) || $user.is_admin)}
|
||||||
|
{#if $user && $user.is_admin && (wid == 0 || works[wid-1].promo != work.promo)}
|
||||||
|
<tr class="bg-info text-light">
|
||||||
|
<th colspan="5" class="fw-bold">
|
||||||
|
{work.promo}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
<tr on:click={e => goto(`works/${work.id}`)}>
|
||||||
|
<td>
|
||||||
|
{#if !work.shown}<i class="bi bi-eye-slash-fill" title="Ce travail n'est pas affiché aux étudiants"></i>{/if}
|
||||||
|
{work.title}
|
||||||
|
{#if work.group}<span class="badge bg-secondary">{work.group}</span>{/if}
|
||||||
|
<SurveyBadge survey={work} class="float-end" />
|
||||||
|
</td>
|
||||||
|
{#if work.startAvailability() > Date.now()}
|
||||||
|
<td title="Ce travail sera disponible à partir du {work.start_availability}">
|
||||||
|
<DateFormat date={work.start_availability} dateStyle="medium" timeStyle="medium" />
|
||||||
|
<i class="bi bi-arrow-bar-right"></i>
|
||||||
|
</td>
|
||||||
|
{:else}
|
||||||
|
<td title="La date de rendu de ce travail est établie au {work.end_availability}">
|
||||||
|
<i class="bi bi-arrow-bar-left"></i>
|
||||||
|
<DateFormat date={work.end_availability} dateStyle="medium" timeStyle="medium" />
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
{#if $user}
|
||||||
|
{#if !work.corrected}
|
||||||
|
<td>N/A</td>
|
||||||
|
{:else}
|
||||||
|
<td>
|
||||||
|
{#await getScore(work)}
|
||||||
|
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||||
|
{:then score}
|
||||||
|
{score.score}
|
||||||
|
{/await}
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:catch error}
|
||||||
|
<div class="text-center text-danger">
|
||||||
|
{error.message}
|
||||||
|
</div>
|
||||||
|
{/await}
|
22
ui/src/routes/works/new.svelte
Normal file
22
ui/src/routes/works/new.svelte
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script>
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
import { user } from '../../stores/user';
|
||||||
|
import WorkAdmin from '../../components/WorkAdmin.svelte';
|
||||||
|
import SurveyBadge from '../../components/SurveyBadge.svelte';
|
||||||
|
import { Work } from '../../lib/works';
|
||||||
|
|
||||||
|
let work = new Work();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<h2>
|
||||||
|
<a href="works/" class="text-muted" style="text-decoration: none"><</a>
|
||||||
|
Nouveau travail
|
||||||
|
</h2>
|
||||||
|
<SurveyBadge class="ms-2" survey={work} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if $user && $user.is_admin}
|
||||||
|
<WorkAdmin {work} on:saved={(e) => { goto(`works/${e.detail.id}`)}} />
|
||||||
|
{/if}
|
Reference in New Issue
Block a user