Skip to content

Commit

Permalink
add learn functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Yanni8 committed Mar 31, 2024
1 parent 228fcb8 commit a68921e
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 1 deletion.
15 changes: 14 additions & 1 deletion app/Http/Controllers/SetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Http\Requests\Set\Import;
use App\Models\Set;
use Inertia\Inertia;

class SetController extends Controller
{
Expand Down Expand Up @@ -39,6 +40,18 @@ public function import(Import $request)

$set->cards()->createMany($cards);

return $set;
return redirect("/train/{$set->id}");
}

public function train(string $set)
{

$set = Set::findOrFail($set);
$initial_flash_cards = $set->cards()->get();

return Inertia::render("Set/Train", [
"set" => $set,
"initialFlashCards" => $initial_flash_cards,
]);
}
}
121 changes: 121 additions & 0 deletions resources/js/Components/FlashCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<template>
<div
class="w-3/4 m-auto flex items-center justify-center"
:class="{ flipped: !gotFlipped, back: flipped && gotFlipped }"
>
<div
@click="$emit('flip')"
class="w-[min(70%,50rem)] mx-20 h-1/2 py-[min(20%,15rem)] flex justify-center cursor-pointer"
id="flashcard"
>
<h1 class="font-bold text-3xl select-none">{{ displayedValue }}</h1>
</div>
</div>
</template>
<script>
export default {
name: "FlashCard",
emits: ["flip"],
props: {
question: {
type: String,
required: true,
},
answer: {
type: String,
required: true,
},
flipped: {
type: Boolean,
required: true,
},
},
data() {
return {
displayedValue: this.question,
gotFlipped: true,
};
},
watch: {
flipped(newValue) {
this.gotFlipped = false;
new Promise((resolve) => setTimeout(resolve, 150)).then(
() =>
(this.displayedValue = newValue
? this.answer
: this.question)
);
new Promise((resolve) => setTimeout(resolve, 320)).then(
() => (this.gotFlipped = true)
);
},
answer(newAnswer) {
this.displayedValue = newAnswer;
},
question(newQuestion) {
this.displayedValue = newQuestion;
},
},
};
</script>

<style lang="scss">
#flashcard {
position: relative;
box-sizing: border-box;
color: #fff;
background: #222222;
background-clip: padding-box;
border: solid 2px transparent;
border-radius: 1em;
&:before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
margin: -2px;
border-radius: inherit;
background: linear-gradient(315deg, #42d392 25%, #647eff);
}
}
.flipped {
animation: rotate 0.3s linear forwards;
}
.flipped h1 {
animation: mirror 0.15s forwards;
}
.back div::before {
background: linear-gradient(225deg, #42d392 25%, #647eff) !important;
}
@keyframes rotate {
0% {
transform: rotateX(0deg);
}
50% {
transform: rotateX(90deg);
}
100% {
transform: rotateX(180deg);
}
}
@keyframes mirror {
0% {
}
100% {
transform: scaleY(-1);
}
}
</style>
43 changes: 43 additions & 0 deletions resources/js/Components/Progress.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<div class="w-full mb-16 relative">
<div class="h-3 relative">
<div class="bg-gray-50 h-3 rounded-lg"></div>
<div
id="progressbar"
class="absolute h-3 rounded-lg top-0"
:style="{ width: percentage }"
></div>
</div>
<div class="absolute right-0">{{ ptr }} / {{ max }}</div>
</div>
</template>
<script>
export default {
name: "LearnProgress",
props: {
ptr: {
type: Number,
required: true,
},
max: {
type: Number,
required: true,
},
},
computed: {
percentage() {
return Math.floor((this.ptr / this.max) * 100) + "%";
},
},
};
</script>
<style>
#progressbar {
background: linear-gradient(
297deg,
rgba(91, 255, 152, 1) 0%,
rgba(148, 190, 233, 1) 100%
);
}
</style>
93 changes: 93 additions & 0 deletions resources/js/Pages/Set/Train.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<template>
<div class="m-12 t-18 flex flex-col items-start">
<learn-progress :ptr="completedCards" :max="totalCards"></learn-progress>
<flash-card
:question="currentFlashCard.key"
:answer="currentFlashCard.value"
:flipped="showAnswer"
@flip="showAnswer = !showAnswer"
></flash-card>
<div class="mt-8 w-40 mx-auto flex justify-between items-center">
<i @click="repeat" class="fa-solid fa-x text-xl cursor-pointer text-red-500 hover:text-red-600"></i>
<i
@click="this.showAnswer = !this.showAnswer"
class="fa-solid fa-repeat cursor-pointer hover:text-gray-300"
></i>
<i
@click="completed"
class="fa-solid fa-check text-xl cursor-pointer text-green-500 hover:text-green-600"
></i>
</div>
</div>
</template>

<script>
import FlashCard from "../../Components/FlashCard.vue";
import LearnProgress from "../../Components/Progress.vue";
export default {
name: "Train",
components: { FlashCard, LearnProgress },
props: {
set: {
type: Object,
required: false,
default: () => {},
},
initialFlashCards: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
flashCards: this.initialFlashCards,
currentFlashCard: this.initialFlashCards[0],
currentIndex: 0,
completedCards: 0,
totalCards: this.initialFlashCards.length,
showAnswer: false,
};
},
methods: {
click(event) {
if (event.code === "Space") {
this.showAnswer = !this.showAnswer;
}
},
repeat() {
this.nextCard();
},
completed() {
this.flashCards.pop(this.currentIndex);
this.completedCards++;
this.nextCard();
},
nextCard() {
this.currentIndex = this.getRandomInt(
this.totalCards - this.completedCards
);
this.currentFlashCard = this.flashCards[this.currentIndex];
},
getRandomInt(max) {
return Math.floor(Math.random() * max);
},
},
mounted() {
document.addEventListener("keydown", this.click);
},
beforeUnmount() {
document.removeEventListener("keydown", this.click);
},
};
</script>

<style></style>
2 changes: 2 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@

Route::post("/import", [SetController::class, 'import']);

Route::get("/train/{set}", [SetController::class, 'train']);

require __DIR__.'/auth.php';

0 comments on commit a68921e

Please sign in to comment.