more changes
This commit is contained in:
parent
e7eaf2a592
commit
727da919a5
153
frontend/package-lock.json
generated
153
frontend/package-lock.json
generated
@ -8,14 +8,20 @@
|
||||
"name": "frontend",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
||||
"@capacitor/cli": "^7.0.1",
|
||||
"@capacitor/core": "^7.0.1",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@inlang/paraglide-sveltekit": "^0.15.5",
|
||||
"@svelte-plugins/datepicker": "^1.0.11",
|
||||
"@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",
|
||||
"date-fns": "^4.1.0",
|
||||
"globe.gl": "^2.39.2",
|
||||
"gsap": "^3.12.7",
|
||||
"leaflet": "^1.9.4",
|
||||
@ -24,6 +30,7 @@
|
||||
"motion": "^12.0.6",
|
||||
"phoenix": "^1.7.18",
|
||||
"pikaday": "^1.8.2",
|
||||
"sonner": "^1.7.3",
|
||||
"svelte-motion": "^0.12.2",
|
||||
"svelte-transitions": "^1.2.0",
|
||||
"three": "^0.172.0"
|
||||
@ -88,6 +95,17 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@atlaskit/pragmatic-drag-and-drop": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.4.0.tgz",
|
||||
"integrity": "sha512-qRY3PTJIcxfl/QB8Gwswz+BRvlmgAC5pB+J2hL6dkIxgqAgVwOhAamMUKsrOcFU/axG2Q7RbNs1xfoLKDuhoPg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"bind-event-listener": "^3.0.0",
|
||||
"raf-schd": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
|
||||
@ -141,6 +159,73 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/modifiers": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz",
|
||||
"integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dnd-kit/core": "^6.3.0",
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/sortable": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
|
||||
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dnd-kit/core": "^6.3.0",
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/utilities": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@duckdb/duckdb-wasm": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.29.0.tgz",
|
||||
@ -1933,6 +2018,12 @@
|
||||
"integrity": "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@svelte-plugins/datepicker": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@svelte-plugins/datepicker/-/datepicker-1.0.11.tgz",
|
||||
"integrity": "sha512-Tqc07QLyRkCpc3Glg6oRLTUApLtCrOh52d6vJ7L32QI17HrwvcDDjaH3LF3X1SBm3CWdMrnqfJp3xjUZmB4wzw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sveltejs/adapter-auto": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.3.1.tgz",
|
||||
@ -3333,6 +3424,12 @@
|
||||
"integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bind-event-listener": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bind-event-listener/-/bind-event-listener-3.0.0.tgz",
|
||||
"integrity": "sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bottleneck": {
|
||||
"version": "2.19.5",
|
||||
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
|
||||
@ -4286,6 +4383,16 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
@ -7256,6 +7363,35 @@
|
||||
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/raf-schd": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@ -7594,6 +7730,13 @@
|
||||
"integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
@ -7723,6 +7866,16 @@
|
||||
"csstype": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sonner": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.3.tgz",
|
||||
"integrity": "sha512-KXLWQfyR6AHpYZuQk8eO8fCbZSJY3JOpgsu/tbGc++jgPjj8JsR1ZpO8vFhqR/OxvWMQCSAmnSShY0gr4FPqHg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
@ -50,14 +50,20 @@
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
||||
"@capacitor/cli": "^7.0.1",
|
||||
"@capacitor/core": "^7.0.1",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@inlang/paraglide-sveltekit": "^0.15.5",
|
||||
"@svelte-plugins/datepicker": "^1.0.11",
|
||||
"@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",
|
||||
"date-fns": "^4.1.0",
|
||||
"globe.gl": "^2.39.2",
|
||||
"gsap": "^3.12.7",
|
||||
"leaflet": "^1.9.4",
|
||||
@ -66,6 +72,7 @@
|
||||
"motion": "^12.0.6",
|
||||
"phoenix": "^1.7.18",
|
||||
"pikaday": "^1.8.2",
|
||||
"sonner": "^1.7.3",
|
||||
"svelte-motion": "^0.12.2",
|
||||
"svelte-transitions": "^1.2.0",
|
||||
"three": "^0.172.0"
|
||||
|
@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Globe from 'globe.gl';
|
||||
import { onMount } from 'svelte';
|
||||
import * as THREE from 'three';
|
||||
|
||||
// Types
|
||||
interface Traveler {
|
||||
|
@ -6,10 +6,10 @@
|
||||
|
||||
// Fleets data
|
||||
const fleets = [
|
||||
{ user: 'Swipe', handle: '@user1', avatar: '😊' },
|
||||
{ user: 'feature', handle: '@user2', avatar: '😎' },
|
||||
{ user: 'feature', handle: '@user3', avatar: '🤩' },
|
||||
{ user: 'feature', handle: '@user4', avatar: '🥳' },
|
||||
{ user: 'Swipe', handle: '@user1', avatar: '😊' , url: '/home/swipe'},
|
||||
{ user: 'Dinner', handle: '@user2', avatar: '😎', url: '/home/dinner'},
|
||||
{ user: 'Chat', handle: '@user3', avatar: '🤩', url: '/home/chat'},
|
||||
{ user: 'Plan', handle: '@user4', avatar: '🥳', url: '/home/plan'},
|
||||
];
|
||||
|
||||
// Tweets data
|
||||
@ -41,7 +41,7 @@
|
||||
{#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">
|
||||
<button on:click={goto(fleet.url)} 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>
|
||||
|
78
frontend/src/routes/(authed)/home/chat/+page.svelte
Normal file
78
frontend/src/routes/(authed)/home/chat/+page.svelte
Normal file
@ -0,0 +1,78 @@
|
||||
<script lang="ts">
|
||||
let messages: any = [];
|
||||
let newMessage = '';
|
||||
let selectedLanguage = 'en';
|
||||
|
||||
const languages = [
|
||||
{ value: 'en', label: 'English' },
|
||||
{ value: 'es', label: 'Español' },
|
||||
{ value: 'fr', label: 'Français' },
|
||||
{ value: 'de', label: 'Deutsch' },
|
||||
{ value: 'ja', label: '日本語' }
|
||||
];
|
||||
|
||||
function addMessage() {
|
||||
if (!newMessage.trim()) return;
|
||||
|
||||
// Add user message
|
||||
messages = [...messages, {
|
||||
id: Date.now(),
|
||||
content: newMessage,
|
||||
isUser: true,
|
||||
timestamp: new Date().toLocaleTimeString()
|
||||
}];
|
||||
|
||||
// Simulate bot response
|
||||
setTimeout(() => {
|
||||
messages = [...messages, {
|
||||
id: Date.now() + 1,
|
||||
content: `Translated to ${selectedLanguage}: [${newMessage}]`,
|
||||
isUser: false,
|
||||
timestamp: new Date().toLocaleTimeString()
|
||||
}];
|
||||
}, 1000);
|
||||
|
||||
newMessage = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full overflow-scroll bg-base-200">
|
||||
<!-- Language Selector -->
|
||||
<div class="flex justify-end p-4 bg-base-100 shadow-sm">
|
||||
<select
|
||||
bind:value={selectedLanguage}
|
||||
class="select select-primary select-sm w-full max-w-xs"
|
||||
>
|
||||
{#each languages as lang}
|
||||
<option value={lang.value}>{lang.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Chat Messages -->
|
||||
<div class="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
{#each messages as message}
|
||||
<div class="chat {message.isUser ? 'chat-end' : 'chat-start'}">
|
||||
<div class="chat-bubble {message.isUser ? 'chat-bubble-primary' : 'chat-bubble-secondary'}">
|
||||
{message.content}
|
||||
<div class="text-xs opacity-50 mt-1">{message.timestamp}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Message Input -->
|
||||
<div class="p-4 bg-base-100 border-t">
|
||||
<form on:submit|preventDefault={addMessage} class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={newMessage}
|
||||
placeholder="Type a message..."
|
||||
class="input input-bordered flex-1"
|
||||
/>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Send
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
113
frontend/src/routes/(authed)/home/dinner/+page.svelte
Normal file
113
frontend/src/routes/(authed)/home/dinner/+page.svelte
Normal file
@ -0,0 +1,113 @@
|
||||
<script>
|
||||
let currentCity = "Lisbon"; // Dynamic location
|
||||
const expeditions = [
|
||||
{
|
||||
name: "Tram 28 Tascas Crawl",
|
||||
seats: 3,
|
||||
cuisine: "Petiscos",
|
||||
highlights: ["Sardines", "Green Wine", "Local Artists"],
|
||||
meeting: "Praça Luís de Camões, 7pm"
|
||||
},
|
||||
{
|
||||
name: "Alfama Family Feast",
|
||||
seats: 5,
|
||||
cuisine: "Seafood",
|
||||
highlights: ["Cataplana Stew", "Fado Music", "Viewpoint"],
|
||||
meeting: "Miradouro Santa Luzia, 7:30pm"
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="h-full overflow-scroll bg-base-100 font-[Inter]">
|
||||
<!-- Header -->
|
||||
|
||||
|
||||
<!-- Local Context Bar -->
|
||||
<div class="bg-primary/10 py-3 px-4 flex items-center gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="text-sm opacity-75">Currently Exploring</div>
|
||||
<div class="text-xl font-bold text-primary">{currentCity}</div>
|
||||
</div>
|
||||
<div class="text-4xl">🍴</div>
|
||||
</div>
|
||||
|
||||
<!-- Expedition Cards -->
|
||||
<main class="p-4 space-y-6">
|
||||
{#each expeditions as expedition}
|
||||
<div class="card bg-base-100 shadow-sm hover:shadow-md transition-shadow">
|
||||
<div class="card-body">
|
||||
<!-- Expedition Header -->
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h2 class="card-title text-lg">{expedition.name}</h2>
|
||||
<div class="badge badge-outline badge-sm mt-1">
|
||||
{expedition.cuisine}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-2xl">
|
||||
{#if expedition.seats > 3}
|
||||
🍽️
|
||||
{:else}
|
||||
🔥
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cultural Highlights -->
|
||||
<div class="my-4 space-y-2">
|
||||
{#each expedition.highlights as item}
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<div class="text-primary">✦</div>
|
||||
<span>{item}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Group Status -->
|
||||
<div class="flex items-center justify-between border-t pt-4">
|
||||
<div class="flex -space-x-3">
|
||||
{#each Array(expedition.seats) as _, i}
|
||||
<div class="avatar placeholder">
|
||||
<div class="w-8 h-8 bg-neutral text-neutral-content">
|
||||
{i === 0 ? '👤' : '?'}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm">
|
||||
Claim Seat ({expedition.seats} left)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Meeting Details -->
|
||||
<div class="mt-4 text-xs opacity-75 flex items-center gap-2">
|
||||
<span class="flex-1">📍 {expedition.meeting}</span>
|
||||
<span class="badge badge-ghost badge-sm">Local Guide: Maria</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</main>
|
||||
|
||||
<!-- Cultural Footprint -->
|
||||
<div class="p-4 bg-base-200 mt-8">
|
||||
<div class="max-w-md mx-auto text-center space-y-4">
|
||||
<div class="text-3xl">🌐</div>
|
||||
<h3 class="font-bold">Your Lisbon Journey</h3>
|
||||
<div class="flex justify-center gap-4 text-sm">
|
||||
<div class="space-y-1">
|
||||
<div class="text-primary font-bold">5</div>
|
||||
<div class="opacity-75">New Allies</div>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<div class="text-primary font-bold">9</div>
|
||||
<div class="opacity-75">Local Dishes</div>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<div class="text-primary font-bold">3</div>
|
||||
<div class="opacity-75">Neighborhoods</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
220
frontend/src/routes/(authed)/home/plan/+page.svelte
Normal file
220
frontend/src/routes/(authed)/home/plan/+page.svelte
Normal file
@ -0,0 +1,220 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
let currentTab = 'pack';
|
||||
let xp = 420;
|
||||
let streak = 8;
|
||||
let packedItems: any = [];
|
||||
let newItem = '';
|
||||
let destinations: any = [];
|
||||
let newDestination = '';
|
||||
let expenses: any = [];
|
||||
let newExpense = '';
|
||||
|
||||
// Duolingo-style achievement system
|
||||
const achievements = [
|
||||
{ name: 'Packing Pro', earned: true },
|
||||
{ name: 'Globetrotter', earned: false },
|
||||
{ name: 'Budget Master', earned: false }
|
||||
];
|
||||
|
||||
function addItem() {
|
||||
if (newItem) {
|
||||
packedItems = [...packedItems, {
|
||||
name: newItem,
|
||||
packed: false,
|
||||
xp: 10,
|
||||
category: ['🧳 Clothing', '📱 Tech', '🧼 Toiletry'][Math.floor(Math.random() * 3)]
|
||||
}];
|
||||
newItem = '';
|
||||
}
|
||||
}
|
||||
|
||||
function togglePacked(item: any) {
|
||||
packedItems = packedItems.map((i: { packed: any; }) =>
|
||||
i === item ? {...i, packed: !i.packed} : i
|
||||
);
|
||||
if (!item.packed) xp += item.xp;
|
||||
}
|
||||
|
||||
function addDestination() {
|
||||
destinations = [...destinations, {
|
||||
name: newDestination,
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
emoji: '🌍'
|
||||
}];
|
||||
newDestination = '';
|
||||
}
|
||||
|
||||
function addExpense() {
|
||||
expenses = [...expenses, {
|
||||
description: newExpense,
|
||||
amount: Math.floor(Math.random() * 100),
|
||||
paidBy: 'You',
|
||||
splitWith: ['Travel Buddy 1', 'Travel Buddy 2']
|
||||
}];
|
||||
newExpense = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.9.2/dist/full.css" rel="stylesheet">
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-base-200 p-8 font-[Poppins]">
|
||||
<!-- XP & Streak Header -->
|
||||
<div class="flex justify-between items-center mb-8">
|
||||
<div class="stats shadow bg-primary text-primary-content">
|
||||
<div class="stat">
|
||||
<div class="stat-title">Travel XP</div>
|
||||
<div class="stat-value">{xp}</div>
|
||||
<div class="stat-desc">Level {Math.floor(xp/100)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="indicator">
|
||||
<span class="indicator-item badge badge-secondary">{streak}🔥</span>
|
||||
<div class="grid w-32 h-20 bg-base-100 place-items-center rounded-box">
|
||||
Streak!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Tabs -->
|
||||
<div class="tabs tabs-boxed bg-accent mb-8">
|
||||
<button
|
||||
class:tab-active={currentTab === 'pack'}
|
||||
on:click={() => currentTab = 'pack'}
|
||||
class="tab text-lg"
|
||||
>
|
||||
🎒 Pack
|
||||
</button>
|
||||
<button
|
||||
class:tab-active={currentTab === 'plan'}
|
||||
on:click={() => currentTab = 'plan'}
|
||||
class="tab text-lg"
|
||||
>
|
||||
🗺 Plan
|
||||
</button>
|
||||
<button
|
||||
class:tab-active={currentTab === 'split'}
|
||||
on:click={() => currentTab = 'split'}
|
||||
class="tab text-lg"
|
||||
>
|
||||
💸 Split
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Packing List Section -->
|
||||
{#if currentTab === 'pack'}
|
||||
<div class="grid gap-4">
|
||||
<div class="join w-full">
|
||||
<input
|
||||
bind:value={newItem}
|
||||
class="join-item input input-bordered w-full"
|
||||
placeholder="Add item (e.g. passport)"
|
||||
/>
|
||||
<button on:click={addItem} class="join-item btn btn-primary">
|
||||
<span class="text-2xl">+</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#each packedItems as item (item.name)}
|
||||
<div transition:fade class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body p-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={item.packed}
|
||||
on:change={() => togglePacked(item)}
|
||||
class="checkbox checkbox-primary checkbox-lg"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-xl font-bold">{item.name}</h3>
|
||||
<div class="badge badge-outline">{item.category}</div>
|
||||
</div>
|
||||
<div class="text-success">+{item.xp}XP</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if currentTab === 'plan'}
|
||||
<!-- Trip Planning Section -->
|
||||
<div class="grid gap-4">
|
||||
<div class="join w-full">
|
||||
<input
|
||||
bind:value={newDestination}
|
||||
class="join-item input input-bordered w-full"
|
||||
placeholder="Add destination (e.g. Paris)"
|
||||
/>
|
||||
<button on:click={addDestination} class="join-item btn btn-secondary">
|
||||
<span class="text-2xl">+</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="carousel rounded-box gap-4">
|
||||
{#each destinations as destination (destination.name)}
|
||||
<div class="carousel-item">
|
||||
<div class="card bg-base-100 shadow-xl w-64">
|
||||
<div class="card-body items-center text-center">
|
||||
<div class="text-6xl mb-2">{destination.emoji}</div>
|
||||
<h2 class="card-title">{destination.name}</h2>
|
||||
<p>{destination.date}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Expense Splitting Section -->
|
||||
<div class="grid gap-4">
|
||||
<div class="join w-full">
|
||||
<input
|
||||
bind:value={newExpense}
|
||||
class="join-item input input-bordered w-full"
|
||||
placeholder="Add expense (e.g. Hotel)"
|
||||
/>
|
||||
<button on:click={addExpense} class="join-item btn btn-accent">
|
||||
<span class="text-2xl">+</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#each expenses as expense (expense.description)}
|
||||
<div class="collapse collapse-arrow bg-base-100">
|
||||
<input type="radio" name="expenses" />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
💰 {expense.description} - ${expense.amount}
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<p>Paid by: {expense.paidBy}</p>
|
||||
<div class="divider"></div>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
{#each expense.splitWith as person}
|
||||
<div class="badge badge-lg badge-outline">
|
||||
👤 {person}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Achievements Floating Button -->
|
||||
<div class="fixed bottom-4 right-4">
|
||||
<div class="dropdown dropdown-top dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-circle btn-primary text-2xl">
|
||||
🏆
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-64">
|
||||
{#each achievements as achievement}
|
||||
<li class={achievement.earned ? 'text-success' : 'text-base-content/50'}>
|
||||
<a>★ {achievement.name}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user