This commit is contained in:
mohamad 2025-01-30 08:30:39 +01:00
parent f903569a60
commit e7eaf2a592
16 changed files with 3166 additions and 206 deletions

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,11 @@
"@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@types/canvas-confetti": "^1.9.0",
"@types/leaflet": "^1.9.16",
"@types/phoenix": "^1.6.6",
"@types/pikaday": "^1.7.9",
"@types/three": "^0.172.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"daisyui": "^4.12.23", "daisyui": "^4.12.23",
"eslint": "^9.18.0", "eslint": "^9.18.0",
@ -48,7 +53,21 @@
"@capacitor/cli": "^7.0.1", "@capacitor/cli": "^7.0.1",
"@capacitor/core": "^7.0.1", "@capacitor/core": "^7.0.1",
"@inlang/paraglide-sveltekit": "^0.15.5", "@inlang/paraglide-sveltekit": "^0.15.5",
"@uppy/core": "^4.4.1",
"@uppy/dashboard": "^4.3.1",
"@uppy/svelte": "^4.3.0",
"@uwdata/vgplot": "^0.12.2",
"canvas-confetti": "^1.9.3",
"globe.gl": "^2.39.2",
"gsap": "^3.12.7",
"leaflet": "^1.9.4",
"lucide-svelte": "^0.474.0", "lucide-svelte": "^0.474.0",
"motion": "^12.0.6" "maplibre-gl": "^5.1.0",
"motion": "^12.0.6",
"phoenix": "^1.7.18",
"pikaday": "^1.8.2",
"svelte-motion": "^0.12.2",
"svelte-transitions": "^1.2.0",
"three": "^0.172.0"
} }
} }

View File

@ -9,3 +9,4 @@
border-radius: 1rem; border-radius: 1rem;
padding: 2rem; padding: 2rem;
} }

View File

@ -1,12 +1,21 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation';
import { i18n } from '$lib/i18n'; import { i18n } from '$lib/i18n';
import Nav from '$lib/Nav.svelte'; import Nav from '$lib/Nav.svelte';
import { ParaglideJS } from '@inlang/paraglide-sveltekit'; import { ParaglideJS } from '@inlang/paraglide-sveltekit';
import { fade } from 'svelte/transition';
let { children } = $props(); let { children } = $props();
</script> </script>
<ParaglideJS {i18n}> <ParaglideJS {i18n}>
<div class="min-h-full fixed w-full bg-gradient-to-b from-sky-100 to-emerald-50 z-30">
<header class="flex items-center justify-between mb-6 px-6 pt-6 sticky top-0 w-full z-10" in:fade>
<button class="btn btn-circle btn-primary" onclick={() => {window.history.back()}}></button>
<h1 class="text-2xl font-bold text-sky-800 drop-shadow-sm">Boundless</h1>
<div class="w-8"></div>
</header>
<div class="fillscreen"> <div class="fillscreen">
{@render children()} {@render children()}
</div> </div>
@ -15,9 +24,9 @@
animate={true} animate={true}
darkMode={false} darkMode={false}
/> />
</div>
</ParaglideJS> </ParaglideJS>
<style> <style>
.fillscreen{ .fillscreen{
height: calc(100svh - 64px); height: calc(100svh - 64px);

View File

@ -1,90 +1,251 @@
<script lang="ts"> <script lang="ts">
import { Globe, Users, Plane, Instagram, Twitter, Youtube, MessageCircle, Heart } from "lucide-svelte"; import Globe from 'globe.gl';
import { onMount } from 'svelte';
import * as THREE from 'three';
const user = { // Types
name: 'Adventure Alex', interface Traveler {
bio: '🌍 Global Wanderer | Making friends one journey at a time', id: string;
location: 'Currently in Bali, Indonesia', name: string;
stats: [ avatar: string;
{ label: 'Countries', value: 27, icon: Globe }, location: string;
{ label: 'Friends', value: 143, icon: Users }, interests: string[];
{ label: 'Trips', value: 89, icon: Plane } currentMission?: string;
], impactPoints: number;
upcomingTrips: [ }
{ id: 1, destination: 'Tokyo, Japan', date: 'March 15, 2024' },
{ id: 2, destination: 'Patagonia, Chile', date: 'April 20, 2024' }
],
socials: [
{ name: 'Instagram', handle: '@adventure_alex', url: '#', icon: Instagram },
{ name: 'Twitter', handle: '@wanderlust_alex', url: '#', icon: Twitter },
{ name: 'YouTube', handle: 'Adventure Alex', url: '#', icon: Youtube }
],
posts: [
{ id: 1, title: 'Sunset in Uluwatu 🌅', content: 'Chasing golden hours with amazing new friends! #BaliVibes', likes: 142, comments: 28, date: '2024-02-20' },
{ id: 2, title: 'Mountain Trekking 🏔️', content: 'Conquered Mount Batur at sunrise! Worth every step.', likes: 89, comments: 15, date: '2024-02-18' },
{ id: 3, title: 'Ocean Dive Adventure 🌊', content: 'Explored the coral reefs today. An unforgettable underwater experience!', likes: 134, comments: 24, date: '2024-02-25' },
{ id: 4, title: 'Cultural Exploration in Ubud 🏯', content: 'Spent the day visiting temples and enjoying the local culture. Bali, you are beautiful.', likes: 210, comments: 37, date: '2024-02-22' },
{ id: 5, title: 'Relaxing Beach Day 🏖️', content: 'Laid back by the waves with a coconut in hand. Pure bliss.', likes: 180, comments: 31, date: '2024-02-23' },
{ id: 6, title: 'Island Hopping 🌴', content: 'Visited Nusa Penida today. The cliffs and beaches are otherworldly!', likes: 250, comments: 48, date: '2024-02-24' },
{ id: 7, title: 'Bali Swing Adventure 🏞️', content: 'Took a swing over the jungle in Ubud. Pure adrenaline!', likes: 320, comments: 55, date: '2024-01-15' },
{ id: 8, title: 'Coffee Tasting in Ubud ☕', content: 'Explored the world of Bali coffee today! The Luwak coffee was a highlight!', likes: 145, comments: 22, date: '2024-01-17' },
{ id: 9, title: 'Sunrise at Tanah Lot ⛅', content: 'Witnessed one of the most breathtaking sunrises at Tanah Lot Temple. A must-see!', likes: 198, comments: 40, date: '2024-01-19' },
{ id: 10, title: 'Exploring Balis Waterfalls 🌿', content: 'Trekking through the jungle to reach these stunning waterfalls. A perfect adventure!', likes: 215, comments: 38, date: '2024-01-21' },
{ id: 11, title: 'Bali Beach Party 🎉', content: 'Danced the night away on the beach with new friends! Bali nights are unforgettable.', likes: 350, comments: 72, date: '2024-01-23' },
{ id: 12, title: 'Yoga Retreat in Ubud 🧘', content: 'Spent the weekend focusing on mindfulness and yoga. Feeling recharged!', likes: 215, comments: 50, date: '2024-01-25' }
]
}; interface Serendipity {
id: string;
type: 'local_tip' | 'chance_meetup' | 'hidden_gem' | 'help_needed';
title: string;
description: string;
location: string;
creator: Traveler;
timePosted: Date;
}
const formatDate = (dateString: string) => { interface JourneyCircle {
const date = new Date(dateString); id: string;
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); name: string;
}; theme: string;
members: Traveler[];
nextDestination?: string;
impactGoal: string;
}
// State
let activeTab: 'magic' | 'allies' | 'impact' = 'magic';
let globeInstance: any;
let globeContainer: HTMLElement;
// Dummy data
const nearbyTravelers: Traveler[] = [
{
id: '1',
name: 'Sofia Chen',
avatar:
'https://w7.pngwing.com/pngs/48/259/png-transparent-profile-man-male-photo-face-portrait-illustration-vector-people-blue.png',
location: 'Currently in: Kyoto, Japan',
interests: ['Cultural Preservation', 'Street Photography', 'Local Markets'],
currentMission: 'Documenting traditional craftspeople',
impactPoints: 2840
},
{
id: '2',
name: 'Marco Rivera',
avatar:
'https://w7.pngwing.com/pngs/48/259/png-transparent-profile-man-male-photo-face-portrait-illustration-vector-people-blue.png',
location: 'Currently in: Kyoto, Japan',
interests: ['Sustainable Travel', 'Language Exchange', 'Hidden Cafes'],
currentMission: 'Teaching free English classes',
impactPoints: 1920
}
];
const serendipityFeeds: Serendipity[] = [
{
id: '1',
type: 'hidden_gem',
title: 'Secret Bamboo Grove Meditation',
description:
'Found a local monk who offers sunrise meditation sessions in a hidden bamboo grove. Space for 2 more people tomorrow!',
location: 'Arashiyama, Kyoto',
creator: nearbyTravelers[0],
timePosted: new Date()
},
{
id: '2',
type: 'help_needed',
title: 'Translation Help at Local Market',
description:
'Elderly shop owner needs help translating her traditional recipe collection. Perfect for Japanese speakers!',
location: 'Nishiki Market, Kyoto',
creator: nearbyTravelers[1],
timePosted: new Date()
}
];
const journeyCircles: JourneyCircle[] = [
{
id: '1',
name: 'Sustainable Storytellers',
theme: 'Documenting Environmental Initiatives',
members: [nearbyTravelers[0]],
nextDestination: 'Rural Hokkaido',
impactGoal: 'Create a photo series of local sustainable farming practices'
},
{
id: '2',
name: 'Culture Bridges',
theme: 'Connecting Communities Through Art',
members: [nearbyTravelers[1]],
nextDestination: 'Osaka',
impactGoal: 'Organize a collaborative mural project with local artists'
}
];
</script> </script>
<section class="p-6"> <div class="h-full overflow-scroll ">
<h2 class="mb-6 text-2xl font-bold text-gray-900">Recent Posts</h2> <main class="container mx-auto px-4 py-6">
<div class="space-y-4"> <!-- Navigation Tabs -->
{#each user.posts as post} <div class="tabs-boxed tabs my-4 justify-center bg-transparent">
<div class="post-card border border-gray-200 rounded-xl bg-white p-4 hover:bg-gray-50 transition-colors"> {#each [{ id: 'magic', icon: '🔮', label: 'Magic' }, { id: 'allies', icon: '👥', label: 'Friends<3' }, { id: 'impact', icon: '🏆', label: 'Impact' }] as tab}
<div class="flex gap-3"> <button
<!-- User Avatar --> class="tab-lg content-center tab p-10 gap-2 text-sm font-bold"
<div class="flex-shrink-0"> class:tab-active={activeTab === tab.id}
<img on:click={() => (activeTab = tab.id as any)}
src="https://w7.pngwing.com/pngs/48/259/png-transparent-profile-man-male-photo-face-portrait-illustration-vector-people-blue.png" >
alt="Adventure Alex" <span class="text-lg">{tab.icon}</span>
class="h-12 w-12 rounded-full border-2 border-blue-200" {tab.label}
/>
</div>
<!-- Tweet Content -->
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 text-sm">
<span class="font-bold text-gray-900">{user.name}</span>
<span class="text-gray-500">
{user.socials.find(s => s.name === 'Twitter')?.handle}
</span>
<span class="text-gray-400">·</span>
<time class="text-gray-500" datetime={post.date}>
{formatDate(post.date)}
</time>
</div>
<!-- Tweet Body -->
<p class="mt-2 text-gray-900 leading-relaxed">
{post.content}
</p>
<!-- Tweet Actions -->
<div class="mt-3 flex gap-6 text-gray-500">
<button class="flex items-center gap-2 hover:text-blue-500 transition-colors">
<MessageCircle size={18} />
<span class="text-sm">{post.comments}</span>
</button> </button>
<button class="flex items-center gap-2 hover:text-red-500 transition-colors"> {/each}
<Heart size={18} /> </div>
<span class="text-sm">{post.likes}</span>
{#if activeTab === 'magic'}
<div class="grid gap-6 md:grid-cols-2">
<!-- Serendipity Feed -->
<div class="space-y-4">
<h2 class="mb-4 text-xl font-bold">Today's Magic Opportunities ✨</h2>
{#each serendipityFeeds as feed}
<div class="card bg-base-100 shadow-sm transition-all hover:shadow-md">
<div class="card-body">
<div class="flex items-start gap-4">
<div class="avatar placeholder">
<div class="w-12 rounded-full bg-primary text-primary-content">
{feed.type === 'hidden_gem' ? '💎' : '🛎️'}
</div>
</div>
<div class="flex-1">
<div class="mb-2 flex items-start justify-between">
<h3 class="card-title text-base">{feed.title}</h3>
<span class="text-xs opacity-50">
{feed.timePosted.toLocaleTimeString()}
</span>
</div>
<p class="mb-4 text-sm opacity-75">{feed.description}</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<div class="avatar">
<a href="/profile">
<div class="w-8 rounded-full">
<img src={feed.creator.avatar} alt={feed.creator.name} />
</div>
</a>
</div>
<a href="/profile">
<span class="text-sm font-medium">{feed.creator.name}</span>
</a>
</div>
<button class="btn btn-primary btn-sm">
Join Now <span class="ml-1"></span>
</button>
</div>
</div>
</div>
</div>
</div>
{/each}
</div>
<!-- Globe Section -->
<div class="card sticky top-4 bg-base-100 shadow-sm">
</div>
</div>
{:else if activeTab === 'allies'}
<!-- Allies Section -->
<div class="grid gap-6 md:grid-cols-2">
<div class="space-y-6">
<h2 class="text-xl font-bold">Potential Allies ({nearbyTravelers.length})</h2>
{#each nearbyTravelers as traveler}
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="flex items-start gap-4">
<div class="avatar">
<div class="w-12 rounded-xl" >
<img src={traveler.avatar} alt={traveler.name} />
</div>
</div>
<div class="flex-1">
<div class="mb-2 flex items-start justify-between">
<h3 class="card-title text-base">{traveler.name}</h3>
<div class="badge badge-primary gap-1">
{traveler.impactPoints}
</div>
</div>
<p class="mb-3 text-sm opacity-75">{traveler.location}</p>
<div class="mb-4 flex flex-wrap gap-2">
{#each traveler.interests as interest}
<div class="badge badge-outline">#{interest}</div>
{/each}
</div>
<button class="btn btn-primary btn-sm btn-block"> Connect 🤝 </button>
</div>
</div>
</div>
</div>
{/each}
</div>
<!-- Journey Circles -->
<div class="space-y-6">
<div class="flex items-center justify-between">
<h2 class="text-xl font-bold">Featured Journey Circles</h2>
<button class="btn btn-link btn-sm">
See all <span class="ml-1"></span>
</button>
</div>
{#each journeyCircles as circle}
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="flex items-start gap-4">
<div class="avatar placeholder">
<div
class="w-12 rounded-xl bg-gradient-to-br from-primary to-secondary text-primary-content"
>
<span>🌟</span>
</div>
</div>
<div class="flex-1">
<h3 class="card-title text-base">{circle.name}</h3>
<p class="mb-3 text-sm opacity-75">{circle.theme}</p>
<div class="mb-4 flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-primary">▶︎ Next:</span>
<span class="font-medium">{circle.nextDestination}</span>
</div>
<div class="badge badge-ghost">
{circle.members.length} members
</div>
</div>
<div class="mb-4 flex items-center gap-2">
<progress class="progress progress-primary w-full" value="65" max="100"
></progress>
<span class="text-sm font-bold text-primary">65%</span>
</div>
<button class="btn btn-primary btn-sm btn-block">
Join Circle <span class="ml-1"></span>
</button> </button>
</div> </div>
</div> </div>
@ -92,4 +253,82 @@
</div> </div>
{/each} {/each}
</div> </div>
</section> </div>
{:else}
<!-- Impact Section -->
<div class="grid gap-6 md:grid-cols-2">
<div class="space-y-6">
<h2 class="text-xl font-bold">Global Impact Dashboard</h2>
<div class="grid grid-cols-2 gap-4">
<div class="card bg-primary text-primary-content">
<div class="card-body p-4">
<div class="text-2xl font-bold">12,847</div>
<div class="text-sm">Kind Acts</div>
<progress class="progress progress-info mt-2" value="82" max="100"></progress>
</div>
</div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body p-4">
<div class="text-2xl font-bold text-primary">47k+</div>
<div class="text-sm opacity-75">Lives Touched</div>
<div class="badge badge-success badge-sm mt-2 gap-1">▲ 12% from last month</div>
</div>
</div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body p-4">
<div class="text-2xl font-bold text-primary">284</div>
<div class="text-sm opacity-75">Projects</div>
<div class="mt-2 flex gap-1">
<div class="h-2 w-4 rounded-full bg-primary"></div>
<div class="h-2 w-4 rounded-full bg-primary/30"></div>
<div class="h-2 w-4 rounded-full bg-primary/30"></div>
</div>
</div>
</div>
<div class="card bg-base-100 shadow-sm">
<div class="card-body p-4">
<div class="text-2xl font-bold text-primary">89%</div>
<div class="text-sm opacity-75">Carbon Offset</div>
<div class="badge badge-ghost mt-2 gap-1">🌳 12k trees planted</div>
</div>
</div>
</div>
</div>
<div class="space-y-6">
<h2 class="text-xl font-bold">Your Impact Journey</h2>
<div class="card bg-base-100 shadow-sm">
<div class="card-body">
<div class="flex items-center gap-4">
<div class="avatar placeholder">
<div class="w-12 rounded-lg bg-primary text-primary-content">🏅</div>
</div>
<div class="flex-1">
<div class="flex items-center justify-between">
<div>
<div class="font-bold">Global Citizen</div>
<div class="text-sm opacity-75">Level 24 Explorer</div>
</div>
<div class="text-right">
<div class="text-sm opacity-75">Next Level</div>
<div class="text-lg font-bold text-primary">+1,240 XP</div>
</div>
</div>
<progress class="progress progress-primary mt-4" value="65" max="100"></progress>
</div>
</div>
</div>
</div>
</div>
</div>
{/if}
</main>
</div>
<style>
:global(body) {
--rounded-box: 1rem;
--rounded-btn: 0.5rem;
--rounded-badge: 1.25rem;
}
</style>

View File

@ -102,7 +102,7 @@
</style> </style>
<!-- Main Layout --> <!-- Main Layout -->
<div class="flex flex-col md:flex-row h-full bg-base-200"> <div class="flex flex-col md:flex-row h-full ">
<!-- Mobile Header --> <!-- Mobile Header -->
<div class="md:hidden navbar bg-base-100 border-b border-base-300 px-4"> <div class="md:hidden navbar bg-base-100 border-b border-base-300 px-4">
<div class="flex items-center w-full"> <div class="flex items-center w-full">
@ -130,8 +130,8 @@
</div> </div>
<!-- User List --> <!-- User List -->
<div class={`w-full md:w-80 bg-base-100 border-r border-base-300 fixed md:relative h-full menu-transition ${$showUserList ? 'translate-x-0' : '-translate-x-full'}`}> <div class={`w-full md:w-80 bg-base-100 border-t border-r rounded-tr-lg border-base-300 fixed md:relative h-full menu-transition ${$showUserList ? 'translate-x-0' : '-translate-x-full'}`}>
<div class="hidden md:block p-4 bg-base-200"> <div class="hidden md:block p-4 bg-neutral-50">
<h1 class="text-xl font-bold">Direct Messages</h1> <h1 class="text-xl font-bold">Direct Messages</h1>
</div> </div>
<div class="menu p-2 overflow-y-auto h-[calc(100vh-4rem)]"> <div class="menu p-2 overflow-y-auto h-[calc(100vh-4rem)]">

View File

@ -1,33 +1,96 @@
<!-- +page.svelte -->
<script> <script>
// @ts-nocheck
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
// You can add your data or functions here if needed // Fleets data
const cards = [ const fleets = [
{ title: 'Swipe', description: 'swipe and make friends', btn: 'lesgooo' }, { user: 'Swipe', handle: '@user1', avatar: '😊' },
]; { user: 'feature', handle: '@user2', avatar: '😎' },
{ user: 'feature', handle: '@user3', avatar: '🤩' },
{ user: 'feature', handle: '@user4', avatar: '🥳' },
];
// Tweets data
const tweets = [
{
user: 'SvelteFan',
handle: '@svelte_dev',
avatar: '🚀',
content: "Just discovered Svelte and it's amazing! #webdev",
likes: 42,
retweets: 12,
time: '2h'
},
{
user: 'WebDev',
handle: '@webdev',
avatar: '💻',
content: 'CSS grid is a game changer for layouts. Change my mind.',
likes: 89,
retweets: 24,
time: '4h'
},
];
</script> </script>
<div class="container mx-auto p-4"> <div class="container mx-auto p-4 max-w-2xl">
<!-- Hero Section --> <!-- Fleets Section -->
<div class="text-center py-12"> <div class="flex space-x-4 pb-4 overflow-x-auto scrollbar-hide">
<h1 class="text-4xl font-bold mb-4">Welcome user</h1> {#each fleets as fleet}
<div class="flex flex-col items-center space-y-2 flex-shrink-0">
<div class="p-1 rounded-full bg-gradient-to-tr from-primary to-secondary hover:from-secondary hover:to-primary">
<button on:click={goto("/swipe")} class="avatar bg-base-200 rounded-full w-16 h-16 flex items-center justify-center text-2xl hover:opacity-90">
{fleet.avatar}
</button>
</div>
<span class="text-sm font-medium">{fleet.user}</span>
</div>
{/each}
</div> </div>
<!-- Card Grid --> <!-- Tweets Section -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div class="space-y-4">
{#each cards as card} {#each tweets as tweet}
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow duration-200"> <div class="bg-white rounded-xl shadow-sm p-4">
<figure class="px-6 pt-6"> <div class="flex gap-3">
<div class="h-32 w-full bg-base-200 rounded-lg flex items-center justify-center"> <!-- Avatar -->
<span class="text-4xl">😎</span> <!-- Replace with actual icon/image --> <div class="avatar bg-base-200 rounded-full w-12 h-12 flex items-center justify-center text-xl">
{tweet.avatar}
</div>
<!-- Tweet Content -->
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="font-bold">{tweet.user}</span>
<span class="text-gray-600">{tweet.handle}</span>
<span class="text-gray-600">· {tweet.time}</span>
</div>
<p class="mt-2">{tweet.content}</p>
<!-- Tweet Actions -->
<div class="flex justify-between mt-4 text-gray-600 max-w-md">
<button class="hover:text-primary flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
{tweet.retweets}
</button>
<button class="hover:text-green-500 flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
{tweet.retweets}
</button>
<button class="hover:text-red-500 flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
{tweet.likes}
</button>
</div> </div>
</figure>
<div class="card-body items-center text-center">
<h2 class="card-title">{card.title}</h2>
<p class="text-gray-600">{card.description}</p>
<div class="card-actions mt-4">
<button on:click={() => {goto('/swipe')}} class="btn btn-primary">{card.btn}</button>
</div> </div>
</div> </div>
</div> </div>
@ -36,8 +99,14 @@
</div> </div>
<style global> <style global>
/* Optional custom styles */ /* Custom scrollbar hide */
:global(body) { .scrollbar-hide::-webkit-scrollbar {
background-color: #f5f5f5; display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
} }
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1,28 +1,76 @@
<script lang="ts"> <script>
import type { Notification } from '$lib/types'; import { BellIcon, CheckIcon, GlobeIcon, UserIcon } from 'lucide-svelte';
import { fly } from 'svelte/transition';
export let notifications: Notification[]; let notifications = [
</script> { type: 'alert', message: 'Lena accepted your Paris trip invite!', timestamp: '2m ago', read: false },
{ type: 'update', message: 'New local experience in Kyoto: Tea ceremony with master Takano', timestamp: '1h ago', read: false },
{ type: 'friend', message: 'Marc from your Barcelona hostel wants to connect', timestamp: '4h ago', read: true },
{ type: 'success', message: 'Your Bali itinerary is ready! 3 locals joined the plan', timestamp: '1d ago', read: true }
];
<div class="p-4 max-w-3xl mx-auto"> function markAllRead() {
<h1 class="text-2xl font-bold mb-6">Notifications</h1> notifications = notifications.map(n => ({ ...n, read: true }));
<div class="space-y-4"> }
{#each notifications as notification (notification.id)} </script>
<div
class="p-4 rounded-lg {!notification.read ? 'bg-primary/10 border-l-4 border-primary' : 'bg-base-200'}" <div class="min-h-screen ">
>
<div class="flex justify-between items-start">
<div> <main class="max-w-2xl mx-auto p-4 space-y-6">
<p class="font-medium">{notification.content}</p> <div class="flex items-center justify-between mb-8">
<p class="text-sm text-base-content/50 mt-1"> <h1 class="text-3xl font-bold flex items-center gap-2">
{notification.timestamp} <GlobeIcon class="w-8 h-8 text-accent" />
</p> Journey Updates
</h1>
<button on:click={markAllRead} class="btn btn-sm btn-outline">Mark all as read</button>
</div>
{#if notifications.length === 0}
<div class="text-center p-8 rounded-xl bg-base-100">
<p class="text-lg text-gray-500">No new updates - your next adventure is waiting!</p>
</div>
{:else}
<div class="space-y-4">
{#each notifications as notification (notification.timestamp)}
<div
transition:fly={{ y: 20, duration: 300 }}
class:opacity-50={notification.read}
class="card bg-base-100 shadow-sm hover:shadow-md transition-shadow cursor-pointer group"
>
<div class="card-body p-4">
<div class="flex items-start gap-4">
{#if notification.type === 'alert'}
<div class="text-warning">
<UserIcon class="w-6 h-6" />
</div>
{:else if notification.type === 'update'}
<div class="text-info">
<GlobeIcon class="w-6 h-6" />
</div>
{:else if notification.type === 'friend'}
<div class="text-secondary">
<UserIcon class="w-6 h-6" />
</div>
{:else}
<div class="text-success">
<CheckIcon class="w-6 h-6" />
</div> </div>
{#if !notification.read}
<span class="badge badge-primary badge-sm">New</span>
{/if} {/if}
<div class="flex-1">
<p class="font-medium">{notification.message}</p>
<p class="text-sm text-gray-500">{notification.timestamp}</p>
</div>
{#if !notification.read}
<div class="w-2 h-2 bg-primary rounded-full self-center ml-4 animate-pulse" />
{/if}
</div>
</div> </div>
</div> </div>
{/each} {/each}
</div> </div>
</div> {/if}
</main>
</div>

View File

@ -10,7 +10,29 @@
MessageCircle, MessageCircle,
Instagram, Instagram,
Twitter, Twitter,
Youtube Youtube,
Flame,
Crown,
Languages,
Trophy,
Clock
,
ListMusic
,
Music
,
Play
,
RepeatIcon
,
Shuffle
} from 'lucide-svelte'; } from 'lucide-svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
@ -52,7 +74,89 @@
comments: 15, comments: 15,
date: '2024-02-18' date: '2024-02-18'
} }
],
duolingo: {
streakDays: 145,
totalXP: 24680,
crown: 78,
languages: [
{
name: 'Spanish',
level: 'Advanced',
progress: 85,
xp: 12400,
streakWeeks: 12
},
{
name: 'Japanese',
level: 'Intermediate',
progress: 65,
xp: 8200,
streakWeeks: 8
},
{
name: 'French',
level: 'Beginner',
progress: 25,
xp: 4080,
streakWeeks: 4
}
] ]
},
spotify: {
topArtists: [
{ name: 'The Lumineers', genre: 'Folk Rock', imageUrl: '/api/placeholder/60/60' },
{ name: 'Coldplay', genre: 'Alternative Rock', imageUrl: '/api/placeholder/60/60' },
{ name: 'Ed Sheeran', genre: 'Pop', imageUrl: '/api/placeholder/60/60' }
],
recentlyPlayed: [
{
title: 'Ho Hey',
artist: 'The Lumineers',
album: 'The Lumineers',
duration: '2:43',
imageUrl: '/api/placeholder/50/50'
},
{
title: 'Viva La Vida',
artist: 'Coldplay',
album: 'Viva la Vida',
duration: '3:45',
imageUrl: '/api/placeholder/50/50'
},
{
title: 'Shape of You',
artist: 'Ed Sheeran',
album: '÷ (Divide)',
duration: '3:53',
imageUrl: '/api/placeholder/50/50'
}
],
playlists: [
{
name: 'Travel Vibes',
tracks: 45,
imageUrl: '/api/placeholder/80/80'
},
{
name: 'Workout Mix',
tracks: 32,
imageUrl: '/api/placeholder/80/80'
},
{
name: 'Chill Evening',
tracks: 28,
imageUrl: '/api/placeholder/80/80'
}
],
currentlyPlaying: {
title: 'Ho Hey',
artist: 'The Lumineers',
album: 'The Lumineers',
progress: 65,
imageUrl: '/api/placeholder/60/60'
}
}
}; };
const toggleFollow = () => { const toggleFollow = () => {
@ -103,15 +207,49 @@
floating = !floating; floating = !floating;
}, 3000); }, 3000);
} }
const languageCards = document.querySelectorAll('.language-card') as any;
languageCards.forEach((card: any, index: any) => {
animate(card, { opacity: [0, 1], x: [20, 0] }, {
delay: 0.5 + index * 0.1,
duration: 0.6,
easing: 'easeOut'
} as any);
}); });
const spotifyCards = document.querySelectorAll('.spotify-card') as any;
const trackItems = document.querySelectorAll('.track-item') as any;
spotifyCards.forEach((card: any, index: any) => {
animate(card, { opacity: [0, 1], y: [20, 0] }, {
delay: 0.3 + index * 0.1,
duration: 0.6,
easing: 'easeOut'
} as any);
});
trackItems.forEach((item: any, index: any) => {
animate(item, { opacity: [0, 1], x: [-20, 0] }, {
delay: 0.4 + index * 0.1,
duration: 0.5,
easing: 'easeOut'
} as any);
});
});
let isPlaying = false;
const togglePlay = () => {
isPlaying = !isPlaying;
};
const formatDate = (dateString: string) => { const formatDate = (dateString: string) => {
const date = new Date(dateString); const date = new Date(dateString);
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}; };
</script> </script>
<div class="h-full overflow-scroll bg-gray-100 py-8"> <div class="h-full overflow-scroll py-8 mb-14">
<!-- Floating Airplane --> <!-- Floating Airplane -->
<div class="floating-plane absolute right-10 top-20 text-blue-500"> <div class="floating-plane absolute right-10 top-20 text-blue-500">
<Plane size={48} class="-rotate-45 transform" /> <Plane size={48} class="-rotate-45 transform" />
@ -226,6 +364,159 @@
</div> </div>
</section> </section>
<!-- Duolingo Section -->
<section class="bg-green-50 p-8">
<div class="mb-6 flex items-center gap-3">
<img
src="https://cdn.worldvectorlogo.com/logos/duolingo-symbol-icon.svg"
alt="Duolingo"
class="mx-4 h-12"
/>
<h2 class="text-2xl font-bold text-gray-900">Language Learning Journey</h2>
</div>
<!-- Stats Cards -->
<div class="mb-8 grid grid-cols-3 gap-4">
<div class="rounded-xl bg-white p-6 shadow-md transition-all hover:shadow-lg">
<div class="flex items-center gap-3">
<Flame class="text-orange-500" size={24} />
<div>
<h3 class="text-3xl font-bold text-gray-900">{user.duolingo.streakDays}</h3>
<p class="text-sm text-gray-600">Day Streak</p>
</div>
</div>
</div>
<div class="rounded-xl bg-white p-6 shadow-md transition-all hover:shadow-lg">
<div class="flex items-center gap-3">
<Trophy class="text-yellow-500" size={24} />
<div>
<h3 class="text-3xl font-bold text-gray-900">{user.duolingo.totalXP}</h3>
<p class="text-sm text-gray-600">Total XP</p>
</div>
</div>
</div>
<div class="rounded-xl bg-white p-6 shadow-md transition-all hover:shadow-lg">
<div class="flex items-center gap-3">
<Crown class="text-purple-500" size={24} />
<div>
<h3 class="text-3xl font-bold text-gray-900">{user.duolingo.crown}</h3>
<p class="text-sm text-gray-600">Crown Level</p>
</div>
</div>
</div>
</div>
<!-- Language Progress Cards -->
<div class="space-y-4">
{#each user.duolingo.languages as language}
<div
class="language-card rounded-xl bg-white p-6 shadow-md transition-all hover:shadow-lg"
>
<div class="mb-4 flex items-center justify-between">
<div class="flex items-center gap-3">
<Languages class="text-green-500" size={24} />
<div>
<h3 class="text-xl font-bold text-gray-900">{language.name}</h3>
<p class="text-sm text-gray-600">{language.level}</p>
</div>
</div>
<div class="text-right">
<p class="text-lg font-bold text-green-500">{language.xp} XP</p>
<p class="text-sm text-gray-600">{language.streakWeeks} week streak</p>
</div>
</div>
<!-- Progress Bar -->
<div class="h-2 w-full overflow-hidden rounded-full bg-gray-200">
<div
class="h-full bg-green-500 transition-all duration-500"
style="width: {language.progress}%"
/>
</div>
</div>
{/each}
</div>
</section>
<!-- Spotify Section -->
<section class="bg-gradient-to-b from-green-900 to-black p-8 text-white">
<div class="mb-6 flex items-center gap-3">
<svg class="h-8 w-8" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"
/>
</svg>
<h2 class="text-2xl font-bold">Music Profile</h2>
</div>
<div class="grid gap-6 lg:grid-cols-2">
<!-- Top Artists -->
<div class="spotify-card rounded-xl bg-white bg-opacity-20 p-6">
<h3 class="mb-4 flex items-center gap-2 text-lg font-semibold">
<Music size={20} />
Top Artists
</h3>
<div class="space-y-4">
{#each user.spotify.topArtists as artist}
<div class="flex items-center gap-4">
<img src={artist.imageUrl} alt={artist.name} class="h-12 w-12 rounded-full" />
<div>
<h4 class="font-semibold">{artist.name}</h4>
<p class="text-sm text-gray-300">{artist.genre}</p>
</div>
</div>
{/each}
</div>
</div>
<!-- Recently Played -->
<div class="spotify-card rounded-xl bg-white bg-opacity-20 p-6">
<h3 class="mb-4 flex items-center gap-2 text-lg font-semibold">
<Clock size={20} />
Current Obsession
</h3>
<div class="space-y-4">
{#each user.spotify.recentlyPlayed as track}
<div class="track-item flex items-center gap-4">
<img src={track.imageUrl} alt={track.title} class="h-10 w-10 rounded" />
<div class="flex-1">
<h4 class="font-semibold">{track.title}</h4>
<p class="text-sm text-gray-300">{track.artist}</p>
</div>
<span class="text-sm text-gray-300">{track.duration}</span>
</div>
{/each}
</div>
</div>
</div>
<!-- Playlists -->
<div class="my-7">
<h3 class="mb-4 flex items-center gap-2 text-lg font-semibold">
<ListMusic size={20} />
Public Playlists
</h3>
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3">
{#each user.spotify.playlists as playlist}
<div
class="spotify-card rounded-xl bg-white bg-opacity-20 p-4 transition-all hover:bg-opacity-30"
>
<img
src={playlist.imageUrl}
alt={playlist.name}
class="mb-5 h-full w-full rounded-lg object-cover"
/>
<h4 class="font-semibold">{playlist.name}</h4>
<p class="text-sm text-gray-300">{playlist.tracks} tracks</p>
</div>
{/each}
</div>
</div>
</section>
<!-- Upcoming Trips --> <!-- Upcoming Trips -->
<section class="p-8"> <section class="p-8">
<h2 class="mb-6 text-2xl font-bold text-gray-900">Upcoming Trips</h2> <h2 class="mb-6 text-2xl font-bold text-gray-900">Upcoming Trips</h2>
@ -260,4 +551,12 @@
.hover-scale:hover { .hover-scale:hover {
transform: translateY(-2px); transform: translateY(-2px);
} }
.language-card {
transition: all 0.2s ease-in-out;
}
.language-card:hover {
transform: translateY(-2px);
}
</style> </style>

View File

@ -55,7 +55,7 @@
} }
</script> </script>
<div class="min-h-screen bg-base-200 p-4 md:p-8"> <div class="h-full p-4 md:p-8">
<!-- Hero Section --> <!-- Hero Section -->
<div class="hero bg-base-100 rounded-box mb-8 p-4 md:p-6" in:fly={{ y: 50, duration: 500 }}> <div class="hero bg-base-100 rounded-box mb-8 p-4 md:p-6" in:fly={{ y: 50, duration: 500 }}>
<div class="hero-content text-center"> <div class="hero-content text-center">
@ -69,7 +69,7 @@
</div> </div>
<!-- Search Bar --> <!-- Search Bar -->
<div class="form-control w-fit max-w-3xl mx-10 mb-4 "> <div class="form-control mx-10 mb-4 ">
<div class="input-group flex gap-4"> <div class="input-group flex gap-4">
<input <input
type="text" type="text"

View File

@ -15,20 +15,26 @@
}; };
} }
// Use proper typing for the stack
let currentIndex = 0; let currentIndex = 0;
let stack: any[] = profiles; let stack: Profile[] = profiles;
let connections: Profile[] = []; let connections: Profile[] = [];
let dragging: Profile | null = null; let dragging: Profile | null = null;
let dragStartOffset = { x: 0, y: 0 }; let dragStartOffset = { x: 0, y: 0 };
let dragCoords = spring({ x: 0, y: 0 }, { // Add proper typing to spring
let dragCoords = spring<{x: number, y: number}>({ x: 0, y: 0 }, {
stiffness: 0.2, stiffness: 0.2,
damping: 0.7 damping: 0.7
}); });
const calculateConnectionPath = (connection: Profile) => { // Add proper return type
const calculateConnectionPath = (connection: Profile): string => {
const start = connection.location || { x: 500, y: 250 }; const start = connection.location || { x: 500, y: 250 };
const end = { x: window.innerWidth - 100, y: window.innerHeight - 100 }; // Store window dimensions to avoid repeated access
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const end = { x: windowWidth - 100, y: windowHeight - 100 };
const controlPoint = { const controlPoint = {
x: (start.x + end.x) / 2, x: (start.x + end.x) / 2,
y: Math.min(start.y, end.y) - 100 y: Math.min(start.y, end.y) - 100
@ -36,18 +42,31 @@
return `M ${start.x},${start.y} Q ${controlPoint.x},${controlPoint.y} ${end.x},${end.y}`; return `M ${start.x},${start.y} Q ${controlPoint.x},${controlPoint.y} ${end.x},${end.y}`;
}; };
const handleAction = (action: 'skip' | 'decide' | 'add') => { // Type the action parameter properly
const handleAction = (action: 'skip' | 'decide' | 'add'): void => {
if (stack.length === 0) return; if (stack.length === 0) return;
const profile = stack[currentIndex]; const profile = stack[currentIndex];
if (action === 'add') { if (action === 'add') {
connections = [...connections, { ...profile, location: { ...$dragCoords } }]; const draggedLocation = { x: $dragCoords.x, y: $dragCoords.y };
connections = [...connections, { ...profile, location: draggedLocation }];
const suitcase = document.querySelector('#suitcase'); const suitcase = document.querySelector('#suitcase');
const profileCard = document.querySelector(`#profile-${profile.id}`); const profileCard = document.querySelector(`#profile-${profile.id}`);
if (suitcase && profileCard) { if (suitcase && profileCard) {
animateProfileToSuitcase(profileCard, suitcase);
}
}
// Remove the current profile and update the stack
stack = stack.filter((_, i) => i !== currentIndex);
currentIndex = Math.min(currentIndex, stack.length - 1);
};
// Extract animation logic to a separate function
const animateProfileToSuitcase = (profileCard: Element, suitcase: Element): void => {
const clone = profileCard.cloneNode(true) as HTMLElement; const clone = profileCard.cloneNode(true) as HTMLElement;
document.body.appendChild(clone); document.body.appendChild(clone);
@ -67,14 +86,9 @@
}); });
setTimeout(() => clone.remove(), 500); setTimeout(() => clone.remove(), 500);
}
}
stack = stack.filter((_, i) => i !== currentIndex);
currentIndex = Math.min(currentIndex, stack.length - 1);
}; };
const startDrag = (event: MouseEvent, profile: Profile) => { const startDrag = (event: MouseEvent, profile: Profile): void => {
const element = event.currentTarget as HTMLElement; const element = event.currentTarget as HTMLElement;
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
@ -92,7 +106,7 @@
document.body.style.cursor = 'grabbing'; document.body.style.cursor = 'grabbing';
}; };
function handleMouseMove(event: MouseEvent) { function handleMouseMove(event: MouseEvent): void {
if (dragging) { if (dragging) {
dragCoords.set({ dragCoords.set({
x: event.clientX - dragStartOffset.x, x: event.clientX - dragStartOffset.x,
@ -101,7 +115,7 @@
} }
} }
function handleMouseUp() { function handleMouseUp(): void {
if (dragging) { if (dragging) {
const suitcase = document.querySelector('#suitcase'); const suitcase = document.querySelector('#suitcase');
if (suitcase) { if (suitcase) {
@ -117,14 +131,14 @@
} }
} }
function isOverlapping(rect1: DOMRect, rect2: DOMRect) { function isOverlapping(rect1: DOMRect, rect2: DOMRect): boolean {
return !(rect1.right < rect2.left || return !(rect1.right < rect2.left ||
rect1.left > rect2.right || rect1.left > rect2.right ||
rect1.bottom < rect2.top || rect1.bottom < rect2.top ||
rect1.top > rect2.bottom); rect1.top > rect2.bottom);
} }
function handleKeyDown(event: KeyboardEvent, profile: Profile) { function handleKeyDown(event: KeyboardEvent, profile: Profile): void {
if (event.key === 'Enter' || event.key === ' ') { if (event.key === 'Enter' || event.key === ' ') {
const element = event.currentTarget as HTMLElement; const element = event.currentTarget as HTMLElement;
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
@ -135,15 +149,22 @@
}), profile); }), profile);
} }
} }
// Add cleanup for window event listeners
import { onDestroy } from 'svelte';
onDestroy(() => {
document.body.style.cursor = '';
});
</script> </script>
<svelte:window on:mousemove={handleMouseMove} on:mouseup={handleMouseUp} /> <svelte:window on:mousemove={handleMouseMove} on:mouseup={handleMouseUp} />
<div class="min-h-screen bg-stone-100 font-handwritten relative overflow-hidden"> <div class="h-full font-handwritten relative overflow-hidden">
<!-- Interactive World Map Background --> <!-- Interactive World Map Background -->
<div class="absolute inset-0 opacity-25 z-0 pointer-events-none"> <div class="absolute inset-0 opacity-25 z-0 pointer-events-none">
<svg viewBox="0 0 1000 500" class="w-full h-full" role="presentation"> <svg viewBox="0 0 1000 500" class="w-full h-full" role="presentation">
{#each connections as connection} {#each connections as connection (connection.id)}
<path d={calculateConnectionPath(connection)} <path d={calculateConnectionPath(connection)}
class="stroke-pink-400/40 stroke-2 animate-dash" class="stroke-pink-400/40 stroke-2 animate-dash"
fill="none" /> fill="none" />
@ -186,14 +207,13 @@
animate:flip={{ delay: i * 50, duration: 800, easing: quintOut }} animate:flip={{ delay: i * 50, duration: 800, easing: quintOut }}
on:mousedown={(e) => startDrag(e, profile)} on:mousedown={(e) => startDrag(e, profile)}
on:keydown={(e) => handleKeyDown(e, profile)}> on:keydown={(e) => handleKeyDown(e, profile)}>
<!-- Profile Card -->
<div class="bg-white p-6 rounded-lg shadow-xl border-4 border-double border-amber-700 <div class="bg-white p-6 rounded-lg shadow-xl border-4 border-double border-amber-700
transition-all hover:shadow-2xl hover:scale-105 origin-center transition-all hover:shadow-2xl hover:scale-105 origin-center
backdrop-blur-sm bg-opacity-90" backdrop-blur-sm bg-opacity-90"
style="width: min(80vw, 400px);"> style="width: min(80vw, 400px);">
<h3 class="text-3xl mb-4 text-cyan-800">{profile.name}</h3> <h3 class="text-3xl mb-4 text-cyan-800">{profile.name}</h3>
<div class="flex flex-wrap gap-2 mb-4"> <div class="flex flex-wrap gap-2 mb-4">
{#each profile.interests as interest} {#each profile.interests as interest (interest)}
<span class="px-3 py-1 bg-yellow-200/50 rounded-full <span class="px-3 py-1 bg-yellow-200/50 rounded-full
border-2 border-dashed border-amber-600/30"> border-2 border-dashed border-amber-600/30">
{interest} {interest}
@ -202,7 +222,7 @@
</div> </div>
<div class="h-48 bg-stone-50 rounded border-2 border-stone-200 mb-4 relative overflow-hidden"> <div class="h-48 bg-stone-50 rounded border-2 border-stone-200 mb-4 relative overflow-hidden">
{#if profile.moodImages} {#if profile.moodImages}
{#each profile.moodImages as img, index} {#each profile.moodImages as img, index (img)}
<img src={img} <img src={img}
alt={`Mood image ${index + 1} for ${profile.name}`} alt={`Mood image ${index + 1} for ${profile.name}`}
class="absolute w-16 h-16 object-cover shadow-lg border-4 border-white" class="absolute w-16 h-16 object-cover shadow-lg border-4 border-white"

View File

@ -141,7 +141,7 @@
/> />
{/each} {/each}
<h1 class="mb-4 animate-[slideDown_0.5s] text-5xl font-bold text-white">🌍 WanderLink</h1> <h1 class="mb-4 animate-[slideDown_0.5s] text-5xl font-bold text-white">Boundless 🌍✨</h1>
<button <button
on:click={() => animateSequence(welcomeEl, registerEl)} on:click={() => animateSequence(welcomeEl, registerEl)}
class="animate-[bounce_2s_infinite] rounded-full bg-white/20 px-8 py-3 text-white backdrop-blur-sm transition-all hover:bg-white/30" class="animate-[bounce_2s_infinite] rounded-full bg-white/20 px-8 py-3 text-white backdrop-blur-sm transition-all hover:bg-white/30"