Skip to content

Commit

Permalink
feat: add countdown module
Browse files Browse the repository at this point in the history
  • Loading branch information
simonostendorf committed Oct 15, 2024
1 parent ba9cce3 commit b08081f
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 56 deletions.
1 change: 1 addition & 0 deletions app/Http/Controllers/Api/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ public function scoreSystemState(): JsonResponse
*
* The definition of the states is as follows:
* setup: The countdown is not set up yet
* idle: The countdown was resetted
* running: The countdown is running
* stopped: The countdown is stopped
*/
Expand Down
5 changes: 2 additions & 3 deletions app/Http/Controllers/DashboardAdminCountdownController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ public function index(): Response
*/
public function indexExecuteSubmit(): JsonResponse
{
dd(Request::all());

// validate the request
$request = Request::validate([
'state' => ['required', 'in:setup,running,stopped'],
'state' => ['required', 'in:setup,idle,running,stopped'],
'time.seconds' => ['required', 'numeric', 'min:0', 'max:59'],
'time.minutes' => ['required', 'numeric', 'min:0', 'max:59'],
'time.hours' => ['required', 'numeric', 'min:0', 'max:23'],
Expand All @@ -53,6 +51,7 @@ public function indexExecuteSubmit(): JsonResponse

// update the state
$state->value = json_encode([
'state' => $request['state'],
'time' => $request['time'],
'direction' => $request['direction'],
]);
Expand Down
8 changes: 4 additions & 4 deletions bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
\Spatie\Permission\PermissionServiceProvider::class,
])
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
web: __DIR__ . '/../routes/web.php',
api: __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
// channels: __DIR__.'/../routes/channels.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo(fn () => route('app.login'));
$middleware->redirectGuestsTo(fn() => route('app.login'));
$middleware->redirectUsersTo(AppServiceProvider::HOME);

$middleware->web(\App\Http\Middleware\HandleInertiaRequests::class);
Expand Down
123 changes: 106 additions & 17 deletions resources/js/pages/Dashboard/Admin/Countdown/Display.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
<template>
<div class="flex h-screen">
<div
class="m-auto bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text font-['EIGHTY_MILES'] text-transparent"
class="m-auto bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text font-['EIGHTY_MILES'] text-transparent text-center"
>
<div v-if="countdownState.time" class="flex flex-row gap-64">
<div class="text-[10rem] leading-[10rem]">
{{ countdownState.time.hours }}
</div>
<div class="text-[10rem] leading-[10rem]">
{{ countdownState.time.minutes }}
</div>
<div class="text-[10rem] leading-[10rem]">
{{ countdownState.time.seconds }}
</div>
<div class="text-[10rem] leading-[10rem]">
{{ countdownState.direction }}
<div
v-if="countdownState.state === 'setup'"
class="text-[10rem] leading-[10rem]"
>
Countdown
</div>
<div
v-else-if="countdownState.state === 'idle'"
class="text-[10rem] leading-[10rem]"
></div>
<div v-else>
<div v-if="countdownState.direction === 'up'">
<div class="text-[4rem] leading-[4rem]">
Maximum:
{{ countdownState.time.hours.toString().padStart(2, "0") }}
:
{{ countdownState.time.minutes.toString().padStart(2, "0") }}
:
{{ countdownState.time.seconds.toString().padStart(2, "0") }}
</div>
</div>
<div class="text-[10rem] leading-[10rem]">
{{ countdownState.state }}
<div>
<div class="text-[10rem] leading-[10rem]">
{{ countdownTime.hours.toString().padStart(2, "0") }}
:
{{ countdownTime.minutes.toString().padStart(2, "0") }}
:
{{ countdownTime.seconds.toString().padStart(2, "0") }}
</div>
</div>
</div>
</div>
Expand All @@ -35,9 +49,19 @@ export default {
<script setup lang="ts">
import { ref, PropType, onBeforeUnmount } from "vue";
const countdownTime = ref<{
seconds: number;
minutes: number;
hours: number;
}>({
seconds: 0,
minutes: 0,
hours: 0,
});
// locale state variables
const countdownState = ref<{
state: "setup" | "running" | "stopped";
state: "setup" | "idle" | "running" | "stopped";
direction: "up" | "down";
time: {
seconds: number;
Expand Down Expand Up @@ -75,15 +99,80 @@ const fetchCountdownState = async () => {
if (response.ok) {
const data = await response.json();
if (
JSON.stringify(data.time) !== JSON.stringify(countdownState.value.time) ||
data.state === "setup" ||
data.state === "idle"
) {
// time was updated or state is setup/idle, resetting local countdown time
if (data.direction === "up") {
countdownTime.value = {
seconds: 0,
minutes: 0,
hours: 0,
};
} else {
countdownTime.value = data.time;
}
}
countdownState.value = data;
}
isFetchingCountdown.value = false;
};
const countdownInterval = setInterval(fetchCountdownState, 1000);
const countdown = async () => {
if (countdownState.value.state === "running") {
if (countdownState.value.direction === "down") {
if (countdownTime.value.seconds > 0) {
countdownTime.value.seconds--;
} else if (countdownTime.value.minutes > 0) {
countdownTime.value.seconds = 59;
countdownTime.value.minutes--;
} else if (countdownTime.value.hours > 0) {
countdownTime.value.seconds = 59;
countdownTime.value.minutes = 59;
countdownTime.value.hours--;
} else {
// nothing, countdown is at 0
}
} else if (countdownState.value.direction === "up") {
if (countdownTime.value.hours >= countdownState.value.time.hours) {
if (countdownTime.value.minutes >= countdownState.value.time.minutes) {
if (
countdownTime.value.seconds >= countdownState.value.time.seconds
) {
// nothing, countdown has reached max value
return;
}
}
}
if (countdownTime.value.seconds < 59) {
countdownTime.value.seconds++;
} else if (countdownTime.value.minutes < 59) {
countdownTime.value.seconds = 0;
countdownTime.value.minutes++;
} else if (countdownTime.value.hours < 23) {
countdownTime.value.seconds = 0;
countdownTime.value.minutes = 0;
countdownTime.value.hours++;
} else {
// nothing, countdown is at max value
}
} else {
// nothing, unknown direction
}
} else {
// nothing, countdown is not running
}
};
const countdownFetchInterval = setInterval(fetchCountdownState, 500);
const countdownInterval = setInterval(countdown, 1000);
onBeforeUnmount(() => {
clearInterval(countdownFetchInterval);
clearInterval(countdownInterval);
});
</script>
93 changes: 61 additions & 32 deletions resources/js/pages/Dashboard/Admin/Countdown/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
<CardBase>
<UiH2>Aktionen</UiH2>

<FormRow>
<div>Aktueller Zustand: {{ state.state }}</div>
</FormRow>

<FormRow>
<AppButton
@click="updateStateAndSubmit('running')"
Expand All @@ -21,6 +25,15 @@
Countdown stoppen
</AppButton>
</FormRow>
<FormRow>
<AppButton
@click="updateStateAndSubmit('idle')"
theme="gray"
class="flex-1 text-center"
>
Countdown idle
</AppButton>
</FormRow>
</CardBase>
<CardBase>
<FormKit
Expand All @@ -34,6 +47,17 @@
<FormRow>
<UiH2>Einstellungen</UiH2>
</FormRow>
<FormRow>
<div>
Wenn der Countdown in Richtung "Aufwärts" zählt, wird die
festgelegte Zeit als Maximum gewertet. Wenn der Countdown in
Richtung "Abwärts" zählt, wird die festgelegte Zeit als
Startwert gewertet und der Countdown zählt bis 0. Wird der
Countdown auf "idle" gestellt, wird nichts mehr angezeigt und
die Zeit wird im Hintergrund zurückgesetzt. "Stopped" stoppt nur
die Anzeige, die Zeit wird nicht zurückgesetzt.
</div>
</FormRow>
<FormRow>
<FormKit
type="number"
Expand Down Expand Up @@ -90,53 +114,58 @@
import { options } from "prettier-plugin-tailwindcss";
import { computed, ref, PropType, onBeforeUnmount } from "vue";
const { state } = defineProps({
state: {
type: Object as PropType<{
time: {
seconds: number;
minutes: number;
hours: number;
};
direction: "up" | "down";
state: "setup" | "idle" | "running" | "stopped";
}>,
required: true,
},
});
console.log("got state");
console.log(state);
console.log(state.time);
console.log("---");
const form = ref<{
seconds: number;
minutes: number;
hours: number;
direction: "up" | "down";
}>({
seconds: 0,
minutes: 0,
hours: 0,
direction: "down",
});
const state = ref<{
state: "setup" | "running" | "stopped";
direction: "up" | "down";
time: {
seconds: number;
minutes: number;
hours: number;
};
}>({
state: "setup",
direction: "down",
time: {
seconds: 0,
minutes: 0,
hours: 0,
},
seconds: state?.time.seconds || 0,
minutes: state?.time.minutes || 0,
hours: state?.time.hours || 0,
direction: state?.direction || "down",
});
const updateStateAndSubmit = (newState: "setup" | "running" | "stopped") => {
state.value.state = newState;
console.log(state.value);
const updateStateAndSubmit = (
newState: "setup" | "idle" | "running" | "stopped",
) => {
state.state = newState;
submitCountdownHandler();
};
const updateTimeAndSubmit = () => {
state.value.time.seconds = form.value.seconds;
state.value.time.minutes = form.value.minutes;
state.value.time.hours = form.value.hours;
state.value.direction = form.value.direction;
console.log(state.value);
console.log(state.value.time);
state.time.seconds = form.value.seconds;
state.time.minutes = form.value.minutes;
state.time.hours = form.value.hours;
state.direction = form.value.direction;
submitCountdownHandler();
};
const submitCountdownHandler = async () => {
console.log("send state");
console.log(state);
console.log(state.time);
console.log("---");
const response = await fetch("/dashboard/admin/countdown", {
method: "POST",
credentials: "include",
Expand All @@ -147,7 +176,7 @@ const submitCountdownHandler = async () => {
.querySelector("meta[name='csrf-token']")
?.getAttribute("content") || "",
},
body: JSON.stringify(state.value),
body: JSON.stringify(state),
});
};
</script>

0 comments on commit b08081f

Please sign in to comment.