Can share survey results with a secret shared key
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
9fd73ce235
commit
fff8b821c5
10 changed files with 367 additions and 7 deletions
|
|
@ -7,7 +7,7 @@
|
|||
export let question = null;
|
||||
|
||||
function refreshProposals() {
|
||||
let req = question.getProposals();
|
||||
let req = question.getProposals(secret);
|
||||
|
||||
req.then((proposals) => {
|
||||
const proposal_idx = { };
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
proposal_idx[proposal.id] = new String(data.labels.length - 1);
|
||||
}
|
||||
|
||||
req_responses = question.getResponses();
|
||||
req_responses = question.getResponses(secret);
|
||||
req_responses.then((responses) => {
|
||||
for (const res of responses) {
|
||||
const rsplt = res.value.split(',');
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
}
|
||||
let req_proposals = null;
|
||||
export let proposals = null;
|
||||
export let secret = null;
|
||||
let req_responses = null;
|
||||
let mean = null;
|
||||
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
|
||||
if (!proposals) {
|
||||
if (question.kind && (question.kind == "int" || question.kind.startsWith("list"))) {
|
||||
req_responses = question.getResponses();
|
||||
req_responses = question.getResponses(secret);
|
||||
req_responses.then((responses) => {
|
||||
const values = [];
|
||||
const proposal_idx = { };
|
||||
|
|
|
|||
|
|
@ -62,8 +62,10 @@ export class Question {
|
|||
this.kind = kind;
|
||||
}
|
||||
|
||||
async getProposals() {
|
||||
const res = await fetch(`api/questions/${this.id}/proposals`, {
|
||||
async getProposals(secret) {
|
||||
let url = `/questions/${this.id}/proposals`;
|
||||
if (secret) url = `/s/surveys/${this.id_survey}` + url + `?secret=${secret}`;
|
||||
const res = await fetch('api' + url, {
|
||||
method: 'GET',
|
||||
headers: {'Accept': 'application/json'},
|
||||
});
|
||||
|
|
@ -91,8 +93,10 @@ export class Question {
|
|||
}
|
||||
}
|
||||
|
||||
async getResponses() {
|
||||
const res = await fetch(`api/surveys/${this.id_survey}/questions/${this.id}/responses`, {
|
||||
async getResponses(secret) {
|
||||
let url = `/surveys/${this.id_survey}/questions/${this.id}/responses`;
|
||||
if (secret) url = `/s` + url + `?secret=${secret}`;
|
||||
const res = await fetch('api' + url, {
|
||||
method: 'GET',
|
||||
headers: {'Accept': 'application/json'},
|
||||
});
|
||||
|
|
@ -161,3 +165,17 @@ export async function getQuestions(sid) {
|
|||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSharedQuestions(sid, secret) {
|
||||
const res = await fetch(`api/s/surveys/${sid}/questions?secret=${secret}`, {headers: {'Accept': 'application/json'}})
|
||||
if (res.status == 200) {
|
||||
const data = await res.json();
|
||||
if (data === null) {
|
||||
return [];
|
||||
} else {
|
||||
return (data).map((q) => new Question(q))
|
||||
}
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,18 @@ export class Survey {
|
|||
}
|
||||
}
|
||||
|
||||
async share() {
|
||||
const res = await fetch(`api/surveys/${this.id}/shares`, {
|
||||
method: 'POST',
|
||||
headers: {'Accept': 'application/json'}
|
||||
});
|
||||
if (res.status == 200) {
|
||||
return await res.json();
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
const res = await fetch(this.id?`api/surveys/${this.id}`:'api/surveys', {
|
||||
method: this.id?'PUT':'POST',
|
||||
|
|
@ -190,3 +202,12 @@ export async function getSurvey(sid) {
|
|||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSharedSurvey(sid, secret) {
|
||||
const res = await fetch(`api/s/surveys/${sid}?secret=${secret}`, {headers: {'Accept': 'application/json'}})
|
||||
if (res.status == 200) {
|
||||
return new Survey(await res.json());
|
||||
} else {
|
||||
throw new Error((await res.json()).errmsg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
61
ui/src/routes/results.svelte
Normal file
61
ui/src/routes/results.svelte
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<script context="module">
|
||||
export async function load({ url }) {
|
||||
return {
|
||||
props: {
|
||||
secret: url.searchParams.get("secret"),
|
||||
idsurvey: url.searchParams.get("survey"),
|
||||
exportview_list: url.searchParams.get("graph_list")?false:true,
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { getSharedQuestions } from '$lib/questions';
|
||||
import { getSharedSurvey } from '$lib/surveys';
|
||||
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
|
||||
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
||||
|
||||
export let secret;
|
||||
export let idsurvey;
|
||||
|
||||
let surveyP = getSharedSurvey(idsurvey, secret);
|
||||
export let exportview_list = true;
|
||||
</script>
|
||||
|
||||
{#await surveyP then survey}
|
||||
<div class="d-flex align-items-center">
|
||||
<h2>
|
||||
{survey.title}
|
||||
<small class="text-muted">Réponses</small>
|
||||
</h2>
|
||||
<SurveyBadge class="ms-2" {survey} />
|
||||
</div>
|
||||
|
||||
{#await getSharedQuestions(survey.id, secret)}
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
||||
<span>Chargement des questions …</span>
|
||||
</div>
|
||||
{:then questions}
|
||||
{#each questions as question (question.id)}
|
||||
<h3>{question.title}</h3>
|
||||
{#if question.kind == "text" || (exportview_list && question.kind.indexOf("list") == 0)}
|
||||
{#await question.getResponses(secret) then responses}
|
||||
{#each responses as response (response.id)}
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<p class="card-text" style:white-space="pre-line">
|
||||
{response.value}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/await}
|
||||
{:else}
|
||||
<CorrectionPieChart {question} {secret} />
|
||||
{/if}
|
||||
<hr class="mb-3">
|
||||
{/each}
|
||||
{/await}
|
||||
{/await}
|
||||
|
|
@ -30,6 +30,15 @@
|
|||
let edit = false;
|
||||
let exportview = false;
|
||||
let exportview_list = false;
|
||||
|
||||
let sharing_link = null;
|
||||
async function shareResults(survey) {
|
||||
const res = await survey.share();
|
||||
sharing_link = res;
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('shareModal'));
|
||||
modal.show();
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await surveyP then survey}
|
||||
|
|
@ -40,6 +49,11 @@
|
|||
class="ms-1 float-end"
|
||||
on:update={() => goto(`surveys/${survey.id}/admin`)}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-outline-dark ms-1 float-end"
|
||||
title="Partager les résultats"
|
||||
on:click={() => shareResults(survey)}
|
||||
><i class="bi bi-share-fill"></i></button>
|
||||
<button
|
||||
class="btn ms-1 float-end"
|
||||
class:btn-dark={exportview}
|
||||
|
|
@ -144,3 +158,20 @@
|
|||
{/if}
|
||||
{/await}
|
||||
{/await}
|
||||
|
||||
<div class="modal fade" tabindex="-1" id="shareModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Partage de résultats</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
Voici le lien de partage des résultats de ce sondage :
|
||||
</p>
|
||||
<pre>{sharing_link}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Reference in a new issue