mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-08-05 05:05:17 +02:00
commit
6b97b3a7f3
29 changed files with 2480 additions and 56 deletions
|
@ -19,8 +19,13 @@ RUN npm run build
|
|||
# Expose the port that the app is listening on
|
||||
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
|
||||
USER node:node
|
||||
|
||||
# This is the command that will be run inside the image when you tell Docker to start the container
|
||||
CMD ["node", "build/index.js"]
|
||||
# get permission to run startup script
|
||||
|
||||
# Run startup.sh instead of the default command
|
||||
CMD ["./startup.sh"]
|
||||
|
|
|
@ -3,9 +3,15 @@ services:
|
|||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
# depends_on:
|
||||
# - db
|
||||
# db:
|
||||
# image: postgres
|
||||
# environment:
|
||||
# POSTGRES_PASSWORD: example
|
||||
environment:
|
||||
- DATABASE_URL=postgres://adventurelog:PO24VjITwGgk@db:5432/adventurelog
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: postgres
|
||||
environment:
|
||||
POSTGRES_USER: adventurelog
|
||||
POSTGRES_PASSWORD: PO24VjITwGgk
|
||||
POSTGRES_DB: adventurelog
|
||||
ports:
|
||||
- "5432:5432"
|
15
drizzle.config.ts
Normal file
15
drizzle.config.ts
Normal 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;
|
5
migrations/0000_fancy_starjammers.sql
Normal file
5
migrations/0000_fancy_starjammers.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
CREATE TABLE IF NOT EXISTS "featuredAdventures" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"location" text
|
||||
);
|
6
migrations/0001_nostalgic_skreet.sql
Normal file
6
migrations/0001_nostalgic_skreet.sql
Normal 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
|
||||
);
|
1
migrations/0002_dusty_captain_midlands.sql
Normal file
1
migrations/0002_dusty_captain_midlands.sql
Normal file
|
@ -0,0 +1 @@
|
|||
ALTER TABLE "sharedAdventures" ALTER COLUMN "id" SET DATA TYPE text;
|
4
migrations/0003_clammy_goblin_queen.sql
Normal file
4
migrations/0003_clammy_goblin_queen.sql
Normal 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";
|
43
migrations/meta/0000_snapshot.json
Normal file
43
migrations/meta/0000_snapshot.json
Normal 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": {}
|
||||
}
|
||||
}
|
77
migrations/meta/0001_snapshot.json
Normal file
77
migrations/meta/0001_snapshot.json
Normal 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": {}
|
||||
}
|
||||
}
|
77
migrations/meta/0002_snapshot.json
Normal file
77
migrations/meta/0002_snapshot.json
Normal 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": {}
|
||||
}
|
||||
}
|
65
migrations/meta/0003_snapshot.json
Normal file
65
migrations/meta/0003_snapshot.json
Normal 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": {}
|
||||
}
|
||||
}
|
34
migrations/meta/_journal.json
Normal file
34
migrations/meta/_journal.json
Normal 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
1845
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
@ -1,32 +1,42 @@
|
|||
{
|
||||
"name": "adventurelog",
|
||||
"version": "0.0.1",
|
||||
"description": "Embark, Explore, Remember. 🌍",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"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": {
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/adapter-node": "^5.0.1",
|
||||
"@sveltejs/adapter-vercel": "^5.2.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@tailwindcss/typography": "^0.5.12",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"daisyui": "^4.9.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"drizzle-kit": "^0.20.14",
|
||||
"pg": "^8.11.4",
|
||||
"postcss": "^8.4.38",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3",
|
||||
"@sveltejs/adapter-vercel": "^5.2.0"
|
||||
"vite": "^5.0.3"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "^0.30.6",
|
||||
"postgres": "^3.4.4"
|
||||
}
|
||||
}
|
||||
|
|
81
sampleData/parks.sql
Normal file
81
sampleData/parks.sql
Normal 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');
|
24
src/lib/components/FeaturedAdventureCard.svelte
Normal file
24
src/lib/components/FeaturedAdventureCard.svelte
Normal 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>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getNumberOfAdventures } from "../../services/adventureService";
|
||||
import { visitCount } from '$lib/utils/stores/visitCountStore';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
async function goHome() {
|
||||
|
@ -8,16 +8,37 @@
|
|||
async function goToLog() {
|
||||
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>
|
||||
<div class="navbar bg-base-100 flex flex-col md:flex-row">
|
||||
<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" 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 class="navbar-center flex justify-center md:justify-center">
|
||||
<a class="btn btn-ghost text-xl" href="/">AdventureLog 🗺️</a>
|
||||
</div>
|
||||
<div class="navbar-end flex justify-around md:justify-end">
|
||||
<p>Adventures: {getNumberOfAdventures()} </p>
|
||||
<div class="navbar-end flex justify-around md:justify-end mr-4">
|
||||
<p>Adventures: {count} </p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
8
src/lib/db/db.server.ts
Normal file
8
src/lib/db/db.server.ts
Normal 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
12
src/lib/db/schema.ts
Normal 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(),
|
||||
})
|
|
@ -1 +1,11 @@
|
|||
// 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;
|
||||
}
|
4
src/lib/utils/stores/visitCountStore.ts
Normal file
4
src/lib/utils/stores/visitCountStore.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { onMount } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export const visitCount = writable(0);
|
11
src/routes/api/share/+server.ts
Normal file
11
src/routes/api/share/+server.ts
Normal 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}));
|
||||
}
|
11
src/routes/featured/+page.server.ts
Normal file
11
src/routes/featured/+page.server.ts
Normal 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[]
|
||||
};
|
||||
})
|
31
src/routes/featured/+page.svelte
Normal file
31
src/routes/featured/+page.svelte
Normal 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>
|
|
@ -10,6 +10,7 @@
|
|||
import SucessToast from "$lib/components/SucessToast.svelte";
|
||||
import mapDrawing from "$lib/assets/adventure_map.svg"
|
||||
import EditModal from "$lib/components/EditModal.svelte";
|
||||
import { generateRandomString } from "$lib";
|
||||
|
||||
let newName = '';
|
||||
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() {
|
||||
editId = NaN;
|
||||
editName = '';
|
||||
|
@ -94,6 +117,12 @@
|
|||
|
||||
</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">
|
||||
<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" />
|
||||
|
@ -101,6 +130,13 @@
|
|||
<input class="btn btn-primary" type="submit" value="Add Adventure">
|
||||
</form>
|
||||
</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}
|
||||
<SucessToast action={toastAction} />
|
||||
|
@ -124,12 +160,15 @@
|
|||
{/if}
|
||||
|
||||
{#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(); }}>
|
||||
<img src={exportFile} class="inline-block -mt-1" alt="Logo" /> Save as File
|
||||
</button>
|
||||
<button class="btn btn-neutral" on:click={deleteData}>
|
||||
<img src={deleteIcon} class="inline-block -mt-1" alt="Logo" /> Delete Data
|
||||
</button>
|
||||
<button class="btn btn-neutral" on:click={shareLink}>
|
||||
<img src={deleteIcon} class="inline-block -mt-1" alt="Logo" /> Share as Link
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
14
src/routes/shared/[key]/+page.server.ts
Normal file
14
src/routes/shared/[key]/+page.server.ts
Normal 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
|
||||
};
|
||||
};
|
8
src/routes/shared/[key]/+page.svelte
Normal file
8
src/routes/shared/[key]/+page.svelte
Normal 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>
|
|
@ -2,9 +2,12 @@ import type { Adventure } from '$lib/utils/types';
|
|||
|
||||
let adventures: Adventure[] = [];
|
||||
|
||||
import { visitCount } from '$lib/utils/stores/visitCountStore';
|
||||
|
||||
// Check if localStorage is available (browser environment)
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
|
||||
// Load adventures from localStorage on startup (only in the browser)
|
||||
if (isBrowser) {
|
||||
const storedAdventures = localStorage.getItem('adventures');
|
||||
|
@ -26,8 +29,10 @@ export function addAdventure(adventure: Adventure) {
|
|||
adventures = [...adventures, adventure];
|
||||
if (isBrowser) {
|
||||
localStorage.setItem('adventures', JSON.stringify(adventures));
|
||||
visitCount.update((n) => n + 1);
|
||||
}
|
||||
console.log(adventures);
|
||||
|
||||
}
|
||||
|
||||
export function getAdventures(): Adventure[] {
|
||||
|
@ -38,7 +43,9 @@ export function removeAdventure(event: { detail: number; }) {
|
|||
adventures = adventures.filter(adventure => adventure.id !== event.detail);
|
||||
if (isBrowser) {
|
||||
localStorage.setItem('adventures', JSON.stringify(adventures));
|
||||
visitCount.update((n) => n - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function saveEdit(adventure:Adventure) {
|
||||
|
@ -61,15 +68,13 @@ export function saveEdit(adventure:Adventure) {
|
|||
}
|
||||
}
|
||||
|
||||
export function getNumberOfAdventures() {
|
||||
return adventures.length;
|
||||
}
|
||||
|
||||
export function clearAdventures() {
|
||||
adventures = [];
|
||||
if (isBrowser) {
|
||||
localStorage.setItem('adventures', JSON.stringify(adventures));
|
||||
visitCount.set(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
24
startup.sh
Normal file
24
startup.sh
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue