From 1aa82bb2efdcbc4ccdc55e857de05c10e6ec6795 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 7 Nov 2022 01:00:04 +0100 Subject: [PATCH] qa: Back to the same situation --- qa/api/exercice.go | 13 +- qa/api/qa.go | 18 +- qa/ui/jsconfig.json | 2 +- qa/ui/src/lib/components/DateFormat.svelte | 17 ++ qa/ui/src/lib/components/ExerciceQA.svelte | 54 ++++++ qa/ui/src/lib/components/Header.svelte | 49 +++-- qa/ui/src/lib/components/MyExercices.svelte | 93 +++++++++ qa/ui/src/lib/components/MyTodo.svelte | 91 +++++++++ qa/ui/src/lib/components/QAItem.svelte | 183 ++++++++++++++++++ qa/ui/src/lib/components/QAItems.svelte | 95 +++++++++ qa/ui/src/lib/components/QANewItem.svelte | 76 ++++++++ qa/ui/src/lib/exercices.js | 66 +++++++ qa/ui/src/lib/qa.js | 127 ++++++++++++ qa/ui/src/lib/stores/auth.js | 29 +++ qa/ui/src/lib/stores/exercices.js | 39 ++++ qa/ui/src/lib/stores/themes.js | 39 ++++ qa/ui/src/lib/stores/todo.js | 26 +++ qa/ui/src/lib/themes.js | 39 ++++ qa/ui/src/lib/todo.js | 61 ++++++ qa/ui/src/routes/+layout.svelte | 8 +- qa/ui/src/routes/+page.svelte | 25 ++- qa/ui/src/routes/exercices/+page.svelte | 51 +++++ qa/ui/src/routes/exercices/[eid]/+page.svelte | 13 ++ qa/ui/src/routes/themes/+layout.svelte | 1 + qa/ui/src/routes/themes/+page.svelte | 54 ++++++ qa/ui/src/routes/themes/[tid]/+page.svelte | 76 ++++++++ .../routes/themes/[tid]/[eid]/+page.svelte | 13 ++ 27 files changed, 1336 insertions(+), 22 deletions(-) create mode 100644 qa/ui/src/lib/components/DateFormat.svelte create mode 100644 qa/ui/src/lib/components/ExerciceQA.svelte create mode 100644 qa/ui/src/lib/components/MyExercices.svelte create mode 100644 qa/ui/src/lib/components/MyTodo.svelte create mode 100644 qa/ui/src/lib/components/QAItem.svelte create mode 100644 qa/ui/src/lib/components/QAItems.svelte create mode 100644 qa/ui/src/lib/components/QANewItem.svelte create mode 100644 qa/ui/src/lib/exercices.js create mode 100644 qa/ui/src/lib/qa.js create mode 100644 qa/ui/src/lib/stores/auth.js create mode 100644 qa/ui/src/lib/stores/exercices.js create mode 100644 qa/ui/src/lib/stores/themes.js create mode 100644 qa/ui/src/lib/stores/todo.js create mode 100644 qa/ui/src/lib/themes.js create mode 100644 qa/ui/src/lib/todo.js create mode 100644 qa/ui/src/routes/exercices/+page.svelte create mode 100644 qa/ui/src/routes/exercices/[eid]/+page.svelte create mode 100644 qa/ui/src/routes/themes/+layout.svelte create mode 100644 qa/ui/src/routes/themes/+page.svelte create mode 100644 qa/ui/src/routes/themes/[tid]/+page.svelte create mode 100644 qa/ui/src/routes/themes/[tid]/[eid]/+page.svelte diff --git a/qa/api/exercice.go b/qa/api/exercice.go index 1303406b..1820a1cc 100644 --- a/qa/api/exercice.go +++ b/qa/api/exercice.go @@ -17,6 +17,8 @@ func declareExercicesRoutes(router *gin.RouterGroup) { exercicesRoutes := router.Group("/exercices/:eid") exercicesRoutes.Use(exerciceHandler) exercicesRoutes.GET("", showExercice) + + declareQARoutes(exercicesRoutes) } func exerciceHandler(c *gin.Context) { @@ -42,8 +44,15 @@ func exerciceHandler(c *gin.Context) { } func listExercices(c *gin.Context) { - // List all exercices - exercices, err := fic.GetExercices() + var exercices []*fic.Exercice + var err error + + if theme, ok := c.Get("theme"); ok { + exercices, err = theme.(*fic.Theme).GetExercices() + } else { + // List all exercices + exercices, err = fic.GetExercices() + } if err != nil { log.Println("Unable to GetExercices: ", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list exercices: %s", err.Error())}) diff --git a/qa/api/qa.go b/qa/api/qa.go index be6c03c8..f3ac4f24 100644 --- a/qa/api/qa.go +++ b/qa/api/qa.go @@ -12,8 +12,7 @@ import ( ) func declareQARoutes(router *gin.RouterGroup) { - exercicesRoutes := router.Group("/qa/:eid") - exercicesRoutes.Use(exerciceHandler) + exercicesRoutes := router.Group("/qa") exercicesRoutes.GET("", getExerciceQA) exercicesRoutes.POST("", createExerciceQA) @@ -30,12 +29,21 @@ func declareQARoutes(router *gin.RouterGroup) { } func qaHandler(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) var qa *fic.QAQuery - if qid, err := strconv.ParseInt(string(c.Param("qid")), 10, 64); err != nil { + + qid, err := strconv.ParseInt(string(c.Param("qid")), 10, 64) + if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad QA identifier."}) return - } else if qa, err = exercice.GetQAQuery(qid); err != nil { + } + + if exercice, ok := c.Get("exercice"); ok { + qa, err = exercice.(*fic.Exercice).GetQAQuery(qid) + } else { + qa, err = fic.GetQAQuery(qid) + } + + if err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "QA entry not found."}) return } diff --git a/qa/ui/jsconfig.json b/qa/ui/jsconfig.json index 3757b0e2..cea8c921 100644 --- a/qa/ui/jsconfig.json +++ b/qa/ui/jsconfig.json @@ -6,5 +6,5 @@ "$lib/*": ["src/lib/*"] } }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] } diff --git a/qa/ui/src/lib/components/DateFormat.svelte b/qa/ui/src/lib/components/DateFormat.svelte new file mode 100644 index 00000000..f2ac4525 --- /dev/null +++ b/qa/ui/src/lib/components/DateFormat.svelte @@ -0,0 +1,17 @@ + + +{formatDate(date, dateStyle, timeStyle)} diff --git a/qa/ui/src/lib/components/ExerciceQA.svelte b/qa/ui/src/lib/components/ExerciceQA.svelte new file mode 100644 index 00000000..907048f6 --- /dev/null +++ b/qa/ui/src/lib/components/ExerciceQA.svelte @@ -0,0 +1,54 @@ + + +

+ {exercice.title} + {#if $themes.length && $themesIdx[exercice.id_theme]} + + {$themesIdx[exercice.id_theme].name} + + {#if $themesIdx[exercice.id_theme].exercices && $themesIdx[exercice.id_theme].exercices[exercice.id]} +
+ + +
+ {/if} + Site du challenge + {/if} +

+ +
+
{@html exercice.statement}
+
{@html exercice.overview}
+
+ +
+ countCreation++} + /> + {#key countCreation} + + {/key} +
diff --git a/qa/ui/src/lib/components/Header.svelte b/qa/ui/src/lib/components/Header.svelte index 875c8733..6140d96d 100644 --- a/qa/ui/src/lib/components/Header.svelte +++ b/qa/ui/src/lib/components/Header.svelte @@ -1,4 +1,6 @@ @@ -30,25 +40,46 @@ diff --git a/qa/ui/src/lib/components/MyExercices.svelte b/qa/ui/src/lib/components/MyExercices.svelte new file mode 100644 index 00000000..efa385d2 --- /dev/null +++ b/qa/ui/src/lib/components/MyExercices.svelte @@ -0,0 +1,93 @@ + + +
+

Vos étapes

+ {#await my_exercicesP} + {:then} + + + + + + + + + {#each my_exercices as todo (todo.id)} + 0} + class:table-warning={todo.queriesNSolved > 0} + on:click={() => show(todo.id_exercice)} + > + + + + {/each} + +
DéfiRequêtes
+ {#if $exercicesIdx.length == 0 && $themesIdx.length == 0} + + {:else if $themesIdx[$exercicesIdx[todo.id_exercice]]} + + {$themesIdx[$exercicesIdx[todo.id_exercice].id_theme].name} + + – + {/if} + {#if $exercicesIdx.length == 0} + + {:else if $exercicesIdx[todo.id_exercice]} + {$exercicesIdx[todo.id_exercice].title} + {#if $exercicesIdx[todo.id_exercice].wip} + + {/if} + {/if} + + {#if todo.queries && todo.queries.length} + {todo.queriesNSolved} / {todo.queriesNClosed} + {:else} + 0 + {/if} +
+ {/await} +
diff --git a/qa/ui/src/lib/components/MyTodo.svelte b/qa/ui/src/lib/components/MyTodo.svelte new file mode 100644 index 00000000..fb4551f0 --- /dev/null +++ b/qa/ui/src/lib/components/MyTodo.svelte @@ -0,0 +1,91 @@ + + +
+

Étapes à tester et valider

+ {#await todos.refresh()} + {:then} + {#await exo_doneP} + {:then exo_done} + + + + + + + + + + {#each $todos as todo (todo.id)} + show(todo.id_exercice)} + > + + + + + {/each} + +
AvancementScénarioDéfi
+ {#if tododone[todo.id_exercice]} + Commenté + {#if !exo_done[todo.id_exercice] || exo_done[todo.id_exercice] != 'solved'} + mais pas testé/terminé + {/if} + {:else if exo_done[todo.id_exercice] && exo_done[todo.id_exercice] != 'access'} + À commenter + {:else} + À tester + {/if} + + {#if $exercicesIdx.length == 0 && $themesIdx.length == 0} + + {:else} + + {$themesIdx[$exercicesIdx[todo.id_exercice].id_theme].name} + + {/if} + + {#if $exercicesIdx.length == 0} + + {:else} + {$exercicesIdx[todo.id_exercice].title} + {#if $exercicesIdx[todo.id_exercice].wip} + + {/if} + {/if} +
+ {/await} + {/await} +
diff --git a/qa/ui/src/lib/components/QAItem.svelte b/qa/ui/src/lib/components/QAItem.svelte new file mode 100644 index 00000000..051e215b --- /dev/null +++ b/qa/ui/src/lib/components/QAItem.svelte @@ -0,0 +1,183 @@ + + + + +
+

{query_selected.subject}

+
+ {#if $auth && $auth.id_team == query_selected.id_team} + {#if query_selected.solved && !query_selected.closed} + + + {/if} + {#if !query_selected.solved} + + {/if} + {:else if $auth && !query_selected.solved} + + {/if} +
+
+
+ +
+
+
Qui ?
+
{query_selected.user} (team #{query_selected.id_team})
+ +
État
+
{query_selected.state}
+ +
Date de création
+
+ +
+ +
Date de résolution
+
+ {#if query_selected.solved} + + {:else} + - + {/if} +
+ +
Date de clôture
+
+ {#if query_selected.closed} + + {:else} + - + {/if} +
+
+
+ {#if $auth && $auth.id_team == query_selected.id_team} + + {/if} +
+
+ {#await query_commentsP} +
+ +
+ Chargement des commentaires en cours… +
+
+ {:then query_comments} + + {#each query_comments as comment (comment.id)} + + + + {/each} +
+ Le , {comment.user} a écrit : {comment.content} +
+ {/await} +
+ + + +
+ +
+
diff --git a/qa/ui/src/lib/components/QAItems.svelte b/qa/ui/src/lib/components/QAItems.svelte new file mode 100644 index 00000000..43cb1795 --- /dev/null +++ b/qa/ui/src/lib/components/QAItems.svelte @@ -0,0 +1,95 @@ + + +{#await queriesP} +{:then queries} + + + + {#each fields as field} + + {/each} + + + + {#if queries.length} + + {#each queries as q (q.id)} + query_selected = q} class:bg-warning={query_selected && q.id == query_selected.id}> + {#each fields as field} + + {/each} + + + {/each} + + {:else} + + + + + + {/if} +
+ { field } +
+ {@html q[field]} + + +
+ Aucune requête enregistrée +
+ + {#if query_selected} + + {/if} +{/await} diff --git a/qa/ui/src/lib/components/QANewItem.svelte b/qa/ui/src/lib/components/QANewItem.svelte new file mode 100644 index 00000000..e3268e1b --- /dev/null +++ b/qa/ui/src/lib/components/QANewItem.svelte @@ -0,0 +1,76 @@ + + +
+
+ Qu'avez-vous pensé de cette étape ? +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
diff --git a/qa/ui/src/lib/exercices.js b/qa/ui/src/lib/exercices.js new file mode 100644 index 00000000..aa9505de --- /dev/null +++ b/qa/ui/src/lib/exercices.js @@ -0,0 +1,66 @@ +export const fieldsExercices = ["title", "headline"]; + +export class Exercice { + constructor(res) { + if (res) { + this.update(res); + } + } + + update({ id, id_theme, title, wip, urlid, path, statement, overview, headline, finished, issue, issuekind, depend, gain, coefficient, videoURI, resolution, seealso }) { + this.id = id; + this.id_theme = id_theme; + this.title = title; + this.wip = wip + this.urlid = urlid; + this.path = path; + this.statement = statement; + this.overview = overview; + this.headline = headline; + this.finished = finished; + this.issue = issue; + this.issuekind = issuekind; + this.depend = depend; + this.gain = gain; + this.coefficient = coefficient; + this.videoURI = videoURI; + this.resolution = resolution; + this.seealso = seealso; + } +} + +export async function getExercices() { + const res = await fetch(`api/exercices`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return (await res.json()).map((t) => new Exercice(t)); + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getThemedExercices(tid) { + const res = await fetch(`api/themes/${tid}/exercices`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return (await res.json()).map((t) => new Exercice(t)); + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getExercice(eid) { + const res = await fetch(`api/exercices/${eid}`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return new Exercice(await res.json()); + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getThemedExercice(tid, eid) { + const res = await fetch(`api/themes/${tid}/exercices/${eid}`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return new Exercice(await res.json()); + } else { + throw new Error((await res.json()).errmsg); + } +} diff --git a/qa/ui/src/lib/qa.js b/qa/ui/src/lib/qa.js new file mode 100644 index 00000000..b0dec4ea --- /dev/null +++ b/qa/ui/src/lib/qa.js @@ -0,0 +1,127 @@ +export const QAStates = { + "ok": "OK", + "orthograph": "Orthographe et grammaire", + "issue-statement": "Pas compris", + "issue-flag": "Problème de flag", + "issue-mcq": "Problème de QCM/QCU", + "issue-hint": "Problème d'indice", + "issue-file": "Problème de fichier", + "issue": "Problème autre", + "suggest": "Suggestion", + "too-hard": "Trop dur", + "too-easy": "Trop facile", +}; + +export class QAQuery { + constructor(res) { + if (res) { + this.update(res); + } + } + + update({ id, id_exercice, id_team, user, creation, state, subject, solved, closed }) { + this.id = id; + this.id_team = id_team; + this.id_exercice = id_exercice; + this.user = user; + this.creation = creation; + this.state = state; + this.subject = subject; + this.solved = solved; + this.closed = closed; + } + + async delete() { + const res = await fetch(`api/exercices/${this.id_exercice}/qa/${this.id}`, { + method: 'DELETE', + headers: {'Accept': 'application/json'} + }); + if (res.status < 300) { + return true; + } else { + throw new Error((await res.json()).errmsg); + } + } + + async save() { + const res = await fetch(this.id?`api/exercices/${this.id_exercice}/qa/${this.id}`:`api/exercices/${this.id_exercice}/qa`, { + 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); + } + } +} + +export async function getExerciceQA(eid) { + const res = await fetch(`api/exercices/${eid}/qa`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + const data = await res.json(); + if (data == null) + return []; + return data.map((t) => new QAQuery(t)); + } else { + throw new Error((await res.json()).errmsg); + } +} + +export class QAComment { + constructor(res) { + if (res) { + this.update(res); + } + } + + update({ id, id_team, user, date, content }) { + this.id = id; + this.id_team = id_team; + this.user = user; + this.date = date; + this.content = content; + } + + async delete(qid) { + const res = await fetch(`api/qa/${qid}/comments/${this.id}`, { + method: 'DELETE', + headers: {'Accept': 'application/json'} + }); + if (res.status == 200) { + return true; + } else { + throw new Error((await res.json()).errmsg); + } + } + + async save(qid) { + const res = await fetch(this.id?`api/qa/${qid}/comments/${this.id}`:`api/qa/${qid}/comments`, { + 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); + } + } +} + +export async function getQAComments(qid) { + const res = await fetch(`api/qa/${qid}/comments`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + const data = await res.json(); + if (data == null) + return []; + return data.map((t) => new QAComment(t)); + } else { + throw new Error((await res.json()).errmsg); + } +} diff --git a/qa/ui/src/lib/stores/auth.js b/qa/ui/src/lib/stores/auth.js new file mode 100644 index 00000000..593682c3 --- /dev/null +++ b/qa/ui/src/lib/stores/auth.js @@ -0,0 +1,29 @@ +import { writable, derived } from 'svelte/store'; + +function createVersionStore() { + const { subscribe, set, update } = writable({"auth":null}); + + return { + subscribe, + + set: (v) => { + update((m) => Object.assign(m, v)); + }, + + update, + + refresh: async () => { + const version = await (await fetch('api/version', {headers: {'Accept': 'application/json'}})).json() + update((m) => version); + return version; + }, + }; + +} + +export const version = createVersionStore(); + +export const auth = derived( + version, + $version => $version.auth, +); diff --git a/qa/ui/src/lib/stores/exercices.js b/qa/ui/src/lib/stores/exercices.js new file mode 100644 index 00000000..72f8bcb0 --- /dev/null +++ b/qa/ui/src/lib/stores/exercices.js @@ -0,0 +1,39 @@ +import { writable, derived } from 'svelte/store'; + +import { getExercices } from '$lib/exercices' + +function createExercicesStore() { + const { subscribe, set, update } = writable([]); + + return { + subscribe, + + set: (v) => { + update((m) => Object.assign(m, v)); + }, + + update, + + refresh: async () => { + const list = await getExercices(); + update((m) => list); + return list; + }, + }; + +} + +export const exercices = createExercicesStore(); + +export const exercicesIdx = derived( + exercices, + $exercices => { + const exercices_idx = { }; + + for (const e of $exercices) { + exercices_idx[e.id] = e; + } + + return exercices_idx; + }, +); diff --git a/qa/ui/src/lib/stores/themes.js b/qa/ui/src/lib/stores/themes.js new file mode 100644 index 00000000..dcc4e4d9 --- /dev/null +++ b/qa/ui/src/lib/stores/themes.js @@ -0,0 +1,39 @@ +import { writable, derived } from 'svelte/store'; + +import { getThemes } from '$lib/themes' + +function createThemesStore() { + const { subscribe, set, update } = writable([]); + + return { + subscribe, + + set: (v) => { + update((m) => Object.assign(m, v)); + }, + + update, + + refresh: async () => { + const list = await getThemes(); + update((m) => list); + return list; + }, + }; + +} + +export const themes = createThemesStore(); + +export const themesIdx = derived( + themes, + $themes => { + const themes_idx = { }; + + for (const t of $themes) { + themes_idx[t.id] = t; + } + + return themes_idx; + }, +); diff --git a/qa/ui/src/lib/stores/todo.js b/qa/ui/src/lib/stores/todo.js new file mode 100644 index 00000000..0e806ea8 --- /dev/null +++ b/qa/ui/src/lib/stores/todo.js @@ -0,0 +1,26 @@ +import { writable } from 'svelte/store'; + +import { getQAWork } from '$lib/todo' + +function createTodosStore() { + const { subscribe, set, update } = writable([]); + + return { + subscribe, + + set: (v) => { + update((m) => Object.assign(m, v)); + }, + + update, + + refresh: async () => { + const list = await getQAWork(); + update((m) => list); + return list; + }, + }; + +} + +export const todos = createTodosStore(); diff --git a/qa/ui/src/lib/themes.js b/qa/ui/src/lib/themes.js new file mode 100644 index 00000000..87358491 --- /dev/null +++ b/qa/ui/src/lib/themes.js @@ -0,0 +1,39 @@ +export class Theme { + constructor(res) { + if (res) { + this.update(res); + } + } + + update({ id, name, urlid, path, authors, intro, headline, image, partner_img, partner_href, partner_txt }) { + this.id = id; + this.name = name; + this.urlid = urlid; + this.path = path; + this.authors = authors; + this.intro = intro; + this.headline = headline; + this.image = image; + this.partner_img = partner_img; + this.partner_href = partner_href; + this.partner_txt = partner_txt; + } +} + +export async function getThemes() { + const res = await fetch(`api/themes`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return (await res.json()).map((t) => new Theme(t)); + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getTheme(tid) { + const res = await fetch(`api/themes/${tid}`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return new Theme(await res.json()); + } else { + throw new Error((await res.json()).errmsg); + } +} diff --git a/qa/ui/src/lib/todo.js b/qa/ui/src/lib/todo.js new file mode 100644 index 00000000..69ddd5a7 --- /dev/null +++ b/qa/ui/src/lib/todo.js @@ -0,0 +1,61 @@ +import { QAQuery } from './qa'; + +export class QATodo { + constructor(res) { + if (res) { + this.update(res); + } + } + + update({ id, id_team, id_exercice }) { + this.id = id; + this.id_team = id_team; + this.id_exercice = id_exercice; + } +} + +export async function getQATodo() { + const res = await fetch(`api/qa_work.json`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return (await res.json()).map((t) => new QATodo(t)); + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getQAWork() { + const res = await fetch(`api/qa_mywork.json`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + const data = await res.json() + if (data) { + return data.map((t) => new QAQuery(t)); + } else { + return []; + } + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getQAView() { + const res = await fetch(`api/qa_myexercices.json`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + const data = await res.json() + if (data) { + return data.map((t) => new QATodo(t)); + } else { + return []; + } + } else { + throw new Error((await res.json()).errmsg); + } +} + +export async function getExerciceTested() { + const res = await fetch(`api/qa_exercices.json`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return await res.json(); + } else { + throw new Error((await res.json()).errmsg); + } +} diff --git a/qa/ui/src/routes/+layout.svelte b/qa/ui/src/routes/+layout.svelte index bb179759..d26588cd 100644 --- a/qa/ui/src/routes/+layout.svelte +++ b/qa/ui/src/routes/+layout.svelte @@ -5,6 +5,10 @@ } from 'sveltestrap'; import Header from '$lib/components/Header.svelte'; + import { version } from '$lib/stores/auth'; + + version.refresh(); + setInterval(version.refresh, 30000); @@ -14,7 +18,9 @@
- + + +