1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-19 21:09:37 +02:00

Enhance user profile and world travel pages with improved UI and functionality

- Updated user profile page to include achievement calculations and enhanced styling for user information and statistics.
- Added icons for better visual representation of user stats and achievements.
- Improved layout for displaying adventures and collections with conditional rendering for empty states.
- Refactored world travel page to include search and filter functionality for cities, with a sidebar for progress and stats.
- Implemented completion percentage and progress bars for visited cities.
- Enhanced map integration with markers for visited and not visited cities, including toggle options for map labels.
This commit is contained in:
Sean Morley 2025-06-14 11:10:59 -04:00
parent d4c76f8718
commit 151c76dbd1
21 changed files with 2209 additions and 2461 deletions

View file

@ -2,49 +2,179 @@
import { goto } from '$app/navigation';
import { t } from 'svelte-i18n';
// Icons
import Account from '~icons/mdi/account';
import MapMarker from '~icons/mdi/map-marker';
import Share from '~icons/mdi/share-variant';
import Shield from '~icons/mdi/shield-account';
import Settings from '~icons/mdi/cog';
import Logout from '~icons/mdi/logout';
export let user: any;
let letter: string = user.first_name[0];
let letter: string = user.first_name?.[0] || user.username?.[0] || '?';
if (user && !user.first_name && user.username) {
letter = user.username[0];
}
// Get display name
$: displayName = user.first_name
? `${user.first_name} ${user.last_name || ''}`.trim()
: user.username || 'User';
// Get initials for fallback
$: initials =
user.first_name && user.last_name ? `${user.first_name[0]}${user.last_name[0]}` : letter;
// Menu items for better organization
const menuItems = [
{
path: `/profile/${user.username}`,
icon: Account,
label: 'navbar.profile',
section: 'main'
},
{
path: '/adventures',
icon: MapMarker,
label: 'navbar.my_adventures',
section: 'main'
},
{
path: '/shared',
icon: Share,
label: 'navbar.shared_with_me',
section: 'main'
},
{
path: '/settings',
icon: Settings,
label: 'navbar.settings',
section: 'secondary'
}
];
// Add admin item if user is staff
$: adminMenuItem = user.is_staff
? {
path: '/admin',
icon: Shield,
label: 'navbar.admin_panel',
section: 'secondary'
}
: null;
</script>
<div class="dropdown dropdown-bottom dropdown-end" tabindex="0" role="button">
<div class="avatar placeholder">
<div class="bg-neutral rounded-full text-neutral-200 w-10 ml-4">
<div class="dropdown dropdown-bottom dropdown-end z-[100]">
<div
tabindex="0"
role="button"
class="btn btn-ghost btn-circle avatar hover:bg-base-200 transition-colors"
>
<div class="w-10 rounded-full ring-2 ring-primary/20 hover:ring-primary/40 transition-all">
{#if user.profile_pic}
<img src={user.profile_pic} alt={$t('navbar.profile')} />
<img src={user.profile_pic} alt={$t('navbar.profile')} class="rounded-full object-cover" />
{:else}
<span class="text-2xl -mt-1">{letter}</span>
<div
class="w-10 h-10 bg-gradient-to-br from-primary to-secondary rounded-full flex items-center justify-center text-primary-content font-semibold text-sm"
>
{initials.toUpperCase()}
</div>
{/if}
</div>
</div>
<!-- svelte-ignore a11y-missing-attribute -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<ul
tabindex="0"
class="dropdown-content z-[999] text-neutral-200 menu p-2 shadow bg-neutral mt-2 rounded-box w-52"
class="dropdown-content z-[100] menu p-4 shadow-2xl bg-base-100 border border-base-300 rounded-2xl w-72 mt-2"
>
<!-- svelte-ignore a11y-missing-attribute -->
<!-- svelte-ignore a11y-missing-attribute -->
<p class="text-lg ml-4 font-bold">
{$t('navbar.greeting')}, {user.first_name
? `${user.first_name} ${user.last_name}`
: user.username}
</p>
<li>
<button on:click={() => goto(`/profile/${user.username}`)}>{$t('navbar.profile')}</button>
</li>
<li><button on:click={() => goto('/adventures')}>{$t('navbar.my_adventures')}</button></li>
<li><button on:click={() => goto('/shared')}>{$t('navbar.shared_with_me')}</button></li>
{#if user.is_staff}
<li><button on:click={() => goto('/admin')}>{$t('navbar.admin_panel')}</button></li>
{/if}
<li><button on:click={() => goto('/settings')}>{$t('navbar.settings')}</button></li>
<form method="post">
<li><button formaction="/?/logout">{$t('navbar.logout')}</button></li>
<!-- User Info Header -->
<div class="px-2 py-3 mb-3 border-b border-base-300">
<div class="flex items-center gap-3">
<div class="avatar placeholder">
<div class="w-12 rounded-full ring-2 ring-primary/20">
{#if user.profile_pic}
<img
src={user.profile_pic}
alt={$t('navbar.profile')}
class="rounded-full object-cover"
/>
{:else}
<div
class="w-12 h-12 bg-gradient-to-br from-primary to-secondary rounded-full flex items-center justify-center text-primary-content font-semibold text-lg"
style="line-height: 3rem;"
>
{initials.toUpperCase()}
</div>
{/if}
</div>
</div>
<div class="flex-1 min-w-0">
<p class="font-semibold text-base text-base-content truncate">
{$t('navbar.greeting')}, {displayName}
</p>
<p class="text-sm text-base-content/60 truncate">
@{user.username}
</p>
</div>
</div>
</div>
<!-- Main Menu Items -->
<div class="space-y-1 mb-3">
{#each menuItems.filter((item) => item.section === 'main') as item}
<li>
<button
class="btn btn-ghost justify-start gap-3 w-full text-left rounded-xl hover:bg-base-200"
on:click={() => goto(item.path)}
>
<svelte:component this={item.icon} class="w-5 h-5 text-base-content/70" />
<span>{$t(item.label)}</span>
</button>
</li>
{/each}
</div>
<div class="divider my-2"></div>
<!-- Secondary Menu Items -->
<div class="space-y-1 mb-3">
{#if adminMenuItem}
<li>
<button
class="btn btn-ghost justify-start gap-3 w-full text-left rounded-xl hover:bg-base-200"
on:click={() => goto(adminMenuItem.path)}
>
<svelte:component this={adminMenuItem.icon} class="w-5 h-5 text-warning" />
<span class="text-warning font-medium">{$t(adminMenuItem.label)}</span>
</button>
</li>
{/if}
{#each menuItems.filter((item) => item.section === 'secondary') as item}
<li>
<button
class="btn btn-ghost justify-start gap-3 w-full text-left rounded-xl hover:bg-base-200"
on:click={() => goto(item.path)}
>
<svelte:component this={item.icon} class="w-5 h-5 text-base-content/70" />
<span>{$t(item.label)}</span>
</button>
</li>
{/each}
</div>
<div class="divider my-2"></div>
<!-- Logout -->
<form method="post" class="w-full">
<li class="w-full">
<button
formaction="/?/logout"
class="btn btn-ghost justify-start gap-3 w-full text-left rounded-xl hover:bg-error/10 hover:text-error transition-colors"
>
<Logout class="w-5 h-5" />
<span>{$t('navbar.logout')}</span>
</button>
</li>
</form>
</ul>
</div>