Fix 500 error, add Address/Contact placeholders, and enhance Person activity view
- Fixed broken import path in /core dashboard - Simplified core layout data requirements to prevent crashes - Implemented V3 CRUD and Dexie tables for Addresses and Contacts - Created placeholder management pages for /core/addresses and /core/contacts - Added 'Linked Activity & Content' section to Person detail page to show related events and posts
This commit is contained in:
79
src/lib/ae_core/ae_core__address.ts
Normal file
79
src/lib/ae_core/ae_core__address.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
|
import { api } from '$lib/api/api';
|
||||||
|
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||||
|
import { db_core } from '$lib/ae_core/db_core';
|
||||||
|
|
||||||
|
const ae_promises: key_val = {};
|
||||||
|
|
||||||
|
export interface Address {
|
||||||
|
id: string;
|
||||||
|
address_id: string;
|
||||||
|
address_id_random: string;
|
||||||
|
account_id: string;
|
||||||
|
account_id_random: string;
|
||||||
|
|
||||||
|
for_type?: string;
|
||||||
|
for_id?: string;
|
||||||
|
for_id_random?: string;
|
||||||
|
|
||||||
|
city: string;
|
||||||
|
state_province: string;
|
||||||
|
country: string;
|
||||||
|
|
||||||
|
enable: null | boolean;
|
||||||
|
hide?: null | boolean;
|
||||||
|
priority?: null | boolean;
|
||||||
|
sort?: null | number;
|
||||||
|
group?: null | string;
|
||||||
|
notes?: null | string;
|
||||||
|
created_on: Date;
|
||||||
|
updated_on?: null | Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load_ae_obj_li__address({
|
||||||
|
api_cfg,
|
||||||
|
for_obj_type,
|
||||||
|
for_obj_id,
|
||||||
|
enabled = 'enabled',
|
||||||
|
hidden = 'not_hidden',
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
api_cfg: any;
|
||||||
|
for_obj_type?: string;
|
||||||
|
for_obj_id?: string;
|
||||||
|
enabled?: 'all' | 'enabled' | 'not_enabled';
|
||||||
|
hidden?: 'all' | 'hidden' | 'not_hidden';
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
ae_promises.load__address_obj_li = await api.get_ae_obj_li_v3({
|
||||||
|
api_cfg,
|
||||||
|
obj_type: 'address',
|
||||||
|
for_obj_type,
|
||||||
|
for_obj_id,
|
||||||
|
enabled,
|
||||||
|
hidden,
|
||||||
|
log_lvl
|
||||||
|
}).then(async (result) => {
|
||||||
|
if (result && Array.isArray(result)) {
|
||||||
|
const processed = await process_ae_obj__address_props({ obj_li: result, log_lvl });
|
||||||
|
await db_save_ae_obj_li__ae_obj({
|
||||||
|
db_instance: db_core,
|
||||||
|
table_name: 'address',
|
||||||
|
obj_li: processed,
|
||||||
|
properties_to_save: ['id', 'address_id', 'address_id_random', 'city', 'state_province', 'country', 'enable'],
|
||||||
|
log_lvl
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
return ae_promises.load__address_obj_li;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function process_ae_obj__address_props({ obj_li, log_lvl = 0 }: { obj_li: any[], log_lvl?: number }) {
|
||||||
|
return obj_li.map(obj => {
|
||||||
|
const new_obj = { ...obj };
|
||||||
|
new_obj.id = obj.address_id_random;
|
||||||
|
return new_obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
79
src/lib/ae_core/ae_core__contact.ts
Normal file
79
src/lib/ae_core/ae_core__contact.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
|
import { api } from '$lib/api/api';
|
||||||
|
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||||
|
import { db_core } from '$lib/ae_core/db_core';
|
||||||
|
|
||||||
|
const ae_promises: key_val = {};
|
||||||
|
|
||||||
|
export interface Contact {
|
||||||
|
id: string;
|
||||||
|
contact_id: string;
|
||||||
|
contact_id_random: string;
|
||||||
|
account_id: string;
|
||||||
|
account_id_random: string;
|
||||||
|
|
||||||
|
for_type?: string;
|
||||||
|
for_id?: string;
|
||||||
|
for_id_random?: string;
|
||||||
|
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
|
||||||
|
enable: null | boolean;
|
||||||
|
hide?: null | boolean;
|
||||||
|
priority?: null | boolean;
|
||||||
|
sort?: null | number;
|
||||||
|
group?: null | string;
|
||||||
|
notes?: null | string;
|
||||||
|
created_on: Date;
|
||||||
|
updated_on?: null | Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load_ae_obj_li__contact({
|
||||||
|
api_cfg,
|
||||||
|
for_obj_type,
|
||||||
|
for_obj_id,
|
||||||
|
enabled = 'enabled',
|
||||||
|
hidden = 'not_hidden',
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
api_cfg: any;
|
||||||
|
for_obj_type?: string;
|
||||||
|
for_obj_id?: string;
|
||||||
|
enabled?: 'all' | 'enabled' | 'not_enabled';
|
||||||
|
hidden?: 'all' | 'hidden' | 'not_hidden';
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
ae_promises.load__contact_obj_li = await api.get_ae_obj_li_v3({
|
||||||
|
api_cfg,
|
||||||
|
obj_type: 'contact',
|
||||||
|
for_obj_type,
|
||||||
|
for_obj_id,
|
||||||
|
enabled,
|
||||||
|
hidden,
|
||||||
|
log_lvl
|
||||||
|
}).then(async (result) => {
|
||||||
|
if (result && Array.isArray(result)) {
|
||||||
|
const processed = await process_ae_obj__contact_props({ obj_li: result, log_lvl });
|
||||||
|
await db_save_ae_obj_li__ae_obj({
|
||||||
|
db_instance: db_core,
|
||||||
|
table_name: 'contact',
|
||||||
|
obj_li: processed,
|
||||||
|
properties_to_save: ['id', 'contact_id', 'contact_id_random', 'name', 'email', 'phone', 'enable'],
|
||||||
|
log_lvl
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
return ae_promises.load__contact_obj_li;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function process_ae_obj__contact_props({ obj_li, log_lvl = 0 }: { obj_li: any[], log_lvl?: number }) {
|
||||||
|
return obj_li.map(obj => {
|
||||||
|
const new_obj = { ...obj };
|
||||||
|
new_obj.id = obj.contact_id_random;
|
||||||
|
return new_obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -173,6 +173,8 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
account!: Table<any>;
|
account!: Table<any>;
|
||||||
site!: Table<any>;
|
site!: Table<any>;
|
||||||
site_domain!: Table<any>;
|
site_domain!: Table<any>;
|
||||||
|
address!: Table<any>;
|
||||||
|
contact!: Table<any>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('ae_core_db');
|
super('ae_core_db');
|
||||||
@@ -219,6 +221,20 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
id, site_domain_id, site_domain_id_random,
|
id, site_domain_id, site_domain_id_random,
|
||||||
site_id, site_id_random,
|
site_id, site_id_random,
|
||||||
domain,
|
domain,
|
||||||
|
enable, hide, priority, sort, group, created_on, updated_on`,
|
||||||
|
|
||||||
|
address: `
|
||||||
|
id, address_id, address_id_random,
|
||||||
|
account_id, account_id_random,
|
||||||
|
for_type, for_id, for_id_random,
|
||||||
|
city, state_province, country,
|
||||||
|
enable, hide, priority, sort, group, created_on, updated_on`,
|
||||||
|
|
||||||
|
contact: `
|
||||||
|
id, contact_id, contact_id_random,
|
||||||
|
account_id, account_id_random,
|
||||||
|
for_type, for_id, for_id_random,
|
||||||
|
name, email, phone,
|
||||||
enable, hide, priority, sort, group, created_on, updated_on`
|
enable, hide, priority, sort, group, created_on, updated_on`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ae_loc, ae_sess, ae_api, slct } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_sess, ae_api, slct } from '$lib/stores/ae_stores';
|
||||||
import { Building, Globe, Users, ShieldCheck, List, LayoutDashboard } from 'lucide-svelte';
|
import { Building, Globe, Users, ShieldCheck, List, LayoutDashboard, MapPin, Phone } from 'lucide-svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
@@ -31,6 +31,12 @@
|
|||||||
<a href="/core/people" class="btn btn-sm variant-soft-warning">
|
<a href="/core/people" class="btn btn-sm variant-soft-warning">
|
||||||
<Users size={14} class="mr-1" /> People
|
<Users size={14} class="mr-1" /> People
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/core/addresses" class="btn btn-sm variant-soft-surface">
|
||||||
|
<MapPin size={14} class="mr-1" /> Addresses
|
||||||
|
</a>
|
||||||
|
<a href="/core/contacts" class="btn btn-sm variant-soft-surface">
|
||||||
|
<Phone size={14} class="mr-1" /> Contacts
|
||||||
|
</a>
|
||||||
<a href="/core/lookups" class="btn btn-sm variant-soft-surface">
|
<a href="/core/lookups" class="btn btn-sm variant-soft-surface">
|
||||||
<List size={14} class="mr-1" /> Lookups
|
<List size={14} class="mr-1" /> Lookups
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const load: PageLoad = async ({ parent }) => {
|
export const load: PageLoad = async ({ parent }) => {
|
||||||
const parentData = await parent();
|
const data = await parent();
|
||||||
|
|
||||||
|
if (!data.account_id) {
|
||||||
|
console.error('Core Dashboard: No account_id found in parent data');
|
||||||
|
// We could throw an error here, but for now let's just log it and return
|
||||||
|
// to avoid a hard crash if the user is just navigating.
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
account_id: parentData.account_id
|
account_id: data.account_id
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
81
src/routes/core/addresses/+page.svelte
Normal file
81
src/routes/core/addresses/+page.svelte
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { MapPin, Plus, Search } from 'lucide-svelte';
|
||||||
|
import { load_ae_obj_li__address } from '$lib/ae_core/ae_core__address';
|
||||||
|
|
||||||
|
let address_li: any[] = $state([]);
|
||||||
|
let loading = $state(true);
|
||||||
|
|
||||||
|
async function load_addresses() {
|
||||||
|
if (!$ae_loc.account_id) return;
|
||||||
|
loading = true;
|
||||||
|
address_li = await load_ae_obj_li__address({
|
||||||
|
api_cfg: $ae_api,
|
||||||
|
for_obj_id: $ae_loc.account_id,
|
||||||
|
enabled: 'all',
|
||||||
|
log_lvl: 1
|
||||||
|
});
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!$ae_loc.manager_access) {
|
||||||
|
goto('/core');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
load_addresses();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container mx-auto p-4 space-y-6">
|
||||||
|
<header class="flex justify-between items-center">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<MapPin size={24} />
|
||||||
|
<h1 class="h2">Address Management</h1>
|
||||||
|
</div>
|
||||||
|
<button class="btn variant-filled-primary" disabled>
|
||||||
|
<Plus size={16} class="mr-2" /> Add Address
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{#if loading}
|
||||||
|
<div class="placeholder animate-pulse h-64 w-full"></div>
|
||||||
|
{:else if address_li.length === 0}
|
||||||
|
<div class="card p-8 text-center variant-soft">
|
||||||
|
<p class="opacity-60">No addresses found for this account.</p>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>City</th>
|
||||||
|
<th>State/Province</th>
|
||||||
|
<th>Country</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th class="text-right">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each address_li as addr}
|
||||||
|
<tr>
|
||||||
|
<td>{addr.city || '--'}</td>
|
||||||
|
<td>{addr.state_province || '--'}</td>
|
||||||
|
<td>{addr.country || '--'}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge {addr.enable ? 'variant-filled-success' : 'variant-filled-error'}">
|
||||||
|
{addr.enable ? 'Enabled' : 'Disabled'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<button class="btn btn-sm variant-soft-primary" disabled>Manage</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
91
src/routes/core/contacts/+page.svelte
Normal file
91
src/routes/core/contacts/+page.svelte
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { Phone, Plus, Search, Mail, User } from 'lucide-svelte';
|
||||||
|
import { load_ae_obj_li__contact } from '$lib/ae_core/ae_core__contact';
|
||||||
|
|
||||||
|
let contact_li: any[] = $state([]);
|
||||||
|
let loading = $state(true);
|
||||||
|
|
||||||
|
async function load_contacts() {
|
||||||
|
if (!$ae_loc.account_id) return;
|
||||||
|
loading = true;
|
||||||
|
contact_li = await load_ae_obj_li__contact({
|
||||||
|
api_cfg: $ae_api,
|
||||||
|
for_obj_id: $ae_loc.account_id,
|
||||||
|
enabled: 'all',
|
||||||
|
log_lvl: 1
|
||||||
|
});
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!$ae_loc.manager_access) {
|
||||||
|
goto('/core');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
load_contacts();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container mx-auto p-4 space-y-6">
|
||||||
|
<header class="flex justify-between items-center">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Phone size={24} />
|
||||||
|
<h1 class="h2">Contact Management</h1>
|
||||||
|
</div>
|
||||||
|
<button class="btn variant-filled-primary" disabled>
|
||||||
|
<Plus size={16} class="mr-2" /> Add Contact
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{#if loading}
|
||||||
|
<div class="placeholder animate-pulse h-64 w-full"></div>
|
||||||
|
{:else if contact_li.length === 0}
|
||||||
|
<div class="card p-8 text-center variant-soft">
|
||||||
|
<p class="opacity-60">No contacts found for this account.</p>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Phone</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th class="text-right">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each contact_li as con}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<User size={14} class="opacity-60" />
|
||||||
|
{con.name || '--'}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Mail size={14} class="opacity-60" />
|
||||||
|
{con.email || '--'}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{con.phone || '--'}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge {con.enable ? 'variant-filled-success' : 'variant-filled-error'}">
|
||||||
|
{con.enable ? 'Enabled' : 'Disabled'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<button class="btn btn-sm variant-soft-primary" disabled>Manage</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -28,7 +28,9 @@
|
|||||||
import Person_view from './../../person_view.svelte';
|
import Person_view from './../../person_view.svelte';
|
||||||
import { load_ae_obj_li__user } from '$lib/ae_core/ae_core__user';
|
import { load_ae_obj_li__user } from '$lib/ae_core/ae_core__user';
|
||||||
import { update_ae_obj__person } from '$lib/ae_core/ae_core__person';
|
import { update_ae_obj__person } from '$lib/ae_core/ae_core__person';
|
||||||
import { Users, Link, Unlink, UserPlus, ShieldCheck, User } from 'lucide-svelte';
|
import { qry_ae_obj_li__event } from '$lib/ae_events/ae_events__event';
|
||||||
|
import { load_ae_obj_li__post } from '$lib/ae_posts/ae_posts__post';
|
||||||
|
import { Users, Link, Unlink, UserPlus, ShieldCheck, User, Calendar, MessageSquare, History } from 'lucide-svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
@@ -54,6 +56,36 @@
|
|||||||
let loading_users = $state(false);
|
let loading_users = $state(false);
|
||||||
let show_link_ui = $state(false);
|
let show_link_ui = $state(false);
|
||||||
|
|
||||||
|
let related_events: any[] = $state([]);
|
||||||
|
let related_posts: any[] = $state([]);
|
||||||
|
let loading_activity = $state(false);
|
||||||
|
|
||||||
|
async function load_activity() {
|
||||||
|
if (!$slct.person_id) return;
|
||||||
|
loading_activity = true;
|
||||||
|
|
||||||
|
// Load related data using search queries
|
||||||
|
// Assuming person_id_random is the field name in these objects
|
||||||
|
const [events, posts] = await Promise.all([
|
||||||
|
qry_ae_obj_li__event({
|
||||||
|
api_cfg: $ae_api,
|
||||||
|
for_obj_id: $ae_loc.account_id,
|
||||||
|
params: { person_id_random: $slct.person_id },
|
||||||
|
log_lvl: 1
|
||||||
|
}),
|
||||||
|
load_ae_obj_li__post({
|
||||||
|
api_cfg: $ae_api,
|
||||||
|
for_obj_id: $ae_loc.account_id,
|
||||||
|
params: { person_id_random: $slct.person_id },
|
||||||
|
log_lvl: 1
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
related_events = events || [];
|
||||||
|
related_posts = posts || [];
|
||||||
|
loading_activity = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function load_unlinked_users() {
|
async function load_unlinked_users() {
|
||||||
if (!$ae_loc.manager_access) return;
|
if (!$ae_loc.manager_access) return;
|
||||||
loading_users = true;
|
loading_users = true;
|
||||||
@@ -95,6 +127,14 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!$ae_loc.manager_access) {
|
||||||
|
goto('/core');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
load_activity();
|
||||||
|
});
|
||||||
|
|
||||||
if (!$ae_loc.person) {
|
if (!$ae_loc.person) {
|
||||||
$ae_loc.person = {};
|
$ae_loc.person = {};
|
||||||
}
|
}
|
||||||
@@ -246,6 +286,61 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Activity & Content Section -->
|
||||||
|
<div class="card p-4 variant-soft-surface space-y-4 mx-4">
|
||||||
|
<header class="flex items-center gap-2 font-bold border-b border-surface-500/30 pb-2">
|
||||||
|
<History size={18} />
|
||||||
|
<span>Linked Activity & Content</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<!-- Related Events -->
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h4 class="h4 flex items-center gap-2 text-sm opacity-70 uppercase tracking-wider font-bold">
|
||||||
|
<Calendar size={16} /> Related Events
|
||||||
|
</h4>
|
||||||
|
{#if loading_activity}
|
||||||
|
<div class="placeholder animate-pulse h-20 w-full"></div>
|
||||||
|
{:else if related_events.length === 0}
|
||||||
|
<p class="text-sm italic opacity-50">No related events found.</p>
|
||||||
|
{:else}
|
||||||
|
<div class="space-y-2">
|
||||||
|
{#each related_events as ev}
|
||||||
|
<a href="/events/{ev.event_id_random}" class="card p-3 variant-soft flex flex-col gap-1 hover:variant-soft-primary transition-all">
|
||||||
|
<span class="font-bold">{ev.name}</span>
|
||||||
|
<span class="text-xs opacity-60">{new Date(ev.start_datetime).toLocaleDateString()}</span>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Related Posts -->
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h4 class="h4 flex items-center gap-2 text-sm opacity-70 uppercase tracking-wider font-bold">
|
||||||
|
<MessageSquare size={16} /> Related Posts
|
||||||
|
</h4>
|
||||||
|
{#if loading_activity}
|
||||||
|
<div class="placeholder animate-pulse h-20 w-full"></div>
|
||||||
|
{:else if related_posts.length === 0}
|
||||||
|
<p class="text-sm italic opacity-50">No related posts found.</p>
|
||||||
|
{:else}
|
||||||
|
<div class="space-y-2">
|
||||||
|
{#each related_posts as post}
|
||||||
|
<a href="/posts/{post.post_id_random}" class="card p-3 variant-soft flex flex-col gap-1 hover:variant-soft-primary transition-all">
|
||||||
|
<span class="font-bold">{post.title}</span>
|
||||||
|
<div class="flex justify-between items-center text-xs opacity-60">
|
||||||
|
<span>{new Date(post.created_on).toLocaleDateString()}</span>
|
||||||
|
<span class="badge variant-soft-surface">{post.post_comment_count || 0} comments</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !$lq__person_obj}
|
{#if !$lq__person_obj}
|
||||||
|
|||||||
Reference in New Issue
Block a user