1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-03 12:15:19 +02:00

Merge pull request #67 from seanmorley15/development

Development
This commit is contained in:
Sean Morley 2024-05-15 06:26:13 -04:00 committed by GitHub
commit a0400bca8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 281 additions and 20 deletions

View file

@ -18,7 +18,7 @@ _**⚠️ AdventureLog is in early development and is not recommended for produc
**Note**: The `ORIGIN` variable is required for CSRF protection. It can be omitted if using a reverse proxy or other HTTPS service.
## About AdventureLog
## About AdventureLog
AdventureLog is a Svelte Kit application that utilizes a PostgreSQL database. Users can log the adventures they have experienced, as well as plan future ones. Key features include:
@ -29,7 +29,7 @@ AdventureLog is a Svelte Kit application that utilizes a PostgreSQL database. Us
AdventureLog aims to be your ultimate travel companion, helping you document your adventures and plan new ones effortlessly.
AdventureLog is liscensed under the GNU General Public License v3.0.
AdventureLog is licensed under the GNU General Public License v3.0.
## Roadmap 🛣️

View file

@ -0,0 +1,67 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import locationDot from "$lib/assets/locationDot.svg";
import calendar from "$lib/assets/calendar.svg";
import { goto } from "$app/navigation";
import { desc } from "drizzle-orm";
import type { Adventure, Trip } from "$lib/utils/types";
const dispatch = createEventDispatcher();
// export let type: String;
export let trip: Trip;
function remove() {
dispatch("remove", trip.id);
}
function edit() {}
function add() {
dispatch("add", trip);
}
function moreInfo() {
console.log(trip.id);
goto(`/trip/${trip.id}`);
}
function markVisited() {
console.log(trip.id);
dispatch("markVisited", trip);
}
</script>
<div
class="card min-w-max lg:w-96 md:w-80 sm:w-60 xs:w-40 bg-primary-content shadow-xl overflow-hidden text-base-content"
>
<div class="card-body">
<h2 class="card-title overflow-ellipsis">{trip.name}</h2>
{#if trip.description && trip.description !== ""}
<div class="inline-flex items-center">
<iconify-icon icon="mdi:map-marker" class="text-xl"></iconify-icon>
<p class="ml-.5">{trip.description}</p>
</div>
{/if}
{#if trip.startDate && trip.startDate !== ""}
<div class="inline-flex items-center">
<iconify-icon icon="mdi:calendar" class="text-xl"></iconify-icon>
<p class="ml-1">{trip.startDate}</p>
</div>
{/if}
{#if trip.endDate && trip.endDate !== ""}
<div class="inline-flex items-center">
<iconify-icon icon="mdi:calendar" class="text-xl"></iconify-icon>
<p class="ml-1">{trip.endDate}</p>
</div>
{/if}
<div class="card-actions justify-end">
<button class="btn btn-secondary" on:click={remove}
><iconify-icon icon="mdi:trash-can-outline" class="text-2xl"
></iconify-icon></button
>
<button class="btn btn-primary" on:click={() => goto(`/trip/${trip.id}`)}
><iconify-icon icon="mdi:launch" class="text-2xl"
></iconify-icon></button
>
</div>
</div>
</div>

View file

@ -7,6 +7,7 @@
import campingDrawing from "$lib/assets/camping.svg";
import AdventureOverlook from "$lib/assets/AdventureOverlook.webp";
import MapWithPins from "$lib/assets/MapWithPins.webp";
import { onMount } from "svelte";
async function navToLog() {
goto("/log");
@ -27,11 +28,29 @@
>
<div class="flex flex-col justify-center space-y-4">
<div class="space-y-2">
{#if data.user}
{#if data.user.first_name && data.user.first_name !== null}
<h1
class="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl/none bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pb-4"
>
{data.user.first_name.charAt(0).toUpperCase() +
data.user.first_name.slice(1)}, Discover the World's Most
Thrilling Adventures
</h1>
{:else}
<h1
class="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl/none bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pb-4"
>
Discover the World's Most Thrilling Adventures
</h1>
{/if}
{:else}
<h1
class="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl/none bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pb-4"
>
Discover the World's Most Thrilling Adventures
</h1>
{/if}
<p class="max-w-[600px] text-gray-500 md:text-xl dark:text-gray-400">
Discover and plan your next epic adventure with our cutting-edge
travel app. Explore breathtaking destinations, create custom

View file

@ -0,0 +1,51 @@
import { db } from "$lib/db/db.server";
import { adventureTable, userPlannedTrips } from "$lib/db/schema";
import type { Adventure, Trip } from "$lib/utils/types";
import { json, type RequestEvent, type RequestHandler } from "@sveltejs/kit";
import { and, eq } from "drizzle-orm";
/**
* Handles the GET request for retrieving a trip.
* @param {Request} request - The request object.
* @param {Response} response - The response object.
* @returns {Promise<void>} - A promise that resolves when the request is handled.
*/
export const GET: RequestHandler = async ({ url, locals }) => {
const id = url.searchParams.get("id");
const user = locals.user;
if (!user) {
return json({ error: "Unauthorized" }, { status: 401 });
}
if (!id) {
return json({ error: "Missing adventure ID" }, { status: 400 });
}
const trip = await db
.select()
.from(userPlannedTrips)
.where(
and(
eq(userPlannedTrips.id, Number(id)), // Convert id to number
eq(userPlannedTrips.userId, user.id)
)
)
.limit(1)
.execute();
if (trip.length === 0) {
return json({ error: "Trip not found" }, { status: 404 });
}
JSON.stringify(
trip.map((r) => {
const adventure: Trip = r as Trip;
})
);
// console.log("GET /api/adventure?id=", id);
// console.log("User:", user);
return json({ trip }, { status: 200 });
};

View file

@ -1,7 +1,7 @@
import { db } from "$lib/db/db.server";
import { userPlannedTrips } from "$lib/db/schema";
import { error, type RequestEvent } from "@sveltejs/kit";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
export async function POST(event: RequestEvent): Promise<Response> {
if (!event.locals.user) {
@ -92,3 +92,38 @@ export async function GET(event: RequestEvent): Promise<Response> {
},
});
}
export async function DELETE(event: RequestEvent): Promise<Response> {
if (!event.locals.user) {
return new Response(JSON.stringify({ error: "No user found" }), {
status: 401,
headers: {
"Content-Type": "application/json",
},
});
}
const body = await event.request.json();
if (!body.id) {
return error(400, {
message: "No trip id provided",
});
}
let res = await db
.delete(userPlannedTrips)
.where(
and(
eq(userPlannedTrips.userId, event.locals.user.id),
eq(userPlannedTrips.id, body.id)
)
)
.execute();
return new Response(JSON.stringify({ message: "Trip deleted" }), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
}

View file

@ -13,6 +13,7 @@
import SucessToast from "$lib/components/SucessToast.svelte";
import mapDrawing from "$lib/assets/adventure_map.svg";
import CreateNewTripPlan from "$lib/components/CreateNewTripPlan.svelte";
import TripCard from "$lib/components/TripCard.svelte";
export let data;
let adventuresPlans: Adventure[] = [];
@ -154,6 +155,34 @@
showToast("Failed to get trips");
});
}
async function removeTrip(event: { detail: number }) {
let initialLength: number = tripPlans.length;
const response = await fetch("/api/trips", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: event.detail }),
})
.then((response) => response.json())
.then((data) => {
console.log("Success:", data);
let theTrip = tripPlans.find((trip) => trip.id === event.detail);
if (theTrip) {
let newArray = tripPlans.filter((trip) => trip.id !== event.detail);
if (newArray.length === initialLength - 1) {
tripPlans = newArray;
showToast("Trip removed successfully!");
} else {
showToast("Failed to remove trip");
}
}
})
.catch((error) => {
showToast("Failed to get trips");
});
}
</script>
{#if isShowingToast}
@ -247,19 +276,20 @@
</div>
{/if}
<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 tripPlans as trip (trip.id)}
<div class="flex justify-center items-center w-full mt-4 mb-4">
<article class="prose">
<h2>{trip.name}</h2>
<p>{trip.description}</p>
<p>
<strong>Start Date:</strong>
{trip.startDate} <strong>End Date:</strong>
{trip.endDate}
</p>
</article>
</div>
<TripCard {trip} on:remove={removeTrip} />
{/each}
</div>
{#if tripPlans.length == 0 && !isLoadingIdeas && !isLoadingTrips && !isShowingMoreFields && !isShowingNewTrip}
<div class="flex flex-col items-center justify-center mt-16">
<article class="prose mb-4"><h2>Add some trips!</h2></article>
<img src={mapDrawing} width="25%" alt="Logo" />
</div>
{/if}
<svelte:head>
<title>My Plans | AdventureLog</title>

View file

@ -0,0 +1,32 @@
import { redirect } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
import { db } from "$lib/db/db.server";
import { and, eq } from "drizzle-orm";
import { adventureTable, userPlannedTrips } from "$lib/db/schema";
export const load: PageServerLoad = (async (event) => {
if (!event.locals.user) {
return redirect(302, "/login");
}
let adventureUserId: any[] = await db
.select({ userId: userPlannedTrips.userId })
.from(userPlannedTrips)
.where(eq(userPlannedTrips.id, Number(event.params.id)))
.limit(1)
.execute();
console.log(adventureUserId);
if (
adventureUserId &&
adventureUserId[0]?.userId !== event.locals.user.id &&
adventureUserId !== null
) {
return redirect(302, "/log");
}
let trip = await event.fetch(`/api/trip?id=${event.params.id}`);
return { trip: await trip.json() };
}) satisfies PageServerLoad;

View file

@ -0,0 +1,27 @@
<script lang="ts">
import type { Adventure, Trip } from "$lib/utils/types";
import { onMount } from "svelte";
import type { PageData } from "./$types";
import { goto } from "$app/navigation";
export let data: PageData;
let trip: Trip | null = null;
onMount(() => {
if (data.trip.trip) {
trip = data.trip.trip[0];
} else {
goto("/404");
}
});
</script>
<main>
{#if trip}
<h1>{trip.name}</h1>
<p>{trip.description}</p>
<p>{trip.startDate}</p>
<p>{trip.endDate}</p>
{/if}
</main>