feat: Kalender Hover-Highlight (Web) + manuelle Event-Erstellung (App)
Web: Hover über Zeitslots zeigt leichten Indigo-Highlight an App: + Button im Kalender-Header öffnet neuen Termin Modal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
fa52148021
commit
3aa9eb1633
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Translation;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$keys = [
|
||||||
|
'events.edit' => ['de' => 'Termin bearbeiten', 'en' => 'Edit event'],
|
||||||
|
'events.new' => ['de' => 'Neuer Termin', 'en' => 'New event'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($keys as $key => $locales) {
|
||||||
|
foreach ($locales as $locale => $value) {
|
||||||
|
Translation::updateOrCreate(
|
||||||
|
['key' => $key, 'locale' => $locale],
|
||||||
|
['value' => $value]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void {}
|
||||||
|
};
|
||||||
|
|
@ -244,6 +244,8 @@ class TranslationSeeder extends Seeder
|
||||||
'calendar.no_events' => ['de' => 'Keine Termine', 'en' => 'No events'],
|
'calendar.no_events' => ['de' => 'Keine Termine', 'en' => 'No events'],
|
||||||
|
|
||||||
// ── Event Form ──────────────────────────
|
// ── Event Form ──────────────────────────
|
||||||
|
'events.edit' => ['de' => 'Termin bearbeiten', 'en' => 'Edit event'],
|
||||||
|
'events.new' => ['de' => 'Neuer Termin', 'en' => 'New event'],
|
||||||
'events.title' => ['de' => 'Titel', 'en' => 'Title'],
|
'events.title' => ['de' => 'Titel', 'en' => 'Title'],
|
||||||
'events.title_placeholder' => ['de' => 'Titel hinzufügen', 'en' => 'Add title'],
|
'events.title_placeholder' => ['de' => 'Titel hinzufügen', 'en' => 'Add title'],
|
||||||
'events.datetime' => ['de' => 'Datum & Zeit', 'en' => 'Date & Time'],
|
'events.datetime' => ['de' => 'Datum & Zeit', 'en' => 'Date & Time'],
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -18,7 +18,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"resources/css/app.css": {
|
"resources/css/app.css": {
|
||||||
"file": "assets/app-BPc0iRlq.css",
|
"file": "assets/app-YhqCCiLi.css",
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"names": [
|
"names": [
|
||||||
"app.css"
|
"app.css"
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ document.addEventListener('alpine:init', () => {
|
||||||
// ── Focus (Einzelklick → Vordergrund) ────────────────────────
|
// ── Focus (Einzelklick → Vordergrund) ────────────────────────
|
||||||
focusedEventId: null,
|
focusedEventId: null,
|
||||||
|
|
||||||
|
// ── Hover-Highlight ──────────────────────────────────────────
|
||||||
|
hoverCol: null,
|
||||||
|
hoverSlot: null,
|
||||||
|
|
||||||
// ── Timed-Event DnD (Week / Day) ─────────────────────────────
|
// ── Timed-Event DnD (Week / Day) ─────────────────────────────
|
||||||
draggingId: null,
|
draggingId: null,
|
||||||
dragOffsetY: 0,
|
dragOffsetY: 0,
|
||||||
|
|
@ -53,6 +57,25 @@ document.addEventListener('alpine:init', () => {
|
||||||
return String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0');
|
return String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
// HOVER HIGHLIGHT (Week + Day timeline)
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
onTimeHover(e, col) {
|
||||||
|
if (this.draggingId) return;
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
const relY = Math.max(0, e.clientY - rect.top);
|
||||||
|
this.hoverSlot = Math.floor(relY / SLOT_PX);
|
||||||
|
this.hoverCol = col;
|
||||||
|
},
|
||||||
|
|
||||||
|
onTimeLeave(e, col) {
|
||||||
|
if (this.hoverCol === col) {
|
||||||
|
this.hoverCol = null;
|
||||||
|
this.hoverSlot = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
// CREATE ON DOUBLE-CLICK (Week + Day timeline)
|
// CREATE ON DOUBLE-CLICK (Week + Day timeline)
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,15 @@
|
||||||
x-on:dragleave="onDragLeave($event)"
|
x-on:dragleave="onDragLeave($event)"
|
||||||
x-on:drop="onDrop($event, '{{ $dayStr }}')"
|
x-on:drop="onDrop($event, '{{ $dayStr }}')"
|
||||||
x-on:dblclick.self="onCreateAt($event, '{{ $dayStr }}')"
|
x-on:dblclick.self="onCreateAt($event, '{{ $dayStr }}')"
|
||||||
x-on:click.self="focusedEventId = null">
|
x-on:click.self="focusedEventId = null"
|
||||||
|
x-on:mousemove="onTimeHover($event, {{ $colIndex }})"
|
||||||
|
x-on:mouseleave="onTimeLeave($event, {{ $colIndex }})">
|
||||||
|
|
||||||
|
{{-- Hover-Highlight --}}
|
||||||
|
<div class="absolute left-0 right-0 pointer-events-none z-[1]"
|
||||||
|
x-show="hoverCol === {{ $colIndex }} && hoverSlot !== null"
|
||||||
|
x-bind:style="hoverSlot !== null ? 'top:' + (hoverSlot * {{ $cellPx / 4 }}) + 'px' : ''"
|
||||||
|
style="height: {{ $cellPx / 4 }}px; background: rgba(99,102,241,0.06);"></div>
|
||||||
|
|
||||||
{{-- Stunden-Linien --}}
|
{{-- Stunden-Linien --}}
|
||||||
@for($h = $hourStart; $h < $hourEnd; $h++)
|
@for($h = $hourStart; $h < $hourEnd; $h++)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue