1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-05 13:15:18 +02:00

Merge pull request #9 from seanmorley15/development

Server Side Logic
This commit is contained in:
Sean Morley 2024-04-02 15:36:51 -04:00 committed by GitHub
commit 6b97b3a7f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 2480 additions and 56 deletions

View file

@ -19,8 +19,13 @@ RUN npm run build
# Expose the port that the app is listening on # Expose the port that the app is listening on
EXPOSE 3000 EXPOSE 3000
RUN chmod +x ./startup.sh
# The USER instruction sets the user name to use as the default user for the remainder of the current stage # The USER instruction sets the user name to use as the default user for the remainder of the current stage
USER node:node USER node:node
# This is the command that will be run inside the image when you tell Docker to start the container # get permission to run startup script
CMD ["node", "build/index.js"]
# Run startup.sh instead of the default command
CMD ["./startup.sh"]

View file

@ -3,9 +3,15 @@ services:
build: . build: .
ports: ports:
- "3000:3000" - "3000:3000"
# depends_on: environment:
# - db - DATABASE_URL=postgres://adventurelog:PO24VjITwGgk@db:5432/adventurelog
# db: depends_on:
# image: postgres - db
# environment: db:
# POSTGRES_PASSWORD: example image: postgres
environment:
POSTGRES_USER: adventurelog
POSTGRES_PASSWORD: PO24VjITwGgk
POSTGRES_DB: adventurelog
ports:
- "5432:5432"

15
drizzle.config.ts Normal file
View file

@ -0,0 +1,15 @@
import type { Config } from 'drizzle-kit';
import * as dotenv from 'dotenv';
dotenv.config();
const { DATABASE_URL } = process.env;
if (!DATABASE_URL) {
throw new Error('No url');
}
export default {
schema: './src/lib/db/schema.ts',
out: './migrations',
driver: 'pg',
dbCredentials: {
connectionString: DATABASE_URL
}
} satisfies Config;

View file

@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS "featuredAdventures" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"location" text
);

View file

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS "sharedAdventures" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"location" text,
"date" text
);

View file

@ -0,0 +1 @@
ALTER TABLE "sharedAdventures" ALTER COLUMN "id" SET DATA TYPE text;

View file

@ -0,0 +1,4 @@
ALTER TABLE "sharedAdventures" ADD COLUMN "data" json NOT NULL;--> statement-breakpoint
ALTER TABLE "sharedAdventures" DROP COLUMN IF EXISTS "name";--> statement-breakpoint
ALTER TABLE "sharedAdventures" DROP COLUMN IF EXISTS "location";--> statement-breakpoint
ALTER TABLE "sharedAdventures" DROP COLUMN IF EXISTS "date";

View file

@ -0,0 +1,43 @@
{
"id": "1639b320-88dd-4af5-ae34-092ab26b4b85",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
"tables": {
"featuredAdventures": {
"name": "featuredAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -0,0 +1,77 @@
{
"id": "183ebfda-8d83-4256-8715-169d36867deb",
"prevId": "1639b320-88dd-4af5-ae34-092ab26b4b85",
"version": "5",
"dialect": "pg",
"tables": {
"featuredAdventures": {
"name": "featuredAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"sharedAdventures": {
"name": "sharedAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false
},
"date": {
"name": "date",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -0,0 +1,77 @@
{
"id": "808d6d31-5ef6-4b22-97f0-cfc73193eb5e",
"prevId": "183ebfda-8d83-4256-8715-169d36867deb",
"version": "5",
"dialect": "pg",
"tables": {
"featuredAdventures": {
"name": "featuredAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"sharedAdventures": {
"name": "sharedAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false
},
"date": {
"name": "date",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -0,0 +1,65 @@
{
"id": "45d98527-f0a9-44fc-9658-d3c461afed95",
"prevId": "808d6d31-5ef6-4b22-97f0-cfc73193eb5e",
"version": "5",
"dialect": "pg",
"tables": {
"featuredAdventures": {
"name": "featuredAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"sharedAdventures": {
"name": "sharedAdventures",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"data": {
"name": "data",
"type": "json",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -0,0 +1,34 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1712061709595,
"tag": "0000_fancy_starjammers",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1712083579596,
"tag": "0001_nostalgic_skreet",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1712083756923,
"tag": "0002_dusty_captain_midlands",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1712083977580,
"tag": "0003_clammy_goblin_queen",
"breakpoints": true
}
]
}

1845
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,32 +1,42 @@
{ {
"name": "adventurelog", "name": "adventurelog",
"version": "0.0.1", "version": "0.0.1",
"description": "Embark, Explore, Remember. 🌍",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"studio": "drizzle-kit studio --config drizzle.config.ts",
"generate": "drizzle-kit generate:pg --config drizzle.config.ts",
"migrate": "drizzle-kit push:pg --config drizzle.config.ts",
"seed": "node seed.js"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.0.1", "@sveltejs/adapter-node": "^5.0.1",
"@sveltejs/adapter-vercel": "^5.2.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/typography": "^0.5.12", "@tailwindcss/typography": "^0.5.12",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"daisyui": "^4.9.0", "daisyui": "^4.9.0",
"dotenv": "^16.4.5",
"drizzle-kit": "^0.20.14",
"pg": "^8.11.4",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"svelte": "^4.2.7", "svelte": "^4.2.7",
"svelte-check": "^3.6.0", "svelte-check": "^3.6.0",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.3",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^5.0.3", "vite": "^5.0.3"
"@sveltejs/adapter-vercel": "^5.2.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"drizzle-orm": "^0.30.6",
"postgres": "^3.4.4"
} }
} }

81
sampleData/parks.sql Normal file
View file

@ -0,0 +1,81 @@
INSERT INTO "featuredAdventures" (id, name, location) VALUES
(20, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(21, 'Yosemite National Park', 'California, USA'),
(22, 'Banff National Park', 'Alberta, Canada'),
(23, 'Kruger National Park', 'Limpopo, South Africa'),
(24, 'Grand Canyon National Park', 'Arizona, USA'),
(25, 'Great Smoky Mountains National Park', 'North Carolina, Tennessee, USA'),
(26, 'Zion National Park', 'Utah, USA'),
(27, 'Glacier National Park', 'Montana, USA'),
(28, 'Rocky Mountain National Park', 'Colorado, USA'),
(29, 'Everglades National Park', 'Florida, USA'),
(30, 'Arches National Park', 'Utah, USA'),
(31, 'Acadia National Park', 'Maine, USA'),
(32, 'Sequoia National Park', 'California, USA'),
(33, 'Joshua Tree National Park', 'California, USA'),
(34, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(35, 'Bryce Canyon National Park', 'Utah, USA'),
(36, 'Grand Teton National Park', 'Wyoming, USA'),
(37, 'Denali National Park', 'Alaska, USA'),
(38, 'Olympic National Park', 'Washington, USA'),
(39, 'Canyonlands National Park', 'Utah, USA'),
(40, 'Death Valley National Park', 'California, Nevada, USA'),
(41, 'Redwood National and State Parks', 'California, USA'),
(42, 'Waterton Lakes National Park', 'Alberta, Canada'),
(43, 'Mesa Verde National Park', 'Colorado, USA'),
(44, 'Petrified Forest National Park', 'Arizona, USA'),
(45, 'Capitol Reef National Park', 'Utah, USA'),
(46, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(47, 'Yosemite National Park', 'California, USA'),
(48, 'Banff National Park', 'Alberta, Canada'),
(49, 'Kruger National Park', 'Limpopo, South Africa'),
(50, 'Grand Canyon National Park', 'Arizona, USA'),
(51, 'Great Smoky Mountains National Park', 'North Carolina, Tennessee, USA'),
(52, 'Zion National Park', 'Utah, USA'),
(53, 'Glacier National Park', 'Montana, USA'),
(54, 'Rocky Mountain National Park', 'Colorado, USA'),
(55, 'Everglades National Park', 'Florida, USA'),
(56, 'Arches National Park', 'Utah, USA'),
(57, 'Acadia National Park', 'Maine, USA'),
(58, 'Sequoia National Park', 'California, USA'),
(59, 'Joshua Tree National Park', 'California, USA'),
(60, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(61, 'Bryce Canyon National Park', 'Utah, USA'),
(62, 'Grand Teton National Park', 'Wyoming, USA'),
(63, 'Denali National Park', 'Alaska, USA'),
(64, 'Olympic National Park', 'Washington, USA'),
(65, 'Canyonlands National Park', 'Utah, USA'),
(66, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(67, 'Yosemite National Park', 'California, USA'),
(68, 'Banff National Park', 'Alberta, Canada'),
(69, 'Kruger National Park', 'Limpopo, South Africa'),
(70, 'Grand Canyon National Park', 'Arizona, USA'),
(71, 'Great Smoky Mountains National Park', 'North Carolina, Tennessee, USA'),
(72, 'Zion National Park', 'Utah, USA'),
(73, 'Glacier National Park', 'Montana, USA'),
(74, 'Rocky Mountain National Park', 'Colorado, USA'),
(75, 'Everglades National Park', 'Florida, USA'),
(76, 'Arches National Park', 'Utah, USA'),
(77, 'Acadia National Park', 'Maine, USA'),
(78, 'Sequoia National Park', 'California, USA'),
(79, 'Joshua Tree National Park', 'California, USA'),
(80, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(81, 'Bryce Canyon National Park', 'Utah, USA'),
(82, 'Grand Teton National Park', 'Wyoming, USA'),
(83, 'Denali National Park', 'Alaska, USA'),
(84, 'Olympic National Park', 'Washington, USA'),
(85, 'Canyonlands National Park', 'Utah, USA'),
(86, 'Yellowstone National Park', 'Wyoming, Montana, Idaho, USA'),
(87, 'Yosemite National Park', 'California, USA'),
(88, 'Banff National Park', 'Alberta, Canada'),
(89, 'Kruger National Park', 'Limpopo, South Africa'),
(90, 'Grand Canyon National Park', 'Arizona, USA'),
(91, 'Great Smoky Mountains National Park', 'North Carolina, Tennessee, USA'),
(92, 'Zion National Park', 'Utah, USA'),
(93, 'Glacier National Park', 'Montana, USA'),
(94, 'Rocky Mountain National Park', 'Colorado, USA'),
(95, 'Everglades National Park', 'Florida, USA'),
(96, 'Arches National Park', 'Utah, USA'),
(97, 'Acadia National Park', 'Maine, USA'),
(98, 'Sequoia National Park', 'California, USA'),
(99, 'Joshua Tree National Park', 'California, USA');

View file

@ -0,0 +1,24 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import locationDot from "$lib/assets/locationDot.svg";
import calendar from "$lib/assets/calendar.svg";
const dispatch = createEventDispatcher();
export let name:String;
export let location:String;
function add() {
dispatch('add', {name, location});
}
</script>
<div class="card min-w-max lg:w-96 md:w-80 sm:w-60 xs:w-40 bg-neutral shadow-xl overflow-hidden">
<div class="card-body">
<h2 class="card-title overflow-ellipsis">{name}</h2>
<p><img src={locationDot} class="inline-block -mt-1 mr-1" alt="Logo" />{location}</p>
<div class="card-actions justify-end">
<button class="btn btn-primary" on:click={add}>Add</button>
</div>
</div>
</div>

View file

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { getNumberOfAdventures } from "../../services/adventureService"; import { visitCount } from '$lib/utils/stores/visitCountStore';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
async function goHome() { async function goHome() {
@ -8,16 +8,37 @@
async function goToLog() { async function goToLog() {
goto('/log'); goto('/log');
} }
async function goToFeatured() {
goto('/featured');
}
let count = 0;
visitCount.subscribe((value) => {
count = value;
});
// Set the visit count to the number of adventures stored in local storage
const isBrowser = typeof window !== 'undefined';
if (isBrowser) {
const storedAdventures = localStorage.getItem('adventures');
if (storedAdventures) {
let parsed = JSON.parse(storedAdventures);
visitCount.set (parsed.length);
}
}
</script> </script>
<div class="navbar bg-base-100 flex flex-col md:flex-row"> <div class="navbar bg-base-100 flex flex-col md:flex-row">
<div class="navbar-start flex justify-around md:justify-start"> <div class="navbar-start flex justify-around md:justify-start">
<button class="btn btn-primary my-2 md:my-0 md:mr-4 md:ml-2" on:click={goHome}>Home</button> <button class="btn btn-primary my-2 md:my-0 md:mr-4 md:ml-2" on:click={goHome}>Home</button>
<button class="btn btn-primary my-2 md:my-0" on:click={goToLog}>My Log</button> <button class="btn btn-primary my-2 md:my-0 md:mr-4 md:ml-2" on:click={goToLog}>My Log</button>
<button class="btn btn-primary my-2 md:my-0" on:click={goToFeatured}>Featured</button>
</div> </div>
<div class="navbar-center flex justify-center md:justify-center"> <div class="navbar-center flex justify-center md:justify-center">
<a class="btn btn-ghost text-xl" href="/">AdventureLog 🗺️</a> <a class="btn btn-ghost text-xl" href="/">AdventureLog 🗺️</a>
</div> </div>
<div class="navbar-end flex justify-around md:justify-end"> <div class="navbar-end flex justify-around md:justify-end mr-4">
<p>Adventures: {getNumberOfAdventures()} </p> <p>Adventures: {count} </p>
</div> </div>
</div> </div>

8
src/lib/db/db.server.ts Normal file
View file

@ -0,0 +1,8 @@
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import dotenv from 'dotenv';
dotenv.config();
const { DATABASE_URL } = process.env;
const client = postgres(DATABASE_URL)
export const db = drizzle(client, {});

12
src/lib/db/schema.ts Normal file
View file

@ -0,0 +1,12 @@
import { pgTable,json,text,serial } from "drizzle-orm/pg-core";
export const featuredAdventures = pgTable("featuredAdventures",{
id:serial("id").primaryKey(),
name:text("name").notNull(),
location:text("location"),
})
export const sharedAdventures = pgTable("sharedAdventures",{
id:text("id").primaryKey(),
data:json("data").notNull(),
})

View file

@ -1 +1,11 @@
// place files you want to import through the `$lib` alias in this folder. // place files you want to import through the `$lib` alias in this folder.
export function generateRandomString() {
let randomString = '';
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i = 0; i < 10; i++) {
const randomIndex = Math.floor(Math.random() * digits.length);
randomString += digits[randomIndex];
}
return randomString;
}

View file

@ -0,0 +1,4 @@
import { onMount } from "svelte";
import { writable } from "svelte/store";
export const visitCount = writable(0);

View file

@ -0,0 +1,11 @@
import { db } from "$lib/db/db.server";
import { sharedAdventures } from "$lib/db/schema";
import type { Adventure } from "$lib/utils/types";
export async function POST({ request }: { request: Request }) {
const { key , data } = await request.json();
let adventure = data as Adventure;
console.log(adventure);
await db.insert(sharedAdventures).values({ id:key,data:adventure }).execute();
return new Response(JSON.stringify({key:key}));
}

View file

@ -0,0 +1,11 @@
import { db } from '$lib/db/db.server';
import { featuredAdventures } from '$lib/db/schema';
import type { Adventure } from '$lib/utils/types';
export const load = (async () => {
const result = await db.select().from(featuredAdventures).orderBy(featuredAdventures.id);
return {
result : result as Adventure[]
};
})

View file

@ -0,0 +1,31 @@
<script lang="ts">
export let data
console.log(data.result);
import FeaturedAdventureCard from '$lib/components/FeaturedAdventureCard.svelte';
import type { Adventure } from '$lib/utils/types.js';
import { addAdventure, getNextId } from '../../services/adventureService.js';
function add(event: CustomEvent<{name: string, location: string}>) {
console.log(event.detail);
let newAdventure:Adventure = {
id: getNextId(),
name: event.detail.name,
location: event.detail.location,
created: ""
}
addAdventure(newAdventure);
}
</script>
<div class="flex justify-center items-center w-full mt-4 mb-4">
<article class="prose">
<h1 class="text-center">Featured Adventure Locations</h1>
</article>
</div>
<div class="grid xl:grid-cols-3 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-4 mt-4 content-center auto-cols-auto ml-6 mr-6">
{#each data.result as adventure (adventure.id)}
<FeaturedAdventureCard on:add={add} name={adventure.name} location={adventure.location} />
{/each}
</div>

View file

@ -10,6 +10,7 @@
import SucessToast from "$lib/components/SucessToast.svelte"; import SucessToast from "$lib/components/SucessToast.svelte";
import mapDrawing from "$lib/assets/adventure_map.svg" import mapDrawing from "$lib/assets/adventure_map.svg"
import EditModal from "$lib/components/EditModal.svelte"; import EditModal from "$lib/components/EditModal.svelte";
import { generateRandomString } from "$lib";
let newName = ''; let newName = '';
let newLocation = ''; let newLocation = '';
@ -79,6 +80,28 @@
} }
} }
function shareLink() {
let key = generateRandomString()
let data = JSON.stringify(adventures)
fetch('/api/share', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ key, data }),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
let url = window.location.origin + '/shared/' + key
navigator.clipboard.writeText(url)
})
.catch((error) => {
console.error('Error:', error);
});
}
function handleClose() { function handleClose() {
editId = NaN; editId = NaN;
editName = ''; editName = '';
@ -94,6 +117,12 @@
</script> </script>
<div class="flex justify-center items-center w-full mt-4 mb-4">
<article class="prose">
<h2 class="text-center">Add new Location</h2>
</article>
</div>
<div class="flex flex-row items-center justify-center gap-4"> <div class="flex flex-row items-center justify-center gap-4">
<form on:submit={createNewAdventure} class="flex gap-2"> <form on:submit={createNewAdventure} class="flex gap-2">
<input type="text" bind:value={newName} placeholder="Adventure Name" class="input input-bordered w-full max-w-xs" /> <input type="text" bind:value={newName} placeholder="Adventure Name" class="input input-bordered w-full max-w-xs" />
@ -101,6 +130,13 @@
<input class="btn btn-primary" type="submit" value="Add Adventure"> <input class="btn btn-primary" type="submit" value="Add Adventure">
</form> </form>
</div> </div>
{#if adventures.length != 0}
<div class="flex justify-center items-center w-full mt-4 mb-4">
<article class="prose">
<h1 class="text-center">My Visited Adventure Locations</h1>
</article>
</div>
{/if}
{#if isShowingToast} {#if isShowingToast}
<SucessToast action={toastAction} /> <SucessToast action={toastAction} />
@ -124,12 +160,15 @@
{/if} {/if}
{#if adventures.length != 0} {#if adventures.length != 0}
<div class="flex flex-row items-center justify-center mt-16 gap-4"> <div class="flex flex-row items-center justify-center mt-16 gap-4 mb-4">
<button class="btn btn-neutral" on:click={async () => { window.location.href = exportData(); }}> <button class="btn btn-neutral" on:click={async () => { window.location.href = exportData(); }}>
<img src={exportFile} class="inline-block -mt-1" alt="Logo" /> Save as File <img src={exportFile} class="inline-block -mt-1" alt="Logo" /> Save as File
</button> </button>
<button class="btn btn-neutral" on:click={deleteData}> <button class="btn btn-neutral" on:click={deleteData}>
<img src={deleteIcon} class="inline-block -mt-1" alt="Logo" /> Delete Data <img src={deleteIcon} class="inline-block -mt-1" alt="Logo" /> Delete Data
</button> </button>
<button class="btn btn-neutral" on:click={shareLink}>
<img src={deleteIcon} class="inline-block -mt-1" alt="Logo" /> Share as Link
</button>
</div> </div>
{/if} {/if}

View file

@ -0,0 +1,14 @@
import { db } from "$lib/db/db.server";
import { sharedAdventures } from "$lib/db/schema";
import { eq } from "drizzle-orm";
import type { Adventure } from "$lib/utils/types";
export async function load({ params }) {
let key = params.key;
let result = await db.select().from(sharedAdventures).where(eq(sharedAdventures.id, key)).execute();
let adventure = result[0].data as Adventure;
console.log(adventure);
return {
result: adventure
};
};

View file

@ -0,0 +1,8 @@
<script lang="ts">
import type { Adventure } from '$lib/utils/types'
export let data;
let result = data.result;
</script>
<p>{result}</p>

View file

@ -2,9 +2,12 @@ import type { Adventure } from '$lib/utils/types';
let adventures: Adventure[] = []; let adventures: Adventure[] = [];
import { visitCount } from '$lib/utils/stores/visitCountStore';
// Check if localStorage is available (browser environment) // Check if localStorage is available (browser environment)
const isBrowser = typeof window !== 'undefined'; const isBrowser = typeof window !== 'undefined';
// Load adventures from localStorage on startup (only in the browser) // Load adventures from localStorage on startup (only in the browser)
if (isBrowser) { if (isBrowser) {
const storedAdventures = localStorage.getItem('adventures'); const storedAdventures = localStorage.getItem('adventures');
@ -26,8 +29,10 @@ export function addAdventure(adventure: Adventure) {
adventures = [...adventures, adventure]; adventures = [...adventures, adventure];
if (isBrowser) { if (isBrowser) {
localStorage.setItem('adventures', JSON.stringify(adventures)); localStorage.setItem('adventures', JSON.stringify(adventures));
visitCount.update((n) => n + 1);
} }
console.log(adventures); console.log(adventures);
} }
export function getAdventures(): Adventure[] { export function getAdventures(): Adventure[] {
@ -38,7 +43,9 @@ export function removeAdventure(event: { detail: number; }) {
adventures = adventures.filter(adventure => adventure.id !== event.detail); adventures = adventures.filter(adventure => adventure.id !== event.detail);
if (isBrowser) { if (isBrowser) {
localStorage.setItem('adventures', JSON.stringify(adventures)); localStorage.setItem('adventures', JSON.stringify(adventures));
visitCount.update((n) => n - 1);
} }
} }
export function saveEdit(adventure:Adventure) { export function saveEdit(adventure:Adventure) {
@ -61,15 +68,13 @@ export function saveEdit(adventure:Adventure) {
} }
} }
export function getNumberOfAdventures() {
return adventures.length;
}
export function clearAdventures() { export function clearAdventures() {
adventures = []; adventures = [];
if (isBrowser) { if (isBrowser) {
localStorage.setItem('adventures', JSON.stringify(adventures)); localStorage.setItem('adventures', JSON.stringify(adventures));
visitCount.set(0);
} }
} }

24
startup.sh Normal file
View file

@ -0,0 +1,24 @@
#!/bin/sh
# Function to check if the database is ready
wait_for_db() {
echo "Waiting for the database to start up..."
while ! nc -z db 5432; do
sleep 1
done
echo "Database is now available."
}
# Start your application here
# Example: node build/index.js
# Print message
echo "Starting AdventureLog"
# Wait for the database to start up
wait_for_db
# Run database migration
npm run migrate
# Start the application
node build/index.js