server/qa/ui/src/lib/components/QAItem.svelte

337 lines
11 KiB
Svelte

<script>
import { goto, invalidate } from '$app/navigation';
import {
Alert,
Button,
Card,
CardBody,
CardHeader,
Col,
Icon,
Row,
Spinner,
} from 'sveltestrap';
import BadgeState from '$lib/components/BadgeState.svelte';
import DateFormat from '$lib/components/DateFormat.svelte';
import { getQAComments, QAComment } from '$lib/qa';
import { auth } from '$lib/stores/auth';
import { ToastsStore } from '$lib/stores/toasts';
import { viewIdx } from '$lib/stores/todo';
export let theme = null;
export let exercice = null;
export let query;
let query_commentsP;
let thumbs = [];
let thumb_me = [];
let has_comments = false;
$: updateComments(query)
function updateComments(query) {
if (query) {
query_commentsP = getQAComments(query.id);
query_commentsP.then((comments) => {
thumbs = [];
thumb_me = [];
has_comments = false;
for (const c of comments) {
has_comments = true;
if (c.content == "+1") {
let found = false;
for (const t of thumbs) {
if (t == c.user) {
found = true;
break;
}
}
if (!found) {
thumbs.push(c.user);
}
if (c.user == $auth.name) {
thumb_me.push(c);
}
}
}
})
}
}
let newComment = new QAComment();
let submissionInProgress = false;
function addComment() {
submissionInProgress = true;
newComment.save(query.id).then(() => {
query_commentsP = getQAComments(query.id);
newComment = new QAComment();
submissionInProgress = false;
}).catch((err) => {
submissionInProgress = false;
ToastsStore.addErrorToast({
msg: err,
})
})
}
function updateQA() {
query.save().then(() => {
invalidate(`api/exercices/${exercice.id}/qa`);
}).catch((err) => {
ToastsStore.addErrorToast({
msg: err,
})
})
}
function solveQA() {
query.solved = new Date();
query.save().then(() => {
invalidate(`api/exercices/${exercice.id}/qa`);
}).catch((err) => {
ToastsStore.addErrorToast({
msg: err,
})
})
}
function reopenQA() {
query.solved = null;
query.save().then(() => {
invalidate(`api/exercices/${exercice.id}/qa`);
}).catch((err) => {
ToastsStore.addErrorToast({
msg: err,
})
})
}
function closeQA() {
query.closed = new Date();
query.save().then(() => {
invalidate(`api/exercices/${exercice.id}/qa`);
}).catch((err) => {
ToastsStore.addErrorToast({
msg: err,
})
})
}
async function deleteQA() {
query.delete().then(async () => {
await invalidate(`api/exercices/${exercice.id}/qa`);
goto(theme?`themes/${theme.id}/${exercice.id}`:`exercices/${exercice.id}`);
}).catch((err) => {
ToastsStore.addErrorToast({
msg: err,
})
})
}
function deleteMyThumbs() {
if (thumb_me.length) {
for (const c of thumb_me) {
c.delete(query.id);
}
updateComments(query);
}
}
function deleteComment(comment) {
comment.delete(query.id).then(() => {
updateComments(query);
}).catch((err) => {
ToastsStore.addErrorToast({
msg: err,
})
})
}
async function rungitlab() {
const res = await fetch('api/gitlab/token');
if (res.status === 200) {
return res.json();
} else {
throw new Error((await res.json()).errmsg);
}
}
const gitlab = rungitlab();
</script>
{#if query}
<Card>
<CardHeader>
<div class="d-flex justify-content-between align-items-center">
<h4 class="card-title fw-bold mb-0">{query.subject}</h4>
<div>
{#if $auth && !query.solved}
{#await gitlab then gl}
<Button
on:click={() => query.export2Gitlab()}
>
<Icon name="gitlab" />
Exporter vers GitLab
</Button>
{/await}
{/if}
{#if $auth && !query.solved && $viewIdx[query.id_exercice]}
<Button on:click={solveQA} color="success">
<Icon name="check" />
Marquer comme résolu
</Button>
{/if}
{#if $auth && $auth.id_team == query.id_team}
{#if query.solved && !query.closed && (query.subject != "RAS" || query.state != "ok")}
<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.solved && !has_comments) || (query.subject == "RAS" && query.state == "ok" && !has_comments)}
<Button on:click={deleteQA} color="danger">
<Icon name="trash-fill" />
Supprimer
</Button>
{/if}
{/if}
</div>
</div>
</CardHeader>
<CardBody>
<Row class="level" cols={5}>
<Col>
<div class="level-item">
<div class="heading">
Qui ?
</div>
<div class="value">
{query.user.split("@")[0]}
</div>
<div class="text-muted">
(team #{query.id_team})
</div>
</div>
</Col>
<Col>
<div class="level-item">
<div class="heading">
État
</div>
<BadgeState class="value" state={query.state} />
<div
class="text-muted"
title={thumbs.join(', ')}
on:click={deleteMyThumbs}
>
<Icon
name="hand-thumbs-up-fill"
class={thumb_me.length?"text-info":""}
style={thumb_me.length?"cursor: pointer;":""}
/>
{thumbs.length}
</div>
</div>
</Col>
<Col>
<div class="level-item">
<div class="heading">
Date de création
</div>
<div class="value">
<DateFormat date={query.creation} dateStyle="long" timeStyle="medium" />
</div>
</div>
</Col>
<Col>
<div class="level-item">
<div class="heading">
Date de résolution
</div>
<div class="value">
{#if query.solved}
<DateFormat date={query.solved} dateStyle="long" timeStyle="medium" />
{:else}
-
{/if}
</div>
</div>
</Col>
<Col>
<div class="level-item">
<div class="heading">
Date de clôture
</div>
<div class="value">
{#if query.closed}
<DateFormat date={query.closed} dateStyle="long" timeStyle="medium" />
{:else}
-
{/if}
</div>
</div>
</Col>
</Row>
<hr>
{#await query_commentsP then query_comments}
{#each query_comments as comment (comment.id)}
{#if comment.content != "+1"}
<Alert fade={false}>
<div style="white-space: pre-line">{comment.content}</div>
<div class="d-flex justify-content-end align-items-center">
{#if comment.user == $auth.name}
<Button
size="sm"
color="danger"
class="me-2"
on:click={() => deleteComment(comment)}
>
<Icon name="trash-fill" />
</Button>
{/if}
<em>
Par <strong>{comment.user}</strong>, le <DateFormat date={comment.date} dateStyle="medium" timeStyle="short" />
</em>
</div>
</Alert>
{/if}
{/each}
{/await}
<form on:submit|preventDefault={addComment}>
<textarea
class="form-control"
placeholder="Ajouter votre commentaire"
rows="2"
id="newComment"
bind:value={newComment.content}
></textarea>
{#if newComment.content && newComment.content.length > 0}
<Button
type="submit"
color="primary"
class="mt-1 float-right"
disabled={submissionInProgress}
>
{#if submissionInProgress}
<Spinner size="sm" />
{/if}
Ajouter le commentaire
</Button>
{/if}
</form>
</CardBody>
</Card>
{/if}