I think you should store them exactly as you would store them in non-draft, just with another field of is_draft 0 or 1 values.
Saving drafts in a quiz like app
Hi all,
I'm having a brain fog moment so any ideas would be appreciated.
I have an app where users select yes or no to series of questions and they need to be able to save their current answers as a draft and come back later to finish the questions, so, what should I save in this table?
At current I have a question table
$table->id();
$table->string('question_id');
$table->string('question');
$table->string('type');
$table->smallInteger('yes_weight');
$table->smallInteger('no_weight');
I have a Vue file that renders my questions:
<template>
<div v-if="questions.length" class="container">
<div class="row">
<div class="col-12">
<div class="row mb-3">
<div class="col-12 col-md-4">
<router-link :to="{ name: 'risk-assessment-test' }" class="btn btn-slate btn-block">Back to dashboard</router-link>
</div>
<div class="col-12 col-md-4">
<button @click="saveDraft" class="btn btn-block btn-slate">Save progress</button>
</div>
<div class="col-12 col-md-4">
<button @click="calculateScores" class="btn btn-block btn-slate">Scores on the doors</button>
</div>
</div>
<!-- Loop through the question objects that contain the three types of questions -->
<div v-for="(question, index) in questions" :key="index" class="row">
<div class="col-12">
<!-- Loop through the identification questions -->
<div v-for="(identificationQuestion, i) in question.identification" :key="'identification_' + i" class="row mb-3">
<!-- Display the identification question -->
<div class="col-12 col-md-6 col-lg-9">
<h5>{{ identificationQuestion.question }}</h5>
<p>{{ identificationQuestion.type }}</p>
</div>
<!-- Display the answers for the identification question -->
<div class="col-12 col-md-6 col-lg-3">
<select class="form-control" v-model="answers.identification[identificationQuestion.id]" required>
<option :value="identificationQuestion.yes_weight">Yes -> {{ identificationQuestion.yes_weight }}</option>
<option :value="identificationQuestion.no_weight">No -> {{ identificationQuestion.no_weight }}</option>
</select>
</div>
</div>
<!-- Loop through the assessment questions -->
<div v-for="(assessmentQuestion, i) in question.assessment" :key="'assessment_' + i" class="row mb-3">
<!-- Display the assessment question -->
<div class="col-12 col-md-6 col-lg-9">
<h5>{{ assessmentQuestion.question }}</h5>
<p>{{ assessmentQuestion.type }}</p>
</div>
<!-- Display the answers for the assessment question -->
<div class="col-12 col-md-6 col-lg-3">
<select class="form-control" v-model="answers.assessment[assessmentQuestion.id]" required>
<option :value="assessmentQuestion.yes_weight">Yes -> {{ assessmentQuestion.yes_weight }}</option>
<option :value="assessmentQuestion.no_weight">No -> {{ assessmentQuestion.no_weight }}</option>
</select>
</div>
</div>
<!-- Loop through the control questions -->
<div v-for="(controlQuestion, i) in question.control" :key="'control_' + i" class="row mb-3">
<!-- Display the control question -->
<div class="col-12 col-md-6 col-lg-9">
<h5>{{ controlQuestion.question }}</h5>
<p>{{ controlQuestion.type }}</p>
</div>
<!-- Display the answers for the control question -->
<div class="col-12 col-md-6 col-lg-3">
<select class="form-control" v-model="answers.control[controlQuestion.id]" required>
<option :value="controlQuestion.yes_weight">Yes -> {{ controlQuestion.yes_weight }}</option>
<option :value="controlQuestion.no_weight">No -> {{ controlQuestion.no_weight }}</option>
</select>
</div>
</div>
</div>
</div>
<!-- Outer question loop ends here -->
</div>
</div>
</div>
</template>
<script>
export default {
components: {},
mounted() {
this.retrieveQuestionsForTest();
},
data() {
return {
test_id: this.$route.params.test_id,
questions: [],
answers: {
identification: {},
assessment: {},
control: {}
}
};
},
methods: {
retrieveQuestionsForTest() {
let url = '/api/risk-assessment/questions';
axios
.get(url)
.then((response) => {
console.log(response.data.questions);
this.questions = response.data.questions.map((question) => {
// Add a score property to each question object
return {
...question,
score: 0
};
});
})
.catch((error) => {
console.log(error);
});
},
saveDraft() {
let url = '/api/risk-assessment/draft/' + this.test_id;
axios
.patch(url, this.answers)
.then((response) => {
console.log(response.data);
this.$swal.fire({
customClass: 'w-100',
position: 'top-end',
type: 'success',
title: `Draft Saved`,
text: 'All progress has been saved.',
backdrop: false,
showConfirmButton: false,
timer: 2500
});
})
.catch((error) => {
console.log(error);
});
},
calculateScores() {
console.log('Calculating...');
this.questions.forEach((question) => {
let score = 0;
});
}
}
};
</script>
<style lang="scss" scoped></style>
I have a draft table that stores:
- A test id
- A user id
What would be the best way to store the other information, as in the actual answers the user gave?
As I need it so that when a draft is loaded you get all of the previous answers so I was thinking maybe a question answer table to store the test id, question id and the value?
The questions are structured in this way going into the component above:
{
"message": "Retrieved questions",
"questions": [
{
"identification": [
{
"id": 1,
"question_id": "1",
"question": "Does the supplier handle personal and/or sensitive data?",
"type": "identification",
"no_weight": 1,
"yes_weight": 10
}
],
"assessment": [
{
"id": 2,
"question_id": "1",
"question": "Is the data processed only inside the UK or the EU?",
"type": "assessment",
"no_weight": 0,
"yes_weight": -3
}
],
"control": [
{
"id": 3,
"question_id": "1",
"question": "Is the supplier registered with the Information Commissioner Office (ICO)?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 4,
"question_id": "1",
"question": "Does the supplier have a GDPR policy and GDPR breach process in place?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 5,
"question_id": "1",
"question": "Does the supplier have controls in place around sharing data with third parties?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 6,
"question_id": "1",
"question": "Does the supplier have a cyber security accreditation, e.g ISO27001?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 7,
"question_id": "1",
"question": "Does the supplier have data-security and cyber-security processes in place to deal with threat/loss?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 8,
"question_id": "1",
"question": "Does the supplier do penetration testing?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
}
]
},
{
"identification": [
{
"id": 9,
"question_id": "2",
"question": "Is the supplier's environmental impact likely to negatively influence Newable?",
"type": "identification",
"no_weight": 1,
"yes_weight": 10
}
],
"assessment": [
{
"id": 10,
"question_id": "2",
"question": "Is the supplier from any of the following industries: oil and gas (etc.)?",
"type": "assessment",
"no_weight": -2,
"yes_weight": 0
},
{
"id": 11,
"question_id": "2",
"question": "Is the supplier a service provider?",
"type": "assessment",
"no_weight": 0,
"yes_weight": -2
}
],
"control": [
{
"id": 12,
"question_id": "2",
"question": "Does the supplier have an environmental accreditation such as Green Mark or ISO140012?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 13,
"question_id": "2",
"question": "Does the supplier actively manage its environmental impact?",
"type": "control",
"no_weight": 0,
"yes_weight": -2
},
{
"id": 14,
"question_id": "2",
"question": "Does the supplier have an Environment Policy?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
}
]
},
{
"identification": [
{
"id": 15,
"question_id": "3",
"question": "Does the supplier carry on a regulated activity?",
"type": "identification",
"no_weight": 1,
"yes_weight": 10
}
],
"control": [
{
"id": 16,
"question_id": "3",
"question": "Does the supplier have the necessary permission or registration to carry out the regulated activity?",
"type": "control",
"no_weight": 0,
"yes_weight": -5
}
]
},
{
"identification": [
{
"id": 17,
"question_id": "4",
"question": "Does the supplier's activities have the potential to cause social harm (e.g modern slavery, bribery)?",
"type": "identification",
"no_weight": 1,
"yes_weight": 10
}
],
"assessment": [
{
"id": 18,
"question_id": "4",
"question": "Does the supplier employ non-UK or non-EU staff?",
"type": "assessment",
"no_weight": -1,
"yes_weight": 0
},
{
"id": 19,
"question_id": "4",
"question": "Is the supplier involved in policy-making/politics?",
"type": "assessment",
"no_weight": -1,
"yes_weight": 0
},
{
"id": 20,
"question_id": "4",
"question": "Is the supplier from an industry with higher risk of human rights issues?",
"type": "assessment",
"no_weight": -1,
"yes_weight": 0
}
],
"control": [
{
"id": 21,
"question_id": "4",
"question": "Does the supplier comply with labour regulations (e.g protects its employees confidential data)?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 22,
"question_id": "4",
"question": "Does the supplier have an anti-bribery policy in place?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 23,
"question_id": "4",
"question": "Does the supplier have a modern slavery policy in place?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 24,
"question_id": "4",
"question": "Has there been a lot of staff turnover with the supplier?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
},
{
"id": 25,
"question_id": "4",
"question": "Does the supplier have health, safety and wellbeing processes in place?",
"type": "control",
"no_weight": 0,
"yes_weight": -1
}
]
},
{
"identification": [
{
"id": 26,
"question_id": "5",
"question": "Is the supplier critical to Newable's operations?",
"type": "identification",
"no_weight": 1,
"yes_weight": 10
}
],
"control": [
{
"id": 27,
"question_id": "5",
"question": "Does the supplier have sufficient financial strength to operate?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 28,
"question_id": "5",
"question": "Are the supplier's latest FY audited accounts filed at Companies House?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 29,
"question_id": "5",
"question": "Is the contract with the supplier recurring or longer than 3 months?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 30,
"question_id": "5",
"question": "Does the supplier have any claims pending or have had any in the past five years?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 31,
"question_id": "5",
"question": "Is the company's current financial performance satisfactory?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 32,
"question_id": "5",
"question": "In the last 4 years, has the supplier made a significant loss?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 33,
"question_id": "5",
"question": "Does the supplier use IT and/or systems for the delivery of the service?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 34,
"question_id": "5",
"question": "Is the contract size critical to Newable's operations?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
},
{
"id": 35,
"question_id": "5",
"question": "Does the supplier have a good track record for customer service?",
"type": "control",
"no_weight": 0,
"yes_weight": 0
}
]
}
]
}
Please or to participate in this conversation.