Skip to content

Commit

Permalink
Better Task Creation Page (Bootstrap-Academy#151)
Browse files Browse the repository at this point in the history
- Enhanced "Create" button with clearer title and icon
- Added an overview showing total quizzes and count of quizzes created by the user
- Marked quizzes with "created by you" label for easier identification
- Displayed quiz creator's name if different from the current user
- Included quiz type for quick identification
- Conditionally rendered delete/edit icons only for admins or quiz creators
- Displayed eye icon for users without delete/edit permissions (non-admin or non-creator)
- Filtered quizzes by user and type
  • Loading branch information
Gheorghii28 committed Nov 1, 2024
1 parent 7d5a801 commit 431e59d
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 15 deletions.
9 changes: 6 additions & 3 deletions components/CodingChallenge/EditableList.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<template>
<section>
<InputBtn class="my-7" full @click="openDialogAndAddNew()">{{
t("Buttons.AddNew")
}}</InputBtn>
<InputBtn class="my-7" full @click="openDialogAndAddNew()">
<PlusCircleIcon class="h-5 w-5" />
{{ t("Buttons.AddNew", { item: t("Buttons.Challenge") }) }}
</InputBtn>

<div v-if="codingChallenges.length">
<section
Expand Down Expand Up @@ -77,6 +78,7 @@ import {
LockOpenIcon,
EyeIcon,
PencilSquareIcon,
PlusCircleIcon,
} from "@heroicons/vue/24/outline";
import type { PropType } from "vue";
import { useDialogSlot } from "../../composables/dialogSlot";
Expand Down Expand Up @@ -155,6 +157,7 @@ export default {
LockOpenIcon,
EyeIcon,
PencilSquareIcon,
PlusCircleIcon,
},
};
</script>
Expand Down
1 change: 1 addition & 0 deletions components/input/Btn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export default defineComponent({
icon: { type: Object, default: null },
iconRight: { type: Boolean, default: false },
iconColor: { type: String, default: "" },
item: { type: String, default: "" },
},
emits: ["click"],
setup(props, { emit }) {
Expand Down
9 changes: 7 additions & 2 deletions components/matching/EditableList.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template>
<div>
<InputBtn class="my-7" full @click="openDialogAndAddNew()">
{{ t("Buttons.AddNew") }}
<PlusCircleIcon class="h-5 w-5" />
{{ t("Buttons.AddNew", { item: t("Buttons.Matching") }) }}
</InputBtn>
<section v-if="matchings?.length">
<div
Expand Down Expand Up @@ -51,7 +52,11 @@
<script lang="ts" setup>
import { useDialogSlot } from "~~/composables/dialogSlot";
import { useI18n } from "vue-i18n";
import { TrashIcon, EyeIcon } from "@heroicons/vue/24/outline";
import {
TrashIcon,
EyeIcon,
PlusCircleIcon,
} from "@heroicons/vue/24/outline";
import type { matching } from "~/types/matching";
const props = defineProps({
Expand Down
96 changes: 92 additions & 4 deletions components/quiz/SubTaskListEditable.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
<template>
<div>
<InputBtn class="my-7" full @click="openDialogAndAddNew()">
{{ t("Buttons.AddNew") }}
<PlusCircleIcon class="h-5 w-5" />
{{ t("Buttons.AddNew", { item: t("Buttons.Quiz") }) }}
</InputBtn>
<section v-if="quizzes?.length">
<div
v-if="quizzes?.length"
class="pb-4 pr-4 pl-4 xl:pb-5 xl:pr-5 xl:pl-5">
<article class="flex justify-between">
<div>
<p class="text-sm text-accent clamp line-1 tight">
{{ t("Headings.Total") }}: {{ filteredQuizzes?.length }}
{{ filteredQuizzes.length === 1 ? t("Headings.Quiz") : t("Headings.Quizzes") }}
</p>
<p class="text-sm clamp line-1 tight">
{{ t("Headings.QuizCreated", {
count: userQuizCount,
quizText: userQuizCount === 1 ? t("Headings.Quiz") : t("Headings.Quizzes")
}) }}
</p>
</div>
<div class="flex gap-4 mb-4">
<InputSelect
id="select_user"
sm
:options="formattedUsers"
btn-type
v-model="selectedUser"
@change="filterQuizzes"
/>
<InputSelect
id="select_quiz_type"
sm
:options="availableTypes"
btn-type
v-model="selectedType"
@change="filterQuizzes"
/>
</div>
</article>
</div>
<section v-if="filteredQuizzes?.length">
<div
v-for="(quiz, i) of quizzes"
v-for="(quiz, i) of filteredQuizzes"
:key="i"
class="p-4 xl:p-5 bg-secondary mb-4 rounded-md"
>
Expand All @@ -16,11 +53,12 @@

<div class="flex gap-3 items-center">
<TrashIcon
v-if="!!user?.admin || quiz?.creator === user?.id"
@click="fnDeleteQuiz(quiz?.id)"
class="h-5 w-5 cursor-pointer text-accent"
/>
<PencilSquareIcon
v-if="!!user?.admin"
v-if="!!user?.admin || quiz?.creator === user?.id"
@click="openDialogCreate(quiz)"
class="h-5 w-5 cursor-pointer text-accent"
/>
Expand All @@ -31,6 +69,14 @@
/>
</div>
</article>
<div class="flex flex-col sm:flex-row items-start gap-1">
<p class="text-xs text-accent clamp line-1 tight sm:pr-3 md:pr-53">
{{ quiz?.creator === user?.id ? t("Headings.QuizCreatedByYou") : t("Body.CreatedBy", { user: quiz?.creator}) }}
</p>
<p class="text-xs text-subheading clamp line-1 tight sm:pr-3 md:pr-5">
{{ quiz?.type }}
</p>
</div>
</div>
</section>
<p
Expand Down Expand Up @@ -60,7 +106,9 @@ import {
TrashIcon,
EyeIcon,
PencilSquareIcon,
PlusCircleIcon,
} from "@heroicons/vue/24/outline";
import type { Quiz } from "~/types/courseTypes";
const props = defineProps({
quizzes: { type: Array as PropType<any>, default: [] },
Expand All @@ -69,9 +117,49 @@ const props = defineProps({
const { t } = useI18n();
const dialog = useDialogSlot();
const dialogCreateQiuz = useDialogCreateSubtask();
const selectedUser = ref();
const selectedType = ref();
const quizzes = ref(props.quizzes);
const filteredQuizzes = ref([...quizzes.value]);
const propData = ref();
const user: any = useUser();
const userQuizCount = computed(() => filteredQuizzes.value.filter((quiz) => quiz.creator === user.id).length);
const formattedUsers = computed<{ label: string; value: string }[]>(() => {
const creatorsSet = new Set<string>(quizzes.value.map((quiz: Quiz) => quiz.creator));
const creatorOptions = Array.from(creatorsSet).map(creatorId => {
const creatorDisplayName = quizzes.value.find((quiz: Quiz) => quiz.creator === creatorId)?.creator || "Unknown";
return {
label: `${creatorDisplayName}${creatorId === user.id ? ' (You)' : ''}`,
value: creatorId,
};
});
return [{ label: t("List.Filter.AllUsers"), value: "" }, ...creatorOptions];
});
const availableTypes = computed<{ label: string; value: string }[]>(() => {
const typesSet = new Set(quizzes.value.map((quiz: Quiz) => quiz.type));
const typeOptions = Array.from(typesSet).map(type => ({ label: String(type), value: String(type) }));
return [{ label: t("List.Filter.AllTypes"), value: "" }, ...typeOptions];
});
onMounted(() => {
selectedUser.value = "";
selectedType.value = "";
});
function filterQuizzes() {
filteredQuizzes.value = quizzes.value.filter((quiz: Quiz) => {
const userMatches = selectedUser.value ? quiz.creator === selectedUser.value : true;
const typeMatches = selectedType.value ? quiz.type === selectedType.value : true;
return userMatches && typeMatches;
});
}
function openDialogCreate(quiz: any) {
propData.value = quiz;
dialog.value = true;
Expand Down
10 changes: 7 additions & 3 deletions locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@
"EmptyCodingChallenge": "Für diese Vorlesung wurde keine Programmieraufgabe gefunden",
"DeleteQuiz": "Quiz löschen",
"Quiz": "Quiz",
"QuizCreated": "{count} {quizText} von dir erstellt",
"QuizCreatedByYou": "Dieses Quiz wurde von dir erstellt",
"Output": "Ausgang",
"FEE": "Gebühr",
"Fee": "Gebühr",
Expand Down Expand Up @@ -452,7 +454,7 @@
"MyCertificates": "Die Zertifikate, die du aus Prüfungen bekommen hast! Vielleicht sind es ja schon genügend um einen Job zu bekommen? Oder solltest du mehr sammeln?",
"GetMorphcoins": "Du willst kein Geld bezahlen? Die Coins kannst du auch auf andere Weise erhalten",
"MoreComingSoon": "Mehr folgt bald...",
"CreatedBy": "Erstellt von",
"CreatedBy": "Erstellt von {user}",
"And": "und",
"LastUpdated": "Zuletzt aktualisiert",
"VerifyCertificate": "Bitte gib das Zertifikatspasswort ein, um das Zertifikat anzuzeigen.",
Expand Down Expand Up @@ -655,7 +657,9 @@
"Any": "Egal",
"All": "Alle",
"Booked": "Gebucht",
"Mine": "Eigene Ereignisse"
"Mine": "Eigene Ereignisse",
"AllUsers": "Alle Benutzer",
"AllTypes": "Alle Typen"
},
"Sort": {
"Any": "@:List.Filter.Any",
Expand Down Expand Up @@ -792,7 +796,7 @@
"CreateCodingChallenge": "Erstelle eine Coding Challenge",
"UpdateCodingChallenge": "Coding Challenge aktualisieren",
"Delete": "Beschreibung",
"AddNew": "Neue hinzufügen",
"AddNew": "Neue {item} hinzufügen",
"AddTask": "Aufgabe hinzufügen",
"SaveDescription": "Beschreibung speichern",
"DeleteDescription": "Beschreibung löschen",
Expand Down
10 changes: 7 additions & 3 deletions locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@
"EmptyCodingChallenge": "No Coding Challenge Found For This Lecture",
"DeleteQuiz": "Delete Quiz",
"Quiz": "Quiz",
"QuizCreated": "{count} {quizText} created by you",
"QuizCreatedByYou": "This quiz was created by you",
"FEE": "Fee",
"Output": "Output",
"NoCodingChallengeCreated": "No Coding Challenge For This Challenge",
Expand Down Expand Up @@ -450,7 +452,7 @@
"MyCertificates": "The certificates you got by doing an exam! Maybe they are already good to get a job? Or should you get more?",
"GetMorphcoins": "Don't want to pay? You can get coins by other ways",
"MoreComingSoon": "More coming soon...",
"CreatedBy": "Created by",
"CreatedBy": "Created by {user}",
"And": "and",
"LastUpdated": "Last Updated",
"VerifyCertificate": "Please enter certificate password in order to view certificate.",
Expand Down Expand Up @@ -653,7 +655,9 @@
"Any": "Any",
"All": "All",
"Booked": "Booked",
"Mine": "Mine"
"Mine": "Mine",
"AllUsers": "All Users",
"AllTypes": "All Types"
},
"Sort": {
"Any": "@:List.Filter.Any",
Expand Down Expand Up @@ -787,7 +791,7 @@
"UpdateQuiz": "Update Quiz",
"CreateCodingChallenge": "Create Coding Challenge",
"UpdateCodingChallenge": "Update Coding Challenge",
"AddNew": "Add New",
"AddNew": "Add New {item}",
"SaveOutput": "Save Output",
"SaveInput": "Save Input",
"Save": "Save",
Expand Down

0 comments on commit 431e59d

Please sign in to comment.