qa: Back to the same situation
This commit is contained in:
parent
00f84e43ca
commit
1aa82bb2ef
27 changed files with 1336 additions and 22 deletions
17
qa/ui/src/lib/components/DateFormat.svelte
Normal file
17
qa/ui/src/lib/components/DateFormat.svelte
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script>
|
||||
export let date;
|
||||
export let dateStyle;
|
||||
export let timeStyle;
|
||||
|
||||
function formatDate(input, dateStyle, timeStyle) {
|
||||
if (typeof input === 'string') {
|
||||
input = new Date(input);
|
||||
}
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle,
|
||||
timeStyle,
|
||||
}).format(input);
|
||||
}
|
||||
</script>
|
||||
|
||||
{formatDate(date, dateStyle, timeStyle)}
|
||||
54
qa/ui/src/lib/components/ExerciceQA.svelte
Normal file
54
qa/ui/src/lib/components/ExerciceQA.svelte
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
Icon,
|
||||
Table,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import QAItems from '$lib/components/QAItems.svelte';
|
||||
import QANewItem from '$lib/components/QANewItem.svelte';
|
||||
import { themes, themesIdx } from '$lib/stores/themes';
|
||||
|
||||
if ($themes.length == 0) {
|
||||
themes.refresh();
|
||||
}
|
||||
|
||||
export let exercice = {};
|
||||
let countCreation = 0;
|
||||
</script>
|
||||
|
||||
<h2>
|
||||
{exercice.title}
|
||||
{#if $themes.length && $themesIdx[exercice.id_theme]}
|
||||
<small>
|
||||
<a href="themes/{exercice.id_theme}" title={$themesIdx[exercice.id_theme].authors}>{$themesIdx[exercice.id_theme].name}</a>
|
||||
</small>
|
||||
{#if $themesIdx[exercice.id_theme].exercices && $themesIdx[exercice.id_theme].exercices[exercice.id]}
|
||||
<div class="btn-group" role="group">
|
||||
<a href="exercices/{$themesIdx[exercice.id_theme].exercices[exercice.id].previous}" title="Exercice précédent" class:disabled={!$themesIdx[exercice.id_theme].exercices[exercice.id].previous} class="btn btn-sm btn-light"><Icon name="chevron-left" /></a>
|
||||
<a href="exercices/{$themesIdx[exercice.id_theme].exercices[exercice.id].next}" title="Exercice suivant" class:disabled={!$themesIdx[exercice.id_theme].exercices[exercice.id].next} class="btn btn-sm btn-light"><Icon name="chevron-right" /></a>
|
||||
</div>
|
||||
{/if}
|
||||
<a href="../{$themesIdx[exercice.id_theme].urlid}/{exercice.urlid}" target="_self" class="float-right ml-2 btn btn-sm btn-info"><Icon name="play-fill" /> Site du challenge</a>
|
||||
{/if}
|
||||
</h2>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">{@html exercice.statement}</div>
|
||||
<div class="col-md-6">{@html exercice.overview}</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<QANewItem
|
||||
{exercice}
|
||||
on:new-query={() => countCreation++}
|
||||
/>
|
||||
{#key countCreation}
|
||||
<QAItems
|
||||
{exercice}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
<script>
|
||||
import { page } from '$app/stores'
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
|
|
@ -20,7 +22,15 @@
|
|||
Row,
|
||||
} from 'sveltestrap';
|
||||
|
||||
const version = fetch('api/version', {headers: {'Accept': 'application/json'}}).then((res) => res.json())
|
||||
import { auth, version } from '$lib/stores/auth';
|
||||
|
||||
export let activemenu = "";
|
||||
$: {
|
||||
const path = $page.url.pathname.split("/");
|
||||
if (path.length > 1) {
|
||||
activemenu = path[1];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Navbar color="dark" dark expand="md">
|
||||
|
|
@ -30,25 +40,46 @@
|
|||
</NavbarBrand>
|
||||
<Nav navbar>
|
||||
<NavItem>
|
||||
<NavLink href=".">
|
||||
<NavLink
|
||||
href="."
|
||||
active={activemenu === ''}
|
||||
>
|
||||
<Icon name="house-door" />
|
||||
Accueil
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink href="themes">
|
||||
<NavLink
|
||||
href="themes"
|
||||
active={activemenu === 'themes'}
|
||||
>
|
||||
<Icon name="box-seam" />
|
||||
Scénarios
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink href="teams">
|
||||
<NavLink
|
||||
href="exercices"
|
||||
active={activemenu === 'exercices'}
|
||||
>
|
||||
<Icon name="bar-chart-steps" />
|
||||
Étapes
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink
|
||||
href="teams"
|
||||
active={activemenu === 'teams'}
|
||||
>
|
||||
<Icon name="people" />
|
||||
Équipes
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink href="repositories">
|
||||
<NavLink
|
||||
href="repositories"
|
||||
active={activemenu === 'repositories'}
|
||||
>
|
||||
<Icon name="archive" />
|
||||
Dépôts
|
||||
</NavLink>
|
||||
|
|
@ -56,12 +87,8 @@
|
|||
</Nav>
|
||||
<Nav class="ms-auto text-light" navbar>
|
||||
<NavItem class="ms-2">
|
||||
{#await version}
|
||||
veuillez patienter
|
||||
{:then v}
|
||||
v{v.version}
|
||||
{#if v.auth}– Logged as {v.auth.name} (team #{v.auth.id_team}){/if}
|
||||
{/await}
|
||||
v{$version.version}
|
||||
{#if $auth}– Logged as {$auth.name} (team #{$auth.id_team}){/if}
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
|
|
|
|||
93
qa/ui/src/lib/components/MyExercices.svelte
Normal file
93
qa/ui/src/lib/components/MyExercices.svelte
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import {
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { getQAView } from '$lib/todo';
|
||||
import { getExerciceQA } from '$lib/qa';
|
||||
import { exercicesIdx } from '$lib/stores/exercices';
|
||||
import { themesIdx } from '$lib/stores/themes';
|
||||
import { todos } from '$lib/stores/todo';
|
||||
|
||||
export { className as class };
|
||||
let className = '';
|
||||
|
||||
function show(id) {
|
||||
goto("exercices/" + id)
|
||||
}
|
||||
|
||||
let my_exercices = [];
|
||||
let my_exercicesP = update_exercices()
|
||||
|
||||
async function update_exercices() {
|
||||
const view = await getQAView();
|
||||
my_exercices = [];
|
||||
|
||||
for (const v of view) {
|
||||
v.queries = null;
|
||||
v.queriesNSolved = 0;
|
||||
v.queriesNClosed = 0;
|
||||
getExerciceQA(v.id_exercice).then((queries) => {
|
||||
v.queries = queries;
|
||||
for (const q of queries) {
|
||||
if (q.solved == null) v.queriesNSolved++;
|
||||
if (q.closed == null) v.queriesNClosed++;
|
||||
}
|
||||
my_exercices.push(v);
|
||||
my_exercices = my_exercices;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={className}>
|
||||
<h3>Vos étapes</h3>
|
||||
{#await my_exercicesP}
|
||||
{:then}
|
||||
<table class="table table-stripped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Défi</th>
|
||||
<th>Requêtes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each my_exercices as todo (todo.id)}
|
||||
<tr
|
||||
class:table-success={todo.queries && todo.queries.length > 0}
|
||||
class:table-warning={todo.queriesNSolved > 0}
|
||||
on:click={() => show(todo.id_exercice)}
|
||||
>
|
||||
<td>
|
||||
{#if $exercicesIdx.length == 0 && $themesIdx.length == 0}
|
||||
<Spinner size="sm" />
|
||||
{:else if $themesIdx[$exercicesIdx[todo.id_exercice]]}
|
||||
<a href="themes/{$exercicesIdx[todo.id_exercice].id_theme}">
|
||||
{$themesIdx[$exercicesIdx[todo.id_exercice].id_theme].name}
|
||||
</a>
|
||||
–
|
||||
{/if}
|
||||
{#if $exercicesIdx.length == 0}
|
||||
<Spinner size="sm" />
|
||||
{:else if $exercicesIdx[todo.id_exercice]}
|
||||
{$exercicesIdx[todo.id_exercice].title}
|
||||
{#if $exercicesIdx[todo.id_exercice].wip}
|
||||
<Icon name="cone-striped" />
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if todo.queries && todo.queries.length}
|
||||
{todo.queriesNSolved} / {todo.queriesNClosed}
|
||||
{:else}
|
||||
0
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/await}
|
||||
</div>
|
||||
91
qa/ui/src/lib/components/MyTodo.svelte
Normal file
91
qa/ui/src/lib/components/MyTodo.svelte
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import {
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { getExerciceTested, getQAWork } from '$lib/todo'
|
||||
import { exercicesIdx } from '$lib/stores/exercices'
|
||||
import { themesIdx } from '$lib/stores/themes'
|
||||
import { todos } from '$lib/stores/todo'
|
||||
|
||||
export { className as class };
|
||||
let className = '';
|
||||
|
||||
let exo_doneP = getExerciceTested();
|
||||
let tododone = { };
|
||||
|
||||
getQAWork().then((queries) => {
|
||||
for (const q of queries) {
|
||||
tododone[q.id_exercice] = q;
|
||||
}
|
||||
})
|
||||
|
||||
function show(id) {
|
||||
goto("exercices/" + id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={className}>
|
||||
<h3>Étapes à tester et valider</h3>
|
||||
{#await todos.refresh()}
|
||||
{:then}
|
||||
{#await exo_doneP}
|
||||
{:then exo_done}
|
||||
<table class="table table-stripped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Avancement</th>
|
||||
<th>Scénario</th>
|
||||
<th>Défi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $todos as todo (todo.id)}
|
||||
<tr
|
||||
style:cursor="pointer"
|
||||
class:text-light={!tododone[todo.id_exercice] && !exo_done[todo.id_exercice]}
|
||||
class:table-dark={!tododone[todo.id_exercice] && !exo_done[todo.id_exercice]}
|
||||
class:table-warning={!tododone[todo.id_exercice] && exo_done[todo.id_exercice] == 'access'}
|
||||
class:table-info={!tododone[todo.id_exercice] && (exo_done[todo.id_exercice] == 'tried' || exo_done[todo.id_exercice] == 'solved')}
|
||||
class:table-success={tododone[todo.id_exercice]} on:click={() => show(todo.id_exercice)}
|
||||
>
|
||||
<td>
|
||||
{#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}
|
||||
</td>
|
||||
<td>
|
||||
{#if $exercicesIdx.length == 0 && $themesIdx.length == 0}
|
||||
<Spinner size="sm" />
|
||||
{:else}
|
||||
<a href="themes/{$exercicesIdx[todo.id_exercice].id_theme}">
|
||||
{$themesIdx[$exercicesIdx[todo.id_exercice].id_theme].name}
|
||||
</a>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if $exercicesIdx.length == 0}
|
||||
<Spinner size="sm" />
|
||||
{:else}
|
||||
{$exercicesIdx[todo.id_exercice].title}
|
||||
{#if $exercicesIdx[todo.id_exercice].wip}
|
||||
<Icon name="cone-striped" />
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/await}
|
||||
{/await}
|
||||
</div>
|
||||
183
qa/ui/src/lib/components/QAItem.svelte
Normal file
183
qa/ui/src/lib/components/QAItem.svelte
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import DateFormat from '$lib/components/DateFormat.svelte';
|
||||
import { getQAComments, QAComment } from '$lib/qa.js';
|
||||
import { auth } from '$lib/stores/auth';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let query_selected;
|
||||
|
||||
let query_commentsP;
|
||||
$: query_commentsP = getQAComments(query_selected.id);
|
||||
|
||||
let newComment = new QAComment();
|
||||
let submissionInProgress = false;
|
||||
function addComment() {
|
||||
submissionInProgress = true;
|
||||
newComment.save(query_selected.id).then(() => {
|
||||
query_commentsP = getQAComments(query_selected.id);
|
||||
newComment = new QAComment();
|
||||
submissionInProgress = false;
|
||||
})
|
||||
}
|
||||
|
||||
function updateQA() {
|
||||
query_selected.save().then(() => {
|
||||
dispatch("update-queries");
|
||||
})
|
||||
}
|
||||
|
||||
function solveQA() {
|
||||
query_selected.solved = new Date();
|
||||
query_selected.save().then(() => {
|
||||
dispatch("update-queries");
|
||||
})
|
||||
}
|
||||
|
||||
function reopenQA() {
|
||||
query_selected.solved = null;
|
||||
query_selected.save().then(() => {
|
||||
dispatch("update-queries");
|
||||
})
|
||||
}
|
||||
|
||||
function closeQA() {
|
||||
query_selected.closed = new Date();
|
||||
query_selected.save().then(() => {
|
||||
dispatch("update-queries");
|
||||
})
|
||||
}
|
||||
|
||||
function deleteQA() {
|
||||
query_selected.delete().then(() => {
|
||||
query_selected = null;
|
||||
dispatch("update-queries");
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="d-flex justify-content-between">
|
||||
<h4 class="card-title mb-0">{query_selected.subject}</h4>
|
||||
<div>
|
||||
{#if $auth && $auth.id_team == query_selected.id_team}
|
||||
{#if query_selected.solved && !query_selected.closed}
|
||||
<Button on:click={closeQA} color="success">
|
||||
<Icon name="check" />
|
||||
Valider la résolution
|
||||
</Button>
|
||||
<Button on:click={reopenQA} color="danger">
|
||||
<Icon name="x" />
|
||||
Réouvrir
|
||||
</Button>
|
||||
{/if}
|
||||
{#if !query_selected.solved}
|
||||
<Button on:click={deleteQA} color="danger">
|
||||
<Icon name="trash-fill" />
|
||||
Supprimer
|
||||
</Button>
|
||||
{/if}
|
||||
{:else if $auth && !query_selected.solved}
|
||||
<Button on:click={solveQA} color="success">
|
||||
<Icon name="check" />
|
||||
Marquer comme résolu
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div class="row">
|
||||
<dl class="col row">
|
||||
<dt class="col-3">Qui ?</dt>
|
||||
<dd class="col-9">{query_selected.user} (team #{query_selected.id_team})</dd>
|
||||
|
||||
<dt class="col-3">État</dt>
|
||||
<dd class="col-9">{query_selected.state}</dd>
|
||||
|
||||
<dt class="col-3">Date de création</dt>
|
||||
<dd class="col-9">
|
||||
<DateFormat date={query_selected.creation} dateStyle="long" timeStyle="medium" />
|
||||
</dd>
|
||||
|
||||
<dt class="col-3">Date de résolution</dt>
|
||||
<dd class="col-9">
|
||||
{#if query_selected.solved}
|
||||
<DateFormat date={query_selected.solved} dateStyle="long" timeStyle="medium" />
|
||||
{:else}
|
||||
-
|
||||
{/if}
|
||||
</dd>
|
||||
|
||||
<dt class="col-3">Date de clôture</dt>
|
||||
<dd class="col-9">
|
||||
{#if query_selected.closed}
|
||||
<DateFormat date={query_selected.closed} dateStyle="long" timeStyle="medium" />
|
||||
{:else}
|
||||
-
|
||||
{/if}
|
||||
</dd>
|
||||
</dl>
|
||||
<div class="col-auto">
|
||||
{#if $auth && $auth.id_team == query_selected.id_team}
|
||||
<Button on:click={updateQA} color="primary">
|
||||
<Icon name="upload" />
|
||||
Mettre à jour
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#await query_commentsP}
|
||||
<div class="d-flex">
|
||||
<Spinner />
|
||||
<div>
|
||||
Chargement des commentaires en cours…
|
||||
</div>
|
||||
</div>
|
||||
{:then query_comments}
|
||||
<table class="table table-striped">
|
||||
{#each query_comments as comment (comment.id)}
|
||||
<tr>
|
||||
<td style="white-space: pre-line">
|
||||
Le <DateFormat date={comment.date} dateStyle="medium" timeStyle="short" />, <strong>{comment.user}</strong> a écrit : {comment.content}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
{/await}
|
||||
<form on:submit|preventDefault={addComment}>
|
||||
<label for="newComment">Répondre :</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
placeholder="Ajouter un commentaire"
|
||||
rows="2"
|
||||
id="newComment"
|
||||
bind:value={newComment.content}
|
||||
></textarea>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
class="mt-1 float-right"
|
||||
disabled={!newComment.content || newComment.content.length == 0 || submissionInProgress}
|
||||
>
|
||||
{#if submissionInProgress}
|
||||
<Spinner size="sm" />
|
||||
{/if}
|
||||
Ajouter le commentaire
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
</CardBody>
|
||||
</Card>
|
||||
95
qa/ui/src/lib/components/QAItems.svelte
Normal file
95
qa/ui/src/lib/components/QAItems.svelte
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<script>
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { getExerciceQA, QAComment } from '$lib/qa.js';
|
||||
import QAItem from '$lib/components/QAItem.svelte';
|
||||
|
||||
export let exercice = { };
|
||||
export let query_selected = null;
|
||||
|
||||
const fields = [ "state", "subject" ];
|
||||
|
||||
let queriesP;
|
||||
$: queriesP = getExerciceQA(exercice.id);
|
||||
|
||||
let thumbInProgress = null;
|
||||
function thumbUp(qid) {
|
||||
thumbInProgress = qid;
|
||||
const thumb = new QAComment({
|
||||
content: "+1",
|
||||
});
|
||||
thumb.save(qid).then(() => {
|
||||
thumbInProgress = null;
|
||||
})
|
||||
}
|
||||
|
||||
function updateQueries() {
|
||||
queriesP = getExerciceQA(exercice.id);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await queriesP}
|
||||
{:then queries}
|
||||
<table
|
||||
class="table table-bordered table-striped"
|
||||
class:table-hover={queries.length}
|
||||
class:table-sm={queries.length}
|
||||
>
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
{#each fields as field}
|
||||
<th>
|
||||
{ field }
|
||||
</th>
|
||||
{/each}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{#if queries.length}
|
||||
<tbody>
|
||||
{#each queries as q (q.id)}
|
||||
<tr on:click={() => query_selected = q} class:bg-warning={query_selected && q.id == query_selected.id}>
|
||||
{#each fields as field}
|
||||
<td>
|
||||
{@html q[field]}
|
||||
</td>
|
||||
{/each}
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-light"
|
||||
disabled={thumbInProgress !== null}
|
||||
on:click|preventDefault={() => thumbUp(q.id)}
|
||||
>
|
||||
{#if thumbInProgress == q.id}
|
||||
<Spinner size="sm" />
|
||||
{:else}
|
||||
<Icon name="hand-thumbs-up" />
|
||||
{/if}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
{:else}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan={fields.length+1} class="font-weight-bold text-info text-center">
|
||||
Aucune requête enregistrée
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{/if}
|
||||
</table>
|
||||
|
||||
{#if query_selected}
|
||||
<QAItem
|
||||
bind:query_selected={query_selected}
|
||||
on:update-queries={updateQueries}
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
76
qa/ui/src/lib/components/QANewItem.svelte
Normal file
76
qa/ui/src/lib/components/QANewItem.svelte
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import { QAQuery, QAStates } from '$lib/qa.js';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let exercice = {};
|
||||
|
||||
let newQuery = new QAQuery();
|
||||
|
||||
let creationInProgress = false;
|
||||
function saveQuery() {
|
||||
newQuery.id_exercice = exercice.id;
|
||||
creationInProgress = true;
|
||||
newQuery.save().then((res) => {
|
||||
dispatch("new-query");
|
||||
newQuery = new QAQuery();
|
||||
creationInProgress = false;
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={saveQuery} class="card mb-3">
|
||||
<div class="card-header">
|
||||
Qu'avez-vous pensé de cette étape ?
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3 row">
|
||||
<label for="state" class="col-2 col-form-label col-form-label-sm">État</label>
|
||||
<div class="col-10">
|
||||
<select class="form-select form-select-sm" id="state" bind:value={newQuery.state}>
|
||||
{#each Object.keys(QAStates) as state}
|
||||
<option value={state}>
|
||||
{QAStates[state]}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="subject" class="col-2 col-form-label col-form-label-sm">Sujet</label>
|
||||
<div class="col-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control form-control-sm"
|
||||
id="subject"
|
||||
placeholder="Le pcap ne contient pas l'IP attendue"
|
||||
bind:value={newQuery.subject}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="description" class="col-2 col-form-label col-form-label-sm">Description</label>
|
||||
<div class="col-10">
|
||||
<textarea class="form-control form-control-sm" placeholder="Ajouter un commentaire" rows="2" id="description" bind:value={newQuery.content}></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
class="float-end"
|
||||
disabled={creationInProgress}
|
||||
>
|
||||
{#if creationInProgress}
|
||||
<Spinner size="sm" />
|
||||
{/if}
|
||||
Soumettre
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
Reference in a new issue