Add profile pictures and improve modal animations

- Add ProfilePicture component with fallback to user initials
- Integrate profile pictures in dashboard recent activity dialog layout
- Add profile pictures to payments list and split details
- Fix modal animation overshoot by using fixed positioning and smooth slide-in
- Add fade-in animation for modal content with proper sequencing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-08 21:50:37 +02:00
parent 815975dba0
commit 712829ad8e
6 changed files with 263 additions and 68 deletions

View File

@@ -2,6 +2,7 @@
import { onMount, createEventDispatcher } from 'svelte';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import ProfilePicture from './ProfilePicture.svelte';
export let paymentId;
@@ -150,10 +151,13 @@
{#each payment.splits as split}
<div class="split-item" class:current-user={split.username === session?.user?.nickname}>
<div class="split-user">
<span class="username">{split.username}</span>
{#if split.username === session?.user?.nickname}
<span class="you-badge">You</span>
{/if}
<ProfilePicture username={split.username} size={24} />
<div class="user-info">
<span class="username">{split.username}</span>
{#if split.username === session?.user?.nickname}
<span class="you-badge">You</span>
{/if}
</div>
</div>
<div class="split-amount" class:positive={split.amount < 0} class:negative={split.amount > 0}>
{#if split.amount > 0}
@@ -362,6 +366,12 @@
gap: 0.5rem;
}
.split-user .user-info {
display: flex;
align-items: center;
gap: 0.5rem;
}
.username {
font-weight: 500;
color: #333;

View File

@@ -0,0 +1,66 @@
<script>
export let username;
export let size = 40; // Default size in pixels
export let alt = '';
let imageError = false;
$: profileUrl = `https://bocken.org/static/user/full/${username}.webp`;
$: altText = alt || `${username}'s profile picture`;
function handleError() {
imageError = true;
}
function getInitials(name) {
if (!name) return '?';
return name.split(' ')
.map(word => word.charAt(0))
.join('')
.toUpperCase()
.substring(0, 2);
}
</script>
<div class="profile-picture" style="width: {size}px; height: {size}px;">
{#if !imageError}
<img
src={profileUrl}
alt={altText}
on:error={handleError}
loading="lazy"
/>
{:else}
<div class="fallback">
{getInitials(username)}
</div>
{/if}
</div>
<style>
.profile-picture {
border-radius: 50%;
overflow: hidden;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
position: relative;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.fallback {
color: white;
font-weight: bold;
font-size: 0.75em;
text-align: center;
line-height: 1;
}
</style>