WIP: frontend optimierungen & sprachkorrekturen
This commit is contained in:
@@ -5,7 +5,6 @@ namespace App\Http\Controllers;
|
|||||||
use App\Models\Image;
|
use App\Models\Image;
|
||||||
use App\Settings\GeneralSettings;
|
use App\Settings\GeneralSettings;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\Lang;
|
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
@@ -14,11 +13,6 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$locale = app()->getLocale();
|
|
||||||
$translations = array_merge(
|
|
||||||
Lang::get('api', [], $locale),
|
|
||||||
Lang::get('settings', [], $locale)
|
|
||||||
);
|
|
||||||
$galleryHeading = $this->settings->gallery_heading;
|
$galleryHeading = $this->settings->gallery_heading;
|
||||||
$newImageTimespanMinutes = $this->settings->new_image_timespan_minutes;
|
$newImageTimespanMinutes = $this->settings->new_image_timespan_minutes;
|
||||||
|
|
||||||
@@ -30,7 +24,6 @@ class HomeController extends Controller
|
|||||||
});
|
});
|
||||||
|
|
||||||
return Inertia::render('Home', [
|
return Inertia::render('Home', [
|
||||||
'translations' => $translations,
|
|
||||||
'galleryHeading' => $galleryHeading,
|
'galleryHeading' => $galleryHeading,
|
||||||
'images' => $images,
|
'images' => $images,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -2,19 +2,19 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<p
|
<p
|
||||||
v-if="!images.length"
|
v-if="!images.length"
|
||||||
class="rounded-3xl border border-dashed border-white/20 bg-white/5 px-4 py-10 text-center text-sm text-slate-300"
|
class="rounded-3xl border border-dashed border-slate-300 bg-white px-4 py-10 text-center text-sm text-slate-500 dark:border-white/20 dark:bg-white/5 dark:text-slate-300"
|
||||||
>
|
>
|
||||||
{{ props.translations.empty_gallery || 'Noch keine Bilder vorhanden.' }}
|
{{ __('api.gallery.empty') }}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="grid grid-cols-2 gap-4 sm:grid-cols-3 sm:gap-6 xl:grid-cols-4 2xl:grid-cols-5"
|
class="grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-5 xl:grid-cols-4 2xl:grid-cols-5"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-for="image in images"
|
v-for="image in images"
|
||||||
:key="image.id ?? image.name"
|
:key="image.id ?? image.name"
|
||||||
type="button"
|
type="button"
|
||||||
class="group relative aspect-[3/4] overflow-hidden rounded-[1.75rem] border border-white/10 bg-slate-900/30 shadow-2xl ring-1 ring-white/5 transition-all hover:border-emerald-300 focus:outline-none focus-visible:ring-4 focus-visible:ring-emerald-400"
|
class="group relative aspect-[4/3] overflow-hidden rounded-[1.75rem] border border-slate-200 bg-white shadow-2xl ring-1 ring-slate-100 transition-all hover:border-emerald-300 focus:outline-none focus-visible:ring-4 focus-visible:ring-emerald-400 dark:border-white/10 dark:bg-slate-900/30 dark:ring-white/5"
|
||||||
@click="$emit('imageTapped', image, $event)"
|
@click="$emit('imageTapped', image, $event)"
|
||||||
:aria-label="fallbackLabel(image)"
|
:aria-label="fallbackLabel(image)"
|
||||||
>
|
>
|
||||||
@@ -31,9 +31,9 @@
|
|||||||
<span class="h-1.5 w-1.5 rounded-full bg-emerald-800"></span>
|
<span class="h-1.5 w-1.5 rounded-full bg-emerald-800"></span>
|
||||||
{{ __('new') }}
|
{{ __('new') }}
|
||||||
</span>
|
</span>
|
||||||
<div class="pointer-events-none absolute inset-x-0 bottom-0 bg-gradient-to-t from-slate-950/70 via-slate-950/10 to-transparent p-4 text-left">
|
<div class="pointer-events-none absolute inset-x-0 bottom-0 bg-gradient-to-t from-slate-900/80 via-slate-900/10 to-transparent p-4 text-left text-white">
|
||||||
<p class="text-base font-semibold text-white drop-shadow">{{ fallbackLabel(image) }}</p>
|
<p class="text-base font-semibold drop-shadow">{{ fallbackLabel(image) }}</p>
|
||||||
<p class="text-xs uppercase tracking-[0.3em] text-white/70">Tap to open</p>
|
<p class="text-xs uppercase tracking-[0.3em] text-white/70">{{ __('api.gallery.tap_to_open') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,17 +48,9 @@ const props = defineProps({
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
translations: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['imageTapped']);
|
const emits = defineEmits(['imageTapped']);
|
||||||
|
|
||||||
const __ = (key) => {
|
const fallbackLabel = (image) => image?.name || 'Bild';
|
||||||
return props.translations[key] || key;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fallbackLabel = (image) => image?.name || props.translations.image || 'Bild';
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
|
<div class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
|
||||||
<div class="bg-white p-6 rounded-lg shadow-lg text-center flex flex-col items-center">
|
<div class="bg-white p-6 rounded-lg shadow-lg text-center flex flex-col items-center">
|
||||||
<div class="loader ease-linear rounded-full border-4 border-t-4 border-gray-200 h-12 w-12 mb-4"></div>
|
<div class="loader ease-linear rounded-full border-4 border-t-4 border-gray-200 h-12 w-12 mb-4"></div>
|
||||||
<p class="text-gray-700 text-lg">{{ __('loading_spinner.processing_image') }}</p>
|
<p class="text-gray-700 text-lg">{{ __('api.loading_spinner.processing_image') }}</p>
|
||||||
<p v-if="progress > 0" class="text-gray-700 text-sm mt-2">{{ progress }}%</p>
|
<p v-if="progress > 0" class="text-gray-700 text-sm mt-2">{{ progress }}%</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="rounded-3xl border border-white/10 bg-white/5 p-5 text-white shadow-2xl backdrop-blur">
|
<nav class="rounded-3xl border border-slate-200 bg-white p-5 text-slate-900 shadow-2xl backdrop-blur transition-colors dark:border-white/10 dark:bg-white/5 dark:text-white">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-4">
|
<div class="flex flex-wrap items-center justify-between gap-4">
|
||||||
<div class="flex items-center gap-2 text-xs uppercase tracking-[0.4em] text-slate-300">
|
<div class="flex items-center gap-2 text-xs uppercase tracking-[0.4em] text-slate-500 dark:text-slate-300">
|
||||||
Seite
|
Seite
|
||||||
<span class="text-xl font-semibold text-white">{{ currentPage }}</span>
|
<span class="text-xl font-semibold text-slate-900 dark:text-white">{{ currentPage }}</span>
|
||||||
<span class="text-slate-500">/</span>
|
<span class="text-slate-400 dark:text-slate-500">/</span>
|
||||||
<span class="text-lg text-slate-200">{{ totalPages }}</span>
|
<span class="text-lg text-slate-700 dark:text-slate-200">{{ totalPages }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-full border border-white/20 bg-white/10 px-4 py-2 text-sm font-semibold text-white transition hover:border-cyan-300 hover:text-cyan-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300 disabled:opacity-50"
|
class="inline-flex items-center gap-2 rounded-full border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-900 transition hover:border-cyan-300 hover:text-cyan-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300 disabled:opacity-50 dark:border-white/20 dark:bg-white/10 dark:text-white dark:hover:text-cyan-200"
|
||||||
@click="$emit('prevPage')"
|
@click="$emit('prevPage')"
|
||||||
:disabled="currentPage === 1"
|
:disabled="currentPage === 1"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 19.5-7.5-7.5 7.5-7.5" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 19.5-7.5-7.5 7.5-7.5" />
|
||||||
</svg>
|
</svg>
|
||||||
{{ __('navigation.previous') }}
|
{{ __('api.navigation.previous') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-full border border-emerald-300 bg-emerald-400/20 px-4 py-2 text-sm font-semibold text-emerald-100 transition hover:bg-emerald-400/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-300 disabled:opacity-50"
|
class="inline-flex items-center gap-2 rounded-full border border-emerald-300 bg-emerald-50 px-4 py-2 text-sm font-semibold text-emerald-700 transition hover:bg-emerald-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-300 disabled:opacity-50 dark:bg-emerald-400/20 dark:text-emerald-100 dark:hover:bg-emerald-400/40"
|
||||||
@click="$emit('nextPage')"
|
@click="$emit('nextPage')"
|
||||||
:disabled="currentPage === totalPages"
|
:disabled="currentPage === totalPages"
|
||||||
>
|
>
|
||||||
{{ __('navigation.next') }}
|
{{ __('api.navigation.next') }}
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fixed inset-0 z-[120] flex items-center justify-center bg-slate-950/70 px-6 py-10 backdrop-blur" @click.self="$emit('close')">
|
<div class="fixed inset-0 z-[120] flex items-center justify-center bg-slate-900/30 px-6 py-10 backdrop-blur dark:bg-slate-950/70" @click.self="$emit('close')">
|
||||||
<div class="w-full max-w-md rounded-[2.5rem] border border-white/10 bg-gradient-to-b from-slate-900 via-slate-900/90 to-slate-950 p-8 text-white shadow-[0_35px_80px_rgba(2,6,23,0.65)]">
|
<div class="w-full max-w-md rounded-[2.5rem] border border-slate-200 bg-gradient-to-b from-white via-white to-slate-50 p-8 text-slate-900 shadow-[0_35px_80px_rgba(2,6,23,0.15)] transition-colors dark:border-white/10 dark:from-slate-900 dark:via-slate-900/90 dark:to-slate-950 dark:text-white dark:shadow-[0_35px_80px_rgba(2,6,23,0.65)]">
|
||||||
<div class="flex items-start justify-between gap-4">
|
<div class="flex items-start justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs uppercase tracking-[0.5em] text-slate-400">{{ __('api.print_dialog.title') }}</p>
|
<p class="text-xs uppercase tracking-[0.5em] text-slate-400">{{ __('api.print_dialog.title') }}</p>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-full border border-white/10 p-2 text-slate-200 transition hover:border-rose-300 hover:text-rose-300"
|
class="rounded-full border border-slate-200 p-2 text-slate-500 transition hover:border-rose-300 hover:text-rose-400 dark:border-white/10 dark:text-slate-200"
|
||||||
@click="$emit('close')"
|
@click="$emit('close')"
|
||||||
:aria-label="__('api.print_dialog.cancel_button')"
|
:aria-label="__('api.print_dialog.cancel_button')"
|
||||||
>
|
>
|
||||||
@@ -23,12 +23,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 flex flex-col gap-6">
|
<div class="mt-8 flex flex-col gap-6">
|
||||||
<div class="rounded-3xl border border-white/10 bg-white/5 p-6 text-center text-slate-200">
|
<div class="rounded-3xl border border-slate-200 bg-white p-6 text-center text-slate-600 dark:border-white/10 dark:bg-white/5 dark:text-slate-200">
|
||||||
<p class="text-sm uppercase tracking-[0.4em] text-slate-400">Anzahl</p>
|
<p class="text-sm uppercase tracking-[0.4em] text-slate-400">Anzahl</p>
|
||||||
<div class="mt-4 flex items-center justify-center gap-6">
|
<div class="mt-4 flex items-center justify-center gap-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex h-14 w-14 items-center justify-center rounded-2xl border border-white/20 bg-white/10 text-3xl font-semibold text-white transition hover:border-emerald-300 hover:text-emerald-200"
|
class="flex h-14 w-14 items-center justify-center rounded-2xl border border-slate-200 bg-white text-3xl font-semibold text-slate-900 transition hover:border-emerald-300 hover:text-emerald-600 dark:border-white/20 dark:bg-white/10 dark:text-white dark:hover:text-emerald-200"
|
||||||
@click="decrementQuantity"
|
@click="decrementQuantity"
|
||||||
aria-label="-1"
|
aria-label="-1"
|
||||||
>
|
>
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
<div class="min-w-[4rem] text-6xl font-bold tracking-tight text-white">{{ quantity }}</div>
|
<div class="min-w-[4rem] text-6xl font-bold tracking-tight text-white">{{ quantity }}</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex h-14 w-14 items-center justify-center rounded-2xl border border-white/20 bg-white/10 text-3xl font-semibold text-white transition hover:border-emerald-300 hover:text-emerald-200"
|
class="flex h-14 w-14 items-center justify-center rounded-2xl border border-slate-200 bg-white text-3xl font-semibold text-slate-900 transition hover:border-emerald-300 hover:text-emerald-600 dark:border-white/20 dark:bg-white/10 dark:text-white dark:hover:text-emerald-200"
|
||||||
@click="incrementQuantity"
|
@click="incrementQuantity"
|
||||||
aria-label="+1"
|
aria-label="+1"
|
||||||
>
|
>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-center text-xs uppercase tracking-[0.4em] text-slate-400">
|
<p class="text-center text-xs uppercase tracking-[0.4em] text-slate-500 dark:text-slate-400">
|
||||||
Du steuerst direkt den Sofortdruck.
|
Du steuerst direkt den Sofortdruck.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,14 +54,14 @@
|
|||||||
<div class="mt-8 flex flex-wrap gap-3">
|
<div class="mt-8 flex flex-wrap gap-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex-1 rounded-full border border-white/20 bg-white/5 px-5 py-3 text-center text-sm font-semibold uppercase tracking-[0.2em] text-slate-200 transition hover:border-rose-300 hover:text-rose-200"
|
class="flex-1 rounded-full border border-slate-300 bg-white px-5 py-3 text-center text-sm font-semibold uppercase tracking-[0.2em] text-slate-600 transition hover:border-rose-300 hover:text-rose-400 dark:border-white/20 dark:bg-white/5 dark:text-slate-200 dark:hover:text-rose-200"
|
||||||
@click="$emit('close')"
|
@click="$emit('close')"
|
||||||
>
|
>
|
||||||
{{ __('api.print_dialog.cancel_button') }}
|
{{ __('api.print_dialog.cancel_button') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex-1 rounded-full border border-emerald-300 bg-emerald-400/20 px-5 py-3 text-center text-sm font-semibold uppercase tracking-[0.2em] text-emerald-100 transition hover:bg-emerald-400/40"
|
class="flex-1 rounded-full border border-emerald-400 bg-emerald-50 px-5 py-3 text-center text-sm font-semibold uppercase tracking-[0.2em] text-emerald-700 transition hover:bg-emerald-100 dark:bg-emerald-400/20 dark:text-emerald-100 dark:hover:bg-emerald-400/40"
|
||||||
@click="confirmPrint"
|
@click="confirmPrint"
|
||||||
>
|
>
|
||||||
{{ __('api.print_dialog.print_button') }}
|
{{ __('api.print_dialog.print_button') }}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-full flex-col gap-4 rounded-3xl border border-white/10 bg-white/5 p-4 text-white">
|
<div class="flex h-full flex-col gap-4 rounded-3xl border border-slate-200 bg-white p-4 text-slate-900 dark:border-white/10 dark:bg-white/5 dark:text-white">
|
||||||
<div class="flex items-center justify-between gap-2 text-xs uppercase tracking-[0.3em] text-slate-300">
|
<div class="flex items-center justify-between gap-2 text-xs uppercase tracking-[0.3em] text-slate-500 dark:text-slate-300">
|
||||||
<span>Varianten</span>
|
<span>Varianten</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-full border border-white/20 p-1 text-white/70 transition hover:border-rose-300 hover:text-rose-300"
|
class="rounded-full border border-slate-200 p-1 text-slate-500 transition hover:border-rose-300 hover:text-rose-400 dark:border-white/20 dark:text-white/70 dark:hover:text-rose-300"
|
||||||
@click="$emit('close')"
|
@click="$emit('close')"
|
||||||
aria-label="Auswahl schließen"
|
aria-label="Auswahl schließen"
|
||||||
>
|
>
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 space-y-3 overflow-y-auto pr-1">
|
<div class="flex-1 space-y-3 overflow-y-auto pr-1">
|
||||||
<p v-if="isLoading" class="rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-center text-sm text-slate-300">
|
<p v-if="isLoading" class="rounded-2xl border border-slate-200 bg-white px-4 py-3 text-center text-sm text-slate-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300">
|
||||||
Stile werden geladen …
|
Stile werden geladen …
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="loadError" class="rounded-2xl border border-rose-400/40 bg-rose-500/10 px-4 py-3 text-center text-sm text-rose-100">
|
<p v-else-if="loadError" class="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-center text-sm text-rose-500 dark:border-rose-400/40 dark:bg-rose-500/10 dark:text-rose-100">
|
||||||
{{ loadError }}
|
{{ loadError }}
|
||||||
</p>
|
</p>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
v-for="style in styles"
|
v-for="style in styles"
|
||||||
:key="style.id"
|
:key="style.id"
|
||||||
type="button"
|
type="button"
|
||||||
class="group flex w-full items-center gap-4 rounded-2xl border border-white/10 bg-white/5 p-3 text-left transition hover:border-emerald-300"
|
class="group flex w-full items-center gap-4 rounded-2xl border border-slate-200 bg-white/90 p-3 text-left text-slate-900 transition hover:border-emerald-300 hover:bg-emerald-50/50 dark:border-white/10 dark:bg-white/5 dark:text-white"
|
||||||
@click="selectStyle(style)"
|
@click="selectStyle(style)"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -38,13 +38,13 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex h-24 w-24 flex-none items-center justify-center rounded-2xl border border-dashed border-white/20 text-[0.65rem] uppercase tracking-[0.4em] text-slate-400"
|
class="flex h-24 w-24 flex-none items-center justify-center rounded-2xl border border-dashed border-slate-200 text-[0.65rem] uppercase tracking-[0.4em] text-slate-500 dark:border-white/20 dark:text-slate-400"
|
||||||
>
|
>
|
||||||
AI
|
AI
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<p class="text-base font-semibold text-white">{{ style.title }}</p>
|
<p class="text-base font-semibold text-slate-900 dark:text-white">{{ style.title }}</p>
|
||||||
<p class="text-sm text-slate-200">{{ style.description }}</p>
|
<p class="text-sm text-slate-500 dark:text-slate-200">{{ style.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
|
<div class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
|
||||||
<div class="bg-white p-4 rounded-lg shadow-lg max-w-3xl w-full text-center">
|
<div class="bg-white p-4 rounded-lg shadow-lg max-w-3xl w-full text-center">
|
||||||
<h2 class="text-xl font-bold mb-4">{{ __('styled_image_display.title') }}</h2>
|
<h2 class="text-xl font-bold mb-4">{{ __('api.styled_image_display.title') }}</h2>
|
||||||
<img :src="image.path" alt="Styled Image" class="max-w-full h-auto mx-auto mb-4 rounded-md" />
|
<img :src="image.path" alt="Styled Image" class="max-w-full h-auto mx-auto mb-4 rounded-md" />
|
||||||
<div class="flex justify-center space-x-4">
|
<div class="flex justify-center space-x-4">
|
||||||
<button
|
<button
|
||||||
@click="$emit('keep', image)"
|
@click="$emit('keep', image)"
|
||||||
class="px-6 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 focus:outline-hidden focus:ring-2 focus:ring-green-500 focus:ring-opacity-50"
|
class="px-6 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 focus:outline-hidden focus:ring-2 focus:ring-green-500 focus:ring-opacity-50"
|
||||||
>
|
>
|
||||||
{{ __('styled_image_display.keep_button') }}
|
{{ __('api.styled_image_display.keep_button') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@click="$emit('delete', image)"
|
@click="$emit('delete', image)"
|
||||||
class="px-6 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 focus:outline-hidden focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
|
class="px-6 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 focus:outline-hidden focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
|
||||||
>
|
>
|
||||||
{{ __('styled_image_display.delete_button') }}
|
{{ __('api.styled_image_display.delete_button') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<Head title="Start" />
|
<Head title="Start" />
|
||||||
<div class="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 text-slate-100">
|
<div class="min-h-screen bg-gradient-to-br from-slate-100 via-white to-slate-200 text-slate-900 transition-colors duration-500 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950 dark:text-slate-100">
|
||||||
<div class="mx-auto flex w-full max-w-7xl flex-col gap-6 px-4 py-8 sm:px-6 lg:px-8">
|
<div class="mx-auto flex w-full max-w-7xl flex-col gap-6 px-2 py-8 sm:px-4 lg:px-6">
|
||||||
<header class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-2xl backdrop-blur">
|
<header class="rounded-3xl border border-slate-200 bg-white/90 p-6 text-slate-900 shadow-2xl backdrop-blur transition-colors duration-300 dark:border-white/10 dark:bg-white/5 dark:text-white">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-6">
|
<div class="flex flex-wrap items-start justify-between gap-6">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs uppercase tracking-[0.35em] text-slate-400">Live Gallery</p>
|
<p class="text-xs uppercase tracking-[0.35em] text-slate-500 dark:text-slate-400">Live Gallery</p>
|
||||||
<h1 class="mt-2 text-3xl font-semibold text-white sm:text-4xl">
|
<h1 class="mt-1 text-3xl font-semibold text-slate-900 dark:text-white sm:text-4xl">
|
||||||
{{ props.galleryHeading }}
|
{{ props.galleryHeading }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-1 text-sm text-slate-400">Touch-friendly wall with the freshest images.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-full border border-white/20 bg-white/10 px-4 py-2 text-sm font-semibold text-white transition hover:border-emerald-400 hover:text-emerald-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400 disabled:opacity-60"
|
class="inline-flex items-center gap-2 rounded-full border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-900 shadow-sm transition hover:border-emerald-400 hover:text-emerald-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400 disabled:opacity-60 dark:border-white/20 dark:bg-white/10 dark:text-white dark:hover:text-emerald-200"
|
||||||
@click="handleManualRefresh"
|
@click="handleManualRefresh"
|
||||||
:disabled="isRefreshing"
|
:disabled="isRefreshing"
|
||||||
>
|
>
|
||||||
@@ -36,7 +35,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-2 text-sm font-semibold text-white transition hover:border-cyan-300 hover:text-cyan-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400"
|
class="inline-flex items-center gap-2 rounded-full border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-900 shadow-sm transition hover:border-cyan-400 hover:text-cyan-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400 dark:border-white/10 dark:bg-white/5 dark:text-white dark:hover:text-cyan-200"
|
||||||
@click="toggleTheme"
|
@click="toggleTheme"
|
||||||
>
|
>
|
||||||
<svg v-if="currentTheme === 'light'" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
<svg v-if="currentTheme === 'light'" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
@@ -53,9 +52,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 flex flex-wrap items-center gap-4 text-xs text-slate-300">
|
<div class="mt-4 flex flex-wrap items-center gap-4 text-xs text-slate-500 dark:text-slate-300">
|
||||||
<div class="inline-flex items-center gap-2 rounded-full bg-emerald-400/10 px-3 py-1 text-emerald-200">
|
<div class="inline-flex items-center gap-2 rounded-full bg-emerald-500/10 px-3 py-1 text-emerald-700 dark:text-emerald-200">
|
||||||
<span class="h-2 w-2 rounded-full bg-emerald-400 animate-pulse"></span>
|
<span class="h-2 w-2 rounded-full bg-emerald-500 animate-pulse"></span>
|
||||||
Auto-Refresh · alle {{ refreshIntervalSeconds }}s
|
Auto-Refresh · alle {{ refreshIntervalSeconds }}s
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -72,12 +71,12 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="rounded-3xl border border-white/5 bg-white/5 p-4 shadow-2xl backdrop-blur touch-pan-y"
|
class="rounded-3xl border border-slate-200 bg-white/80 p-3 shadow-2xl backdrop-blur transition-colors duration-300 touch-pan-y dark:border-white/5 dark:bg-white/5 sm:p-4"
|
||||||
@touchstart.passive="handleTouchStart"
|
@touchstart.passive="handleTouchStart"
|
||||||
@touchmove.passive="handleTouchMove"
|
@touchmove.passive="handleTouchMove"
|
||||||
@touchend="handleTouchEnd"
|
@touchend="handleTouchEnd"
|
||||||
>
|
>
|
||||||
<GalleryGrid :images="paginatedImages" @imageTapped="showContextMenu" :translations="props.translations" />
|
<GalleryGrid :images="paginatedImages" @imageTapped="showContextMenu" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Navigation :currentPage="currentPage" :totalPages="totalPages" @prevPage="prevPage" @nextPage="nextPage" />
|
<Navigation :currentPage="currentPage" :totalPages="totalPages" @prevPage="prevPage" @nextPage="nextPage" />
|
||||||
@@ -133,10 +132,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'Gallery',
|
default: 'Gallery',
|
||||||
},
|
},
|
||||||
translations: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
images: {
|
images: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ library.add(faPrint, faMagicWandSparkles, faXmark);
|
|||||||
|
|
||||||
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
||||||
|
|
||||||
|
const resolveTranslation = (source, path) => {
|
||||||
|
if (!source || !path) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.split('.').reduce((acc, segment) => {
|
||||||
|
if (acc && Object.prototype.hasOwnProperty.call(acc, segment)) {
|
||||||
|
return acc[segment];
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}, source);
|
||||||
|
};
|
||||||
|
|
||||||
createInertiaApp({
|
createInertiaApp({
|
||||||
title: (title) => `${title} - ${appName}`,
|
title: (title) => `${title} - ${appName}`,
|
||||||
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
|
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
|
||||||
@@ -27,18 +41,29 @@ createInertiaApp({
|
|||||||
.component('font-awesome-icon', FontAwesomeIcon) // Register Font Awesome component
|
.component('font-awesome-icon', FontAwesomeIcon) // Register Font Awesome component
|
||||||
.mixin({
|
.mixin({
|
||||||
methods: {
|
methods: {
|
||||||
__: (key, replace = {}) => {
|
__: function (key, replace = {}) {
|
||||||
let translation = props.initialPage.props.translations[key];
|
const sources = [
|
||||||
|
this?.$page?.props?.translations,
|
||||||
|
props.initialPage.props.translations,
|
||||||
|
];
|
||||||
|
|
||||||
if (translation === undefined) {
|
let translation;
|
||||||
translation = key; // Fallback to key if translation not found
|
|
||||||
|
for (const source of sources) {
|
||||||
|
const value = resolveTranslation(source, key);
|
||||||
|
if (value !== undefined) {
|
||||||
|
translation = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let placeholder in replace) {
|
let output = translation ?? key;
|
||||||
translation = translation.replace(`:${placeholder}`, replace[placeholder]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return translation;
|
Object.entries(replace).forEach(([placeholder, val]) => {
|
||||||
|
output = output.replace(`:${placeholder}`, val);
|
||||||
|
});
|
||||||
|
|
||||||
|
return output;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -47,4 +72,4 @@ createInertiaApp({
|
|||||||
progress: {
|
progress: {
|
||||||
color: '#4B5563',
|
color: '#4B5563',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,23 +8,35 @@ return [
|
|||||||
'image_deleted_successfully' => 'Bild erfolgreich gelöscht.',
|
'image_deleted_successfully' => 'Bild erfolgreich gelöscht.',
|
||||||
'image_or_provider_not_found' => 'Bild oder API-Anbieter nicht gefunden.',
|
'image_or_provider_not_found' => 'Bild oder API-Anbieter nicht gefunden.',
|
||||||
'no_styles_available' => 'Keine Stile oder API-Anbieter aktiviert/verfügbar.',
|
'no_styles_available' => 'Keine Stile oder API-Anbieter aktiviert/verfügbar.',
|
||||||
'api.dark_mode' => 'Dunkler Modus',
|
'dark_mode' => 'Dunkler Modus',
|
||||||
'api.light_mode' => 'Heller Modus',
|
'light_mode' => 'Heller Modus',
|
||||||
'gallery_title' => 'Eure Bilder aus der Fotobox',
|
'gallery_title' => 'Eure Bilder aus der Fotobox',
|
||||||
'navigation.previous' => 'Zurück',
|
'navigation' => [
|
||||||
'navigation.next' => 'Weiter',
|
'previous' => 'Zurück',
|
||||||
'navigation.page_of' => 'Seite :currentPage von :totalPages',
|
'next' => 'Weiter',
|
||||||
'loading_spinner.processing_image' => 'Bild wird verarbeitet...',
|
'page_of' => 'Seite :currentPage von :totalPages',
|
||||||
'styled_image_display.title' => 'Neu gestyltes Bild',
|
],
|
||||||
'styled_image_display.keep_button' => 'Behalten',
|
'loading_spinner' => [
|
||||||
'styled_image_display.delete_button' => 'Löschen',
|
'processing_image' => 'Bild wird verarbeitet...',
|
||||||
|
],
|
||||||
|
'styled_image_display' => [
|
||||||
|
'title' => 'Neu gestyltes Bild',
|
||||||
|
'keep_button' => 'Behalten',
|
||||||
|
'delete_button' => 'Löschen',
|
||||||
|
],
|
||||||
'print_command_sent_successfully' => 'Druckbefehl erfolgreich gesendet.',
|
'print_command_sent_successfully' => 'Druckbefehl erfolgreich gesendet.',
|
||||||
'failed_to_send_print_command' => 'Druckbefehl konnte nicht gesendet werden.',
|
'failed_to_send_print_command' => 'Druckbefehl konnte nicht gesendet werden.',
|
||||||
'api.print_dialog.title' => 'Bild drucken',
|
'print_dialog' => [
|
||||||
'api.print_dialog.quantity_prompt' => 'Wie viele Kopien möchten Sie drucken?',
|
'title' => 'Bild drucken',
|
||||||
'api.print_dialog.cancel_button' => 'Abbrechen',
|
'quantity_prompt' => 'Wie viele Kopien möchtest du drucken?',
|
||||||
'api.print_dialog.print_button' => 'Drucken',
|
'cancel_button' => 'Abbrechen',
|
||||||
'api.download_button' => 'Herunterladen',
|
'print_button' => 'Drucken',
|
||||||
'api.download_success' => 'Download gestartet!',
|
],
|
||||||
'api.download_error' => 'Download fehlgeschlagen.',
|
'download_button' => 'Herunterladen',
|
||||||
|
'download_success' => 'Download gestartet!',
|
||||||
|
'download_error' => 'Download fehlgeschlagen.',
|
||||||
|
'gallery' => [
|
||||||
|
'tap_to_open' => 'Zum Öffnen tippen',
|
||||||
|
'empty' => 'Noch keine Bilder vorhanden.',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -11,20 +11,32 @@ return [
|
|||||||
'dark_mode' => 'Dark Mode',
|
'dark_mode' => 'Dark Mode',
|
||||||
'light_mode' => 'Light Mode',
|
'light_mode' => 'Light Mode',
|
||||||
'gallery_title' => 'Your images from the photobooth',
|
'gallery_title' => 'Your images from the photobooth',
|
||||||
'navigation.previous' => 'Previous',
|
'navigation' => [
|
||||||
'navigation.next' => 'Next',
|
'previous' => 'Previous',
|
||||||
'navigation.page_of' => 'Page :currentPage of :totalPages',
|
'next' => 'Next',
|
||||||
'loading_spinner.processing_image' => 'Processing image...',
|
'page_of' => 'Page :currentPage of :totalPages',
|
||||||
'styled_image_display.title' => 'Newly Styled Image',
|
],
|
||||||
'styled_image_display.keep_button' => 'Keep',
|
'loading_spinner' => [
|
||||||
'styled_image_display.delete_button' => 'Delete',
|
'processing_image' => 'Processing image...',
|
||||||
|
],
|
||||||
|
'styled_image_display' => [
|
||||||
|
'title' => 'Newly Styled Image',
|
||||||
|
'keep_button' => 'Keep',
|
||||||
|
'delete_button' => 'Delete',
|
||||||
|
],
|
||||||
'print_command_sent_successfully' => 'Print command sent successfully.',
|
'print_command_sent_successfully' => 'Print command sent successfully.',
|
||||||
'failed_to_send_print_command' => 'Failed to send print command.',
|
'failed_to_send_print_command' => 'Failed to send print command.',
|
||||||
'print_dialog.title' => 'Print Image',
|
'print_dialog' => [
|
||||||
'print_dialog.quantity_prompt' => 'How many copies would you like to print?',
|
'title' => 'Print Image',
|
||||||
'print_dialog.cancel_button' => 'Cancel',
|
'quantity_prompt' => 'How many copies would you like to print?',
|
||||||
'print_dialog.print_button' => 'Print',
|
'cancel_button' => 'Cancel',
|
||||||
|
'print_button' => 'Print',
|
||||||
|
],
|
||||||
'download_button' => 'Download',
|
'download_button' => 'Download',
|
||||||
'download_success' => 'Download started!',
|
'download_success' => 'Download started!',
|
||||||
'download_error' => 'Download failed.',
|
'download_error' => 'Download failed.',
|
||||||
|
'gallery' => [
|
||||||
|
'tap_to_open' => 'Tap to open',
|
||||||
|
'empty' => 'No images available yet.',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user