feat(frontend): UI vragen & antwoorden verbeterd.
This commit is contained in:
parent
10a329bed3
commit
97f6a603f5
9 changed files with 164 additions and 217 deletions
|
@ -15,7 +15,6 @@
|
|||
<div
|
||||
v-for="question in questions"
|
||||
:key="(question.sequenceNumber, question.content)"
|
||||
class="border rounded-2xl p-4 shadow-sm bg-white"
|
||||
>
|
||||
<SingleQuestion :question="question"></SingleQuestion>
|
||||
</div>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
language: Language;
|
||||
learningObjectHruid?: string;
|
||||
forGroup?: GroupDTOId | undefined;
|
||||
withTitle?: boolean;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -65,6 +66,8 @@
|
|||
const createQuestionMutation = useCreateQuestionMutation(loID);
|
||||
const groupsQueryResult = useStudentGroupsQuery(authService.authState.user?.profile.preferred_username);
|
||||
|
||||
const showQuestionBox = computed(() => authService.authState.activeRole === AccountType.Student && pathIsAssignment.value);
|
||||
|
||||
function submitQuestion(): void {
|
||||
const assignments = studentAssignmentsQueryResult.data.value?.assignments as AssignmentDTO[];
|
||||
const assignment = assignments.find(
|
||||
|
@ -93,24 +96,26 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="authService.authState.activeRole === AccountType.Student && pathIsAssignment"
|
||||
class="question-box"
|
||||
>
|
||||
<div class="input-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="`${t(`question-input-placeholder`)}`"
|
||||
class="question-input"
|
||||
v-model="questionInput"
|
||||
/>
|
||||
<button
|
||||
@click="submitQuestion"
|
||||
class="send-button"
|
||||
>
|
||||
▶
|
||||
</button>
|
||||
</div>
|
||||
<h3 v-if="props.withTitle && showQuestionBox">{{ t('askAQuestion') }}:</h3>
|
||||
<div class="question-box" v-if="showQuestionBox">
|
||||
<v-textarea
|
||||
:label="t('question-input-placeholder')"
|
||||
v-model="questionInput"
|
||||
class="question-field"
|
||||
density="compact"
|
||||
rows="1"
|
||||
variant="outlined"
|
||||
auto-grow>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn
|
||||
icon="mdi mdi-send"
|
||||
size="small"
|
||||
variant="plain"
|
||||
class="question-button"
|
||||
@click="submitQuestion"
|
||||
/>
|
||||
</template>
|
||||
</v-textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -119,40 +124,5 @@
|
|||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin: 20px auto;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 999px;
|
||||
padding: 8px 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.question-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.question-input::placeholder {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.send-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.send-button:hover {
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
import UsingQueryResult from "./UsingQueryResult.vue";
|
||||
import type { AnswersResponse } from "@/controllers/answers";
|
||||
import type { AnswerData, AnswerDTO } from "@dwengo-1/common/interfaces/answer";
|
||||
import type { UserDTO } from "@dwengo-1/common/interfaces/user";
|
||||
import authService from "@/services/auth/auth-service";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { AccountType } from "@dwengo-1/common/util/account-types"
|
||||
import { AccountType } from "@dwengo-1/common/util/account-types";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -65,161 +66,119 @@
|
|||
createAnswerMutation.mutate(answerData, {
|
||||
onSuccess: async () => {
|
||||
answer.value = "";
|
||||
expanded.value = true;
|
||||
await answersQuery.refetch();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function displayNameFor(user: UserDTO) {
|
||||
if (user.firstName && user.lastName) {
|
||||
return `${user.firstName} ${user.lastName}`;
|
||||
} else {
|
||||
return user.username;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="flex justify-between items-center mb-2"
|
||||
style="
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
"
|
||||
<v-card
|
||||
class="question-card"
|
||||
>
|
||||
<span class="font-semibold text-lg text-gray-800">{{
|
||||
question.author.firstName + " " + question.author.lastName
|
||||
}}</span>
|
||||
<span class="text-sm text-gray-500">{{ formatDate(question.timestamp) }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-gray-700 mb-3"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
{{ question.content }}
|
||||
</div>
|
||||
<div
|
||||
v-if="authService.authState.activeRole === AccountType.Teacher"
|
||||
class="answer-input-container"
|
||||
>
|
||||
<input
|
||||
v-model="answer"
|
||||
type="text"
|
||||
:placeholder="t('answer-input-placeholder')"
|
||||
class="answer-input"
|
||||
/>
|
||||
<button
|
||||
@click="submitAnswer"
|
||||
class="submit-button"
|
||||
<v-card-title class="author-title">{{ displayNameFor(question.author) }}</v-card-title>
|
||||
<v-card-subtitle>{{ formatDate(question.timestamp) }}</v-card-subtitle>
|
||||
<v-card-text>
|
||||
{{ question.content }}
|
||||
</v-card-text>
|
||||
<template v-slot:actions
|
||||
v-if="authService.authState.activeRole === AccountType.Teacher || answersQuery.data?.value?.answers?.length > 0"
|
||||
>
|
||||
▶
|
||||
</button>
|
||||
</div>
|
||||
<using-query-result
|
||||
:query-result="answersQuery"
|
||||
v-slot="answersResponse: { data: AnswersResponse }"
|
||||
>
|
||||
<button
|
||||
v-if="answersResponse.data.answers && answersResponse.data.answers.length > 0"
|
||||
@click="toggle()"
|
||||
class="toggle-answers-btn"
|
||||
>
|
||||
{{ expanded ? t("answers-toggle-hide") : t("answers-toggle-show") }}
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-show="expanded"
|
||||
ref="answersContainer"
|
||||
class="mt-3 pl-4 border-l-2 border-blue-200 space-y-2"
|
||||
>
|
||||
<div
|
||||
v-for="(answer, answerIndex) in answersResponse.data.answers as AnswerDTO[]"
|
||||
:key="answerIndex"
|
||||
class="text-gray-600"
|
||||
>
|
||||
<v-divider :thickness="2" />
|
||||
<div
|
||||
class="flex justify-between items-center mb-2"
|
||||
style="
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
"
|
||||
<div class="question-actions-container">
|
||||
<v-textarea
|
||||
v-if="authService.authState.activeRole === AccountType.Teacher"
|
||||
:label="t('answer-input-placeholder')"
|
||||
v-model="answer"
|
||||
class="answer-field"
|
||||
density="compact"
|
||||
rows="1"
|
||||
variant="outlined"
|
||||
auto-grow>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn
|
||||
icon="mdi mdi-send"
|
||||
size="small"
|
||||
variant="plain"
|
||||
class="answer-button"
|
||||
@click="submitAnswer"
|
||||
/>
|
||||
</template>
|
||||
</v-textarea>
|
||||
<using-query-result
|
||||
:query-result="answersQuery"
|
||||
v-slot="answersResponse: { data: AnswersResponse }"
|
||||
>
|
||||
<span class="font-semibold text-lg text-gray-800">{{ answer.author.username }}</span>
|
||||
<span class="text-sm text-gray-500">{{ formatDate(answer.timestamp) }}</span>
|
||||
</div>
|
||||
<v-btn
|
||||
v-if="answersResponse.data.answers && answersResponse.data.answers.length > 0"
|
||||
@click="toggle()"
|
||||
>
|
||||
{{ expanded ? t("answers-toggle-hide") : t("answers-toggle-show") }}
|
||||
</v-btn>
|
||||
|
||||
<div
|
||||
class="text-gray-700 mb-3"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
{{ answer.content }}
|
||||
</div>
|
||||
<div
|
||||
v-show="expanded"
|
||||
ref="answersContainer"
|
||||
class="mt-3 pl-4 border-l-2 border-blue-200 space-y-2"
|
||||
>
|
||||
<v-card
|
||||
v-for="(answer, answerIndex) in answersResponse.data.answers as AnswerDTO[]"
|
||||
:key="answerIndex"
|
||||
class="answer-card"
|
||||
>
|
||||
<v-card-title class="author-title">{{ displayNameFor(answer.author) }}</v-card-title>
|
||||
<v-card-subtitle>{{ formatDate(answer.timestamp) }}</v-card-subtitle>
|
||||
<v-card-text>
|
||||
{{ answer.content }}
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</using-query-result>
|
||||
</div>
|
||||
</div>
|
||||
</using-query-result>
|
||||
</template>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.toggle-answers-btn {
|
||||
font-size: 0.875rem;
|
||||
text-decoration: none;
|
||||
background-color: #f0f4ff; /* subtle blue background */
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
.answer-field {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.toggle-answers-btn:hover {
|
||||
background-color: #e0eaff; /* slightly darker on hover */
|
||||
text-decoration: underline;
|
||||
.answer-button {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.answer-input-container {
|
||||
margin: 5px;
|
||||
}
|
||||
.answer-input {
|
||||
flex-grow: 1;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #374151; /* gray-700 */
|
||||
font-size: 0.875rem; /* smaller font size */
|
||||
|
||||
.question-card {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.answer-input::placeholder {
|
||||
color: #9ca3af; /* gray-400 */
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
margin-left: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
background-color: #f3f4f6; /* gray-100 */
|
||||
border-radius: 9999px;
|
||||
transition: background-color 0.2s;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.submit-button:hover {
|
||||
background-color: #e5e7eb; /* gray-200 */
|
||||
}
|
||||
|
||||
.submit-icon {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
color: #4b5563; /* gray-600 */
|
||||
}
|
||||
.answer-input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #d1d5db; /* gray-300 */
|
||||
border-radius: 9999px;
|
||||
padding: 0.5rem 1rem;
|
||||
max-width: 28rem;
|
||||
.question-actions-container {
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.answer-card {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.author-title {
|
||||
font-size: 14pt;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -169,10 +169,12 @@
|
|||
"hintKeywordsSeparatedBySpaces": "Schlüsselwörter durch Leerzeichen getrennt",
|
||||
"questions": "Fragen",
|
||||
"view-questions": "Fragen anzeigen auf ",
|
||||
"question-input-placeholder": "Frage...",
|
||||
"answer-input-placeholder": "Antwort...",
|
||||
"question-input-placeholder": "Ihre Frage...",
|
||||
"answer-input-placeholder": "Ihre Antwort...",
|
||||
"answers-toggle-hide": "Antworten verstecken",
|
||||
"answers-toggle-show": "Antworten anzeigen",
|
||||
"no-questions": "Keine Fragen",
|
||||
"no-discussion-tip": "Wählen Sie ein Lernobjekt aus, um dessen Fragen anzuzeigen"
|
||||
"no-discussion-tip": "Wählen Sie ein Lernobjekt aus, um dessen Fragen anzuzeigen",
|
||||
"askAQuestion": "Eine Frage stellen",
|
||||
"questionsCapitalized": "Fragen"
|
||||
}
|
||||
|
|
|
@ -169,10 +169,12 @@
|
|||
"hintKeywordsSeparatedBySpaces": "Keywords separated by spaces",
|
||||
"questions": "questions",
|
||||
"view-questions": "View questions in ",
|
||||
"question-input-placeholder": "question...",
|
||||
"answer-input-placeholder": "answer...",
|
||||
"question-input-placeholder": "Your question...",
|
||||
"answer-input-placeholder": "Your answer...",
|
||||
"answers-toggle-hide": "Hide answers",
|
||||
"answers-toggle-show": "Show answers",
|
||||
"no-questions": "No questions asked yet",
|
||||
"no-discussion-tip": "Choose a learning object to view its questions"
|
||||
"no-discussion-tip": "Choose a learning object to view its questions",
|
||||
"askAQuestion": "Ask a question",
|
||||
"questionsCapitalized": "Questions"
|
||||
}
|
||||
|
|
|
@ -170,10 +170,12 @@
|
|||
"hintKeywordsSeparatedBySpaces": "Mots-clés séparés par des espaces",
|
||||
"questions": "Questions",
|
||||
"view-questions": "Voir les questions dans ",
|
||||
"question-input-placeholder": "question...",
|
||||
"answer-input-placeholder": "réponse...",
|
||||
"question-input-placeholder": "Votre question...",
|
||||
"answer-input-placeholder": "Votre réponse...",
|
||||
"answers-toggle-hide": "Masquer réponses",
|
||||
"answers-toggle-show": "Afficher réponse",
|
||||
"no-questions": "Aucune question trouvée",
|
||||
"no-discussion-tip": "Sélectionnez un objet d'apprentissage pour afficher les questions qui s'y rapportent"
|
||||
"no-discussion-tip": "Sélectionnez un objet d'apprentissage pour afficher les questions qui s'y rapportent",
|
||||
"askAQuestion": "Pose une question",
|
||||
"questionsCapitalized": "Questions"
|
||||
}
|
||||
|
|
|
@ -169,10 +169,12 @@
|
|||
"hintKeywordsSeparatedBySpaces": "Trefwoorden gescheiden door spaties",
|
||||
"questions": "vragen",
|
||||
"view-questions": "Bekijk vragen in ",
|
||||
"question-input-placeholder": "vraag...",
|
||||
"answer-input-placeholder": "antwoord...",
|
||||
"question-input-placeholder": "Uw vraag...",
|
||||
"answer-input-placeholder": "Uw antwoord...",
|
||||
"answers-toggle-hide": "Verberg antwoorden",
|
||||
"answers-toggle-show": "Toon antwoorden",
|
||||
"no-questions": "Nog geen vragen gesteld",
|
||||
"no-discussion-tip": "Kies een leerobject om zijn vragen te bekijken"
|
||||
"no-discussion-tip": "Kies een leerobject om zijn vragen te bekijken",
|
||||
"askAQuestion": "Stel een vraag",
|
||||
"questionsCapitalized": "Vragen"
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
import type { QuestionDTO } from "@dwengo-1/common/interfaces/question";
|
||||
import DiscussionsSideBar from "@/components/DiscussionsSideBar.vue";
|
||||
import QuestionBox from "@/components/QuestionBox.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
|
@ -120,21 +123,29 @@
|
|||
|
||||
<template>
|
||||
<DiscussionsSideBar></DiscussionsSideBar>
|
||||
<QuestionBox
|
||||
:hruid="props.hruid"
|
||||
:language="props.language"
|
||||
:learningObjectHruid="props.learningObjectHruid"
|
||||
:forGroup="forGroup"
|
||||
/>
|
||||
<using-query-result
|
||||
:query-result="getQuestionsQuery"
|
||||
v-slot="questionsResponse: { data: QuestionsResponse }"
|
||||
>
|
||||
<QandA :questions="(questionsResponse.data.questions as QuestionDTO[]) ?? []" />
|
||||
</using-query-result>
|
||||
<div class="discussions-container">
|
||||
<QuestionBox
|
||||
:hruid="props.hruid"
|
||||
:language="props.language"
|
||||
:learningObjectHruid="props.learningObjectHruid"
|
||||
:forGroup="forGroup"
|
||||
withTitle
|
||||
/>
|
||||
<h3>{{ t("questionsCapitalized") }}:</h3>
|
||||
<using-query-result
|
||||
:query-result="getQuestionsQuery"
|
||||
v-slot="questionsResponse: { data: QuestionsResponse }"
|
||||
>
|
||||
<QandA :questions="(questionsResponse.data.questions as QuestionDTO[]) ?? []" />
|
||||
</using-query-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.discussions-container {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.learning-path-title {
|
||||
white-space: normal;
|
||||
}
|
||||
|
|
|
@ -308,12 +308,6 @@
|
|||
v-if="currentNode"
|
||||
></learning-object-view>
|
||||
</div>
|
||||
<QuestionBox
|
||||
:hruid="props.hruid"
|
||||
:language="props.language"
|
||||
:learningObjectHruid="props.learningObjectHruid"
|
||||
:forGroup="forGroup"
|
||||
/>
|
||||
<div class="navigation-buttons-container">
|
||||
<v-btn
|
||||
prepend-icon="mdi-chevron-left"
|
||||
|
@ -333,6 +327,7 @@
|
|||
</v-btn>
|
||||
</div>
|
||||
<using-query-result
|
||||
v-if="forGroup"
|
||||
:query-result="getQuestionsQuery"
|
||||
v-slot="questionsResponse: { data: QuestionsResponse }"
|
||||
>
|
||||
|
@ -346,7 +341,12 @@
|
|||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<QuestionBox
|
||||
:hruid="props.hruid"
|
||||
:language="props.language"
|
||||
:learningObjectHruid="props.learningObjectHruid"
|
||||
:forGroup="forGroup"
|
||||
/>
|
||||
<QandA :questions="(questionsResponse.data.questions as QuestionDTO[]) ?? []" />
|
||||
</using-query-result>
|
||||
</using-query-result>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue