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:
parent
d4c76f8718
commit
151c76dbd1
21 changed files with 2209 additions and 2461 deletions
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue