This repository has been archived on 2025-06-10. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
server/qa/ui/src/lib/components/QAItem.svelte

285 lines
9.5 KiB
Svelte

<script>
import { createEventDispatcher } from 'svelte';
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 { viewIdx } from '$lib/stores/todo';
const dispatch = createEventDispatcher();
export let query_selected;
let query_commentsP;
let thumbs = [];
let thumb_me = [];
let has_comments = false;
$: updateComments(query_selected)
function updateComments(query_selected) {
if (query_selected) {
query_commentsP = getQAComments(query_selected.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_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");
})
}
function deleteMyThumbs() {
if (thumb_me.length) {
for (const c of thumb_me) {
c.delete(query_selected.id);
}
dispatch("update-queries");
}
}
function deleteComment(comment) {
comment.delete(query_selected.id).then(() => {
dispatch("update-queries");
})
}
</script>
{#if query_selected}
<Card>
<CardHeader>
<div class="d-flex justify-content-between align-items-center">
<h4 class="card-title fw-bold mb-0">{query_selected.subject}</h4>
<div>
{#if $auth && !query_selected.solved && $viewIdx[query_selected.id_exercice]}
<Button on:click={solveQA} color="success">
<Icon name="check" />
Marquer comme résolu
</Button>
{/if}
{#if $auth && $auth.id_team == query_selected.id_team}
{#if query_selected.solved && !query_selected.closed && (query_selected.subject != "RAS" || query_selected.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_selected.solved && !has_comments) || (query_selected.subject == "RAS" && query_selected.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_selected.user.split("@")[0]}
</div>
<div class="text-muted">
(team #{query_selected.id_team})
</div>
</div>
</Col>
<Col>
<div class="level-item">
<div class="heading">
État
</div>
<BadgeState class="value" state={query_selected.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_selected.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_selected.solved}
<DateFormat date={query_selected.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_selected.closed}
<DateFormat date={query_selected.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}