Adds locations table for additional location meta data; updates seeder to add locations; separates modal out into parts
This commit is contained in:
parent
c58a498e44
commit
9f3ceabd7d
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('locations', function (Blueprint $table) {
|
||||||
|
$table->id(); // BIGINT PK
|
||||||
|
$table->string('display_name'); // “Home”, “Reading Terminal Market”
|
||||||
|
$table->text('raw_address')->nullable(); // what the user originally typed
|
||||||
|
$table->string('street')->nullable();
|
||||||
|
$table->string('city')->nullable();
|
||||||
|
$table->string('state', 64)->nullable();
|
||||||
|
$table->string('postal', 32)->nullable();
|
||||||
|
$table->string('country', 64)->nullable();
|
||||||
|
$table->decimal('lat', 9, 6)->nullable(); // ±90.000000
|
||||||
|
$table->decimal('lon', 9, 6)->nullable(); // ±180.000000
|
||||||
|
$table->string('phone', 32)->nullable();
|
||||||
|
$table->json('hours_json')->nullable(); // flexible opening-hours blob
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// optional composite for fast “near me” queries
|
||||||
|
$table->index(['lat', 'lon'], 'idx_locations_lat_lon');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('locations');
|
||||||
|
}
|
||||||
|
};
|
@ -9,23 +9,37 @@ return new class extends Migration
|
|||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('event_meta', function (Blueprint $table) {
|
Schema::create('event_meta', function (Blueprint $table) {
|
||||||
|
|
||||||
|
// FK = PK → calendarobjects.id
|
||||||
$table->unsignedInteger('event_id')->primary();
|
$table->unsignedInteger('event_id')->primary();
|
||||||
|
|
||||||
/* cached fields from ical blob for quick ui */
|
// cached fields from the VEVENT
|
||||||
$table->string('title')->nullable();
|
$table->string('title')->nullable();
|
||||||
$table->text('description')->nullable();
|
$table->text('description')->nullable();
|
||||||
|
|
||||||
|
// free-text fallback if no geocoded location
|
||||||
$table->string('location')->nullable();
|
$table->string('location')->nullable();
|
||||||
|
|
||||||
|
// foreign-key to the richer locations table
|
||||||
|
$table->unsignedBigInteger('location_id')->nullable();
|
||||||
|
$table->foreign('location_id')
|
||||||
|
->references('id')
|
||||||
|
->on('locations')
|
||||||
|
->nullOnDelete();
|
||||||
|
|
||||||
|
// all-day flag
|
||||||
$table->boolean('all_day')->default(false);
|
$table->boolean('all_day')->default(false);
|
||||||
|
|
||||||
/* extra metadata */
|
// extra metadata
|
||||||
$table->string('category')->nullable();
|
$table->string('category')->nullable();
|
||||||
$table->string('reminder_minutes')->nullable();
|
$table->string('reminder_minutes')->nullable();
|
||||||
$table->boolean('is_private')->default(false);
|
$table->boolean('is_private')->default(false);
|
||||||
$table->dateTime('start_at')->nullable();
|
$table->dateTime('start_at')->nullable();
|
||||||
$table->dateTime('end_at')->nullable();
|
$table->dateTime('end_at')->nullable();
|
||||||
$table->json('extra')->nullable();
|
$table->json('extra')->nullable();
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
|
// FK to Sabre calendarobjects table
|
||||||
$table->foreign('event_id')
|
$table->foreign('event_id')
|
||||||
->references('id')
|
->references('id')
|
||||||
->on('calendarobjects')
|
->on('calendarobjects')
|
@ -71,10 +71,67 @@ class DatabaseSeeder extends Seeder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* create helper function for events to be added
|
* create the locations connected to each event
|
||||||
**/
|
**/
|
||||||
|
|
||||||
$insertEvent = function (Carbon $start, string $summary) use ($calId) {
|
// locations
|
||||||
|
$locationSeeds = [
|
||||||
|
'Home' => [ // free-text, no geo
|
||||||
|
'display' => 'Home',
|
||||||
|
'raw' => null,
|
||||||
|
],
|
||||||
|
'Living Room' => [
|
||||||
|
'display' => 'Living Room',
|
||||||
|
'raw' => null,
|
||||||
|
],
|
||||||
|
'McCahill Park' => [
|
||||||
|
'display' => 'McCahill Park',
|
||||||
|
'raw' => '625 Hemlock Hollow Rd, Pittsburgh, PA 15238',
|
||||||
|
],
|
||||||
|
'Meadow Park' => [
|
||||||
|
'display' => 'Meadow Park',
|
||||||
|
'raw' => '2 Meadow Park Lane, Pittsburgh, PA 15215',
|
||||||
|
],
|
||||||
|
'AHN Pediatrics' => [
|
||||||
|
'display' => 'AHN Pediatrics',
|
||||||
|
'raw' => '3394 Saxonburg Blvd Suite 600, Glenshaw, PA 15116',
|
||||||
|
],
|
||||||
|
'The Discovery School' => [
|
||||||
|
'display' => 'The Discovery School',
|
||||||
|
'raw' => '4225 Middle Rd, Allison Park, PA 15101',
|
||||||
|
],
|
||||||
|
'Fairview Elementary School' => [
|
||||||
|
'display' => 'Fairview Elementary School',
|
||||||
|
'raw' => '738 Dorseyville Rd, Pittsburgh, PA 15238',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// create the locations first
|
||||||
|
$locationIdMap = [];
|
||||||
|
foreach ($locationSeeds as $key => $data) {
|
||||||
|
$locId = DB::table('locations')->updateOrInsert(
|
||||||
|
['display_name' => $data['display']], // uniqueness key
|
||||||
|
[
|
||||||
|
'raw_address' => $data['raw'],
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// updateOrInsert returns boolean; fetch id explicitly
|
||||||
|
$locId = DB::table('locations')->where('display_name', $data['display'])->value('id');
|
||||||
|
$locationIdMap[$key] = $locId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* cevent creation helper function
|
||||||
|
**/
|
||||||
|
|
||||||
|
$insertEvent = function (Carbon $start,
|
||||||
|
string $summary,
|
||||||
|
string $locationKey) use ($calId, $locationIdMap)
|
||||||
|
{
|
||||||
|
|
||||||
// set base vars
|
// set base vars
|
||||||
$uid = Str::uuid().'@kithkin.lan';
|
$uid = Str::uuid().'@kithkin.lan';
|
||||||
@ -85,6 +142,10 @@ class DatabaseSeeder extends Seeder
|
|||||||
$dtstart = $start->copy()->utc()->format('Ymd\\THis\\Z');
|
$dtstart = $start->copy()->utc()->format('Ymd\\THis\\Z');
|
||||||
$dtend = $end->copy()->utc()->format('Ymd\\THis\\Z');
|
$dtend = $end->copy()->utc()->format('Ymd\\THis\\Z');
|
||||||
|
|
||||||
|
$locationDisplay = $locationKey;
|
||||||
|
$locationRaw = $locationSeeds[$locationKey]['raw'] ?? null;
|
||||||
|
$icalLocation = $locationRaw ?? $locationDisplay;
|
||||||
|
|
||||||
$ical = <<<ICS
|
$ical = <<<ICS
|
||||||
BEGIN:VCALENDAR
|
BEGIN:VCALENDAR
|
||||||
VERSION:2.0
|
VERSION:2.0
|
||||||
@ -117,7 +178,8 @@ ICS;
|
|||||||
[
|
[
|
||||||
'title' => $summary,
|
'title' => $summary,
|
||||||
'description' => 'Automatically seeded event',
|
'description' => 'Automatically seeded event',
|
||||||
'location' => 'Home Office',
|
'location' => $locationRaw ? null : $locationDisplay,
|
||||||
|
'location_id' => $locationIdMap[$locationKey] ?? null,
|
||||||
'all_day' => false,
|
'all_day' => false,
|
||||||
'category' => 'Demo',
|
'category' => 'Demo',
|
||||||
'start_at' => $start->copy()->utc(),
|
'start_at' => $start->copy()->utc(),
|
||||||
@ -136,23 +198,23 @@ ICS;
|
|||||||
$now = Carbon::now()->setSeconds(0);
|
$now = Carbon::now()->setSeconds(0);
|
||||||
|
|
||||||
// 3 events today
|
// 3 events today
|
||||||
$insertEvent($now->copy(), 'Playground with James');
|
$insertEvent($now->copy(), 'Playground with James', 'McCaHill Park');
|
||||||
$insertEvent($now->copy()->addHours(2), 'Lunch with Daniel');
|
$insertEvent($now->copy()->addHours(2), 'Lunch with Daniel', 'Home');
|
||||||
$insertEvent($now->copy()->addHours(4), 'Baseball practice');
|
$insertEvent($now->copy()->addHours(4), 'Baseball practice', 'Meadow Park');
|
||||||
|
|
||||||
// 1 event 3 days ago
|
// 1 event 3 days ago
|
||||||
$past = $now->copy()->subDays(3)->setTime(10, 0);
|
$past = $now->copy()->subDays(3)->setTime(10, 0);
|
||||||
$insertEvent($past, 'Kids doctors appointments');
|
$insertEvent($past, 'Kids doctors appointments', 'AHN Pediatrics');
|
||||||
|
|
||||||
// 1 event 2 days ahead
|
// 1 event 2 days ahead
|
||||||
$future2 = $now->copy()->addDays(2)->setTime(14, 0);
|
$future2 = $now->copy()->addDays(2)->setTime(14, 0);
|
||||||
$insertEvent($future2, 'Teacher conference (Nuthatches)');
|
$insertEvent($future2, 'Teacher conference (Nuthatches)', 'The Discovery School');
|
||||||
|
|
||||||
// 2 events 5 days ahead
|
// 2 events 5 days ahead
|
||||||
$future5a = $now->copy()->addDays(5)->setTime(9, 0);
|
$future5a = $now->copy()->addDays(5)->setTime(9, 0);
|
||||||
$future5b = $future5a->copy()->addHours(2);
|
$future5b = $future5a->copy()->addHours(2);
|
||||||
$insertEvent($future5a, 'Teacher conference (3rd grade)');
|
$insertEvent($future5a, 'Teacher conference (3rd grade)', 'Fairview Elementary');
|
||||||
$insertEvent($future5b, 'Family game night');
|
$insertEvent($future5b, 'Family game night', 'Living Room');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
BIN
resources/font/bogart-bold.ttf
Normal file
BIN
resources/font/bogart-bold.ttf
Normal file
Binary file not shown.
3
resources/views/components/modal/body.blade.php
Normal file
3
resources/views/components/modal/body.blade.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<section class="flex flex-col px-8 pb-6">
|
||||||
|
{{ $slot }}
|
||||||
|
</section>
|
9
resources/views/components/modal/title.blade.php
Normal file
9
resources/views/components/modal/title.blade.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@props([
|
||||||
|
'border' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
<header @class(['px-8 py-6', 'header--with-border' => $border])>
|
||||||
|
<h2 class="text-lg font-semibold">
|
||||||
|
{{ $slot }}
|
||||||
|
</h2>
|
||||||
|
</header>
|
@ -1,10 +1,9 @@
|
|||||||
<x-modal.content>
|
<x-modal.content>
|
||||||
<div class="p-6 space-y-4">
|
<x-modal.title>
|
||||||
<h2 class="text-lg font-semibold">
|
{{ $event->meta->title ?? '(no title)' }}
|
||||||
{{ $event->meta->title ?? '(no title)' }}
|
</x-modal.title>
|
||||||
</h2>
|
<x-modal.body>
|
||||||
|
<p class="text-gray-700">
|
||||||
<p class="text-sm text-gray-600">
|
|
||||||
{{ $start->format('l, F j, Y · g:i A') }}
|
{{ $start->format('l, F j, Y · g:i A') }}
|
||||||
@unless ($start->equalTo($end))
|
@unless ($start->equalTo($end))
|
||||||
–
|
–
|
||||||
@ -15,13 +14,13 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
@if ($event->meta->location)
|
@if ($event->meta->location)
|
||||||
<p class="text-sm"><strong>Where:</strong> {{ $event->meta->location }}</p>
|
<p><strong>Where:</strong> {{ $event->meta->location }}</p>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($event->meta->description)
|
@if ($event->meta->description)
|
||||||
<div class="prose max-w-none text-sm">
|
<div>
|
||||||
{!! nl2br(e($event->meta->description)) !!}
|
{!! nl2br(e($event->meta->description)) !!}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</x-modal.body>
|
||||||
</x-modal.content>
|
</x-modal.content>
|
||||||
|
Loading…
Reference in New Issue
Block a user