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

Merge pull request #53 from seanmorley15/development

Development
This commit is contained in:
Sean Morley 2024-05-04 11:59:04 -04:00 committed by GitHub
commit 89e4899e43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 747 additions and 67 deletions

View file

@ -20,6 +20,7 @@
dispatch("remove", adventure.id); dispatch("remove", adventure.id);
} }
function edit() { function edit() {
console.log(adventure.activityTypes);
dispatch("edit", adventure.id); dispatch("edit", adventure.id);
} }
function add() { function add() {
@ -49,6 +50,17 @@
<p class="ml-1">{adventure.date}</p> <p class="ml-1">{adventure.date}</p>
</div> </div>
{/if} {/if}
{#if adventure.activityTypes && adventure.activityTypes.length > 0}
<ul class="flex flex-wrap">
{#each adventure.activityTypes as activity}
<div
class="badge badge-primary mr-1 text-md font-semibold pb-2 pt-1 mb-1"
>
{activity}
</div>
{/each}
</ul>
{/if}
<div class="card-actions justify-end"> <div class="card-actions justify-end">
{#if type == "mylog"} {#if type == "mylog"}
<button class="btn btn-primary" on:click={moreInfo} <button class="btn btn-primary" on:click={moreInfo}
@ -64,6 +76,20 @@
></iconify-icon></button ></iconify-icon></button
> >
{/if} {/if}
{#if type == "planner"}
<button class="btn btn-primary" on:click={moreInfo}
><iconify-icon icon="mdi:launch" class="text-2xl"
></iconify-icon></button
>
<button class="btn btn-primary" on:click={edit}
><iconify-icon icon="mdi:file-document-edit" class="text-2xl"
></iconify-icon></button
>
<button class="btn btn-secondary" on:click={remove}
><iconify-icon icon="mdi:trash-can-outline" class="text-2xl"
></iconify-icon></button
>
{/if}
{#if type == "featured"} {#if type == "featured"}
<button class="btn btn-primary" on:click={add} <button class="btn btn-primary" on:click={add}
><iconify-icon icon="mdi:plus" class="text-2xl" ><iconify-icon icon="mdi:plus" class="text-2xl"

View file

@ -1,12 +1,14 @@
<script lang="ts"> <script lang="ts">
let newAdventure: Adventure; let newAdventure: Adventure;
export let type: string;
newAdventure = { newAdventure = {
id: -1, id: -1,
type: "mylog", type: type,
name: "", name: "",
location: "", location: "",
date: "", date: "",
activityTypes: [],
}; };
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
@ -23,6 +25,7 @@
}); });
function create() { function create() {
addActivityType();
dispatch("create", newAdventure); dispatch("create", newAdventure);
console.log(newAdventure); console.log(newAdventure);
} }
@ -36,6 +39,19 @@
close(); close();
} }
} }
let activityInput: string = "";
function addActivityType() {
if (activityInput.trim() !== "") {
const activities = activityInput
.split(" ")
.filter((activity) => activity.trim() !== "");
newAdventure.activityTypes = activities;
activityInput = "";
}
console.log(newAdventure.activityTypes);
}
</script> </script>
<dialog id="my_modal_1" class="modal"> <dialog id="my_modal_1" class="modal">
@ -85,8 +101,30 @@
class="input input-bordered w-full max-w-xs" class="input input-bordered w-full max-w-xs"
/> />
</div> </div>
<button class="btn btn-primary mr-4 mt-4" on:click={create} <div>
>Create</button <label for="date">Activity Types (Comma Seperated)</label>
<input
type="text"
id="activityTypes"
bind:value={activityInput}
class="input input-bordered w-full max-w-xs"
/>
</div>
<div>
<label for="rating">Rating</label>
<input
type="number"
min="0"
max="5"
id="rating"
bind:value={newAdventure.rating}
class="input input-bordered w-full max-w-xs"
/>
</div>
<button
type="submit"
class="btn btn-primary mr-4 mt-4"
on:click={create}>Create</button
> >
<!-- if there is a button in form, it will close the modal --> <!-- if there is a button in form, it will close the modal -->
<button class="btn mt-4" on:click={close}>Close</button> <button class="btn mt-4" on:click={close}>Close</button>

View file

@ -6,6 +6,8 @@
import { onMount } from "svelte"; import { onMount } from "svelte";
let modal: HTMLDialogElement; let modal: HTMLDialogElement;
console.log(adventureToEdit.id);
let originalName = adventureToEdit.name; let originalName = adventureToEdit.name;
onMount(() => { onMount(() => {
@ -13,9 +15,11 @@
if (modal) { if (modal) {
modal.showModal(); modal.showModal();
} }
activityInput = (adventureToEdit?.activityTypes || []).join(", ");
}); });
function submit() { function submit() {
addActivityType();
dispatch("submit", adventureToEdit); dispatch("submit", adventureToEdit);
console.log(adventureToEdit); console.log(adventureToEdit);
} }
@ -29,6 +33,19 @@
close(); close();
} }
} }
let activityInput: string = "";
function addActivityType() {
if (activityInput.trim() !== "") {
const activities = activityInput
.split(",")
.filter((activity) => activity.trim() !== "");
adventureToEdit.activityTypes = activities;
activityInput = "";
}
console.log(adventureToEdit.activityTypes);
}
</script> </script>
<dialog id="my_modal_1" class="modal"> <dialog id="my_modal_1" class="modal">
@ -78,6 +95,26 @@
class="input input-bordered w-full max-w-xs" class="input input-bordered w-full max-w-xs"
/> />
</div> </div>
<div>
<label for="date">Activity Types (Comma Seperated)</label>
<input
type="text"
id="activityTypes"
bind:value={activityInput}
class="input input-bordered w-full max-w-xs"
/>
</div>
<div>
<label for="rating">Rating</label>
<input
type="number"
min="0"
max="5"
id="rating"
bind:value={adventureToEdit.rating}
class="input input-bordered w-full max-w-xs"
/>
</div>
<button class="btn btn-primary mr-4 mt-4" on:click={submit}>Save</button <button class="btn btn-primary mr-4 mt-4" on:click={submit}>Save</button
> >
<!-- if there is a button in form, it will close the modal --> <!-- if there is a button in form, it will close the modal -->

View file

@ -78,6 +78,11 @@
<button class="btn btn-primary my-2 md:my-0 md:mr-4" on:click={goToLog} <button class="btn btn-primary my-2 md:my-0 md:mr-4" on:click={goToLog}
>My Log</button >My Log</button
> >
<button
class="btn btn-primary my-2 md:my-0 md:mr-4"
on:click={() => goto("/planner")}>Planner</button
>
<!-- <button <!-- <button
class="btn btn-primary my-2 md:my-0 md:mr-4" class="btn btn-primary my-2 md:my-0 md:mr-4"
on:click={() => goto("/planner")}>Planner</button on:click={() => goto("/planner")}>Planner</button

View file

@ -9,7 +9,7 @@ export const load = (async (event) => {
return redirect(302, "/login"); return redirect(302, "/login");
} }
let adventureUserId = await db let adventureUserId: any[] = await db
.select({ userId: adventureTable.userId }) .select({ userId: adventureTable.userId })
.from(adventureTable) .from(adventureTable)
.where(eq(adventureTable.id, Number(event.params.id))) .where(eq(adventureTable.id, Number(event.params.id)))

View file

@ -11,6 +11,7 @@
onMount(() => { onMount(() => {
if (data.adventure.adventure) { if (data.adventure.adventure) {
adventure = data.adventure.adventure[0]; adventure = data.adventure.adventure[0];
console.log(adventure.activityTypes);
} else { } else {
goto("/404"); goto("/404");
} }
@ -59,8 +60,22 @@
<img <img
src={adventure.imageUrl} src={adventure.imageUrl}
alt={adventure.name} alt={adventure.name}
class="w-50 mt-4 align-middle rounded-lg shadow-lg" class="w-1/2 mt-4 align-middle rounded-lg shadow-lg"
/> />
</div> </div>
{/if} {/if}
{#if adventure.activityTypes && adventure.activityTypes.length > 0}
<div class="flex justify-center items-center mt-4">
<p class="text-center text-lg">Activities:&nbsp</p>
<ul class="flex flex-wrap">
{#each adventure.activityTypes as activity}
<div
class="badge badge-primary mr-1 text-md font-semibold pb-2 pt-1 mb-1"
>
{activity}
</div>
{/each}
</ul>
</div>
{/if}
{/if} {/if}

View file

@ -1,5 +1,6 @@
import { db } from "$lib/db/db.server"; import { db } from "$lib/db/db.server";
import { adventureTable } from "$lib/db/schema"; import { adventureTable } from "$lib/db/schema";
import type { Adventure } from "$lib/utils/types";
import { json, type RequestEvent, type RequestHandler } from "@sveltejs/kit"; import { json, type RequestEvent, type RequestHandler } from "@sveltejs/kit";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
@ -38,6 +39,20 @@ export const GET: RequestHandler = async ({ url, locals }) => {
return json({ error: "Adventure not found" }, { status: 404 }); return json({ error: "Adventure not found" }, { status: 404 });
} }
JSON.stringify(
adventure.map((r) => {
const adventure: Adventure = r as Adventure;
if (typeof adventure.activityTypes === "string") {
try {
adventure.activityTypes = JSON.parse(adventure.activityTypes);
} catch (error) {
console.error("Error parsing activityTypes:", error);
adventure.activityTypes = undefined;
}
}
})
);
// console.log("GET /api/adventure?id=", id); // console.log("GET /api/adventure?id=", id);
// console.log("User:", user); // console.log("User:", user);

View file

@ -0,0 +1,223 @@
import { lucia } from "$lib/server/auth";
import { error, type RequestEvent } from "@sveltejs/kit";
import { adventureTable } from "$lib/db/schema";
import { db } from "$lib/db/db.server";
import { and, eq } from "drizzle-orm";
import type { Adventure } from "$lib/utils/types";
// Gets all the adventures that the user has visited
export async function GET(event: RequestEvent): Promise<Response> {
if (!event.locals.user) {
return new Response(JSON.stringify({ error: "No user found" }), {
status: 401,
headers: {
"Content-Type": "application/json",
},
});
}
let result = await db
.select()
.from(adventureTable)
.where(
and(
eq(adventureTable.userId, event.locals.user.id),
eq(adventureTable.type, "planner")
)
)
.execute();
return new Response(
// turn the result into an Adventure object array
JSON.stringify(
result.map((r) => {
const adventure: Adventure = r as Adventure;
if (typeof adventure.activityTypes === "string") {
try {
adventure.activityTypes = JSON.parse(adventure.activityTypes);
} catch (error) {
console.error("Error parsing activityTypes:", error);
adventure.activityTypes = undefined;
}
}
return adventure;
})
),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
}
);
}
// deletes the adventure given the adventure id and the user object
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",
},
});
}
// get id from the body
const { id } = await event.request.json();
if (!id) {
return new Response(JSON.stringify({ error: "No id found" }), {
status: 400,
headers: {
"Content-Type": "application/json",
},
});
}
let res = await db
.delete(adventureTable)
.where(
and(
eq(adventureTable.userId, event.locals.user.id),
eq(adventureTable.id, Number(id))
)
)
.execute();
return new Response(JSON.stringify({ id: id, res: res }), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
}
export async function POST(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.detailAdventure) {
return error(400, {
message: "No adventure data provided",
});
}
const { name, location, date, description, activityTypes, rating } =
body.detailAdventure;
if (!name) {
return error(400, {
message: "Name field is required!",
});
}
if (rating && (rating < 1 || rating > 5)) {
return error(400, {
message: "Rating must be between 1 and 5",
});
}
console.log(activityTypes);
// insert the adventure to the user's visited list
let res = await db
.insert(adventureTable)
.values({
userId: event.locals.user.id,
type: "planner",
name: name,
location: location || null,
date: date || null,
description: description || null,
activityTypes: JSON.stringify(activityTypes) || null,
rating: rating || null,
})
.returning({ insertedId: adventureTable.id })
.execute();
let insertedId = res[0].insertedId;
console.log(insertedId);
body.detailAdventure.id = insertedId;
// return a response with the adventure object values
return new Response(
JSON.stringify({
adventure: body.detailAdventure,
message: { message: "Adventure added" },
id: insertedId,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
}
);
}
// put route to update existing adventure
export async function PUT(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.detailAdventure) {
return error(400, {
message: "No adventure data provided",
});
}
const { name, location, date, description, activityTypes, id, rating } =
body.detailAdventure;
if (!name) {
return error(400, {
message: "Name field is required!",
});
}
// update the adventure in the user's visited list
await db
.update(adventureTable)
.set({
name: name,
location: location,
date: date,
description: description,
rating: rating,
activityTypes: JSON.stringify(activityTypes),
})
.where(
and(
eq(adventureTable.userId, event.locals.user.id),
eq(adventureTable.id, Number(id))
)
)
.execute();
return new Response(
JSON.stringify({
adventure: body.detailAdventure,
message: { message: "Adventure updated" },
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
}
);
}

View file

@ -18,11 +18,29 @@ export async function GET(event: RequestEvent): Promise<Response> {
let result = await db let result = await db
.select() .select()
.from(adventureTable) .from(adventureTable)
.where(eq(adventureTable.userId, event.locals.user.id)) .where(
and(
eq(adventureTable.userId, event.locals.user.id),
eq(adventureTable.type, "mylog")
)
)
.execute(); .execute();
return new Response( return new Response(
// turn the result into an Adventure object array // turn the result into an Adventure object array
JSON.stringify(result.map((r) => r as Adventure)), JSON.stringify(
result.map((r) => {
const adventure: Adventure = r as Adventure;
if (typeof adventure.activityTypes === "string") {
try {
adventure.activityTypes = JSON.parse(adventure.activityTypes);
} catch (error) {
console.error("Error parsing activityTypes:", error);
adventure.activityTypes = undefined;
}
}
return adventure;
})
),
{ {
status: 200, status: 200,
headers: { headers: {
@ -73,7 +91,6 @@ export async function DELETE(event: RequestEvent): Promise<Response> {
}); });
} }
// add the adventure to the user's visited list
export async function POST(event: RequestEvent): Promise<Response> { export async function POST(event: RequestEvent): Promise<Response> {
if (!event.locals.user) { if (!event.locals.user) {
return new Response(JSON.stringify({ error: "No user found" }), { return new Response(JSON.stringify({ error: "No user found" }), {
@ -84,9 +101,15 @@ export async function POST(event: RequestEvent): Promise<Response> {
}); });
} }
const { newAdventure } = await event.request.json(); const body = await event.request.json();
console.log(newAdventure); if (!body.detailAdventure) {
const { name, location, date, description } = newAdventure; return error(400, {
message: "No adventure data provided",
});
}
const { name, location, date, description, activityTypes, rating } =
body.detailAdventure;
if (!name) { if (!name) {
return error(400, { return error(400, {
@ -94,6 +117,14 @@ export async function POST(event: RequestEvent): Promise<Response> {
}); });
} }
if (rating && (rating < 1 || rating > 5)) {
return error(400, {
message: "Rating must be between 1 and 5",
});
}
console.log(activityTypes);
// insert the adventure to the user's visited list // insert the adventure to the user's visited list
let res = await db let res = await db
.insert(adventureTable) .insert(adventureTable)
@ -104,6 +135,8 @@ export async function POST(event: RequestEvent): Promise<Response> {
location: location || null, location: location || null,
date: date || null, date: date || null,
description: description || null, description: description || null,
activityTypes: JSON.stringify(activityTypes) || null,
rating: rating || null,
}) })
.returning({ insertedId: adventureTable.id }) .returning({ insertedId: adventureTable.id })
.execute(); .execute();
@ -111,10 +144,12 @@ export async function POST(event: RequestEvent): Promise<Response> {
let insertedId = res[0].insertedId; let insertedId = res[0].insertedId;
console.log(insertedId); console.log(insertedId);
body.detailAdventure.id = insertedId;
// return a response with the adventure object values // return a response with the adventure object values
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
adventure: { name, location, date }, adventure: body.detailAdventure,
message: { message: "Adventure added" }, message: { message: "Adventure added" },
id: insertedId, id: insertedId,
}), }),
@ -138,10 +173,21 @@ export async function PUT(event: RequestEvent): Promise<Response> {
}); });
} }
// get properties from the body const body = await event.request.json();
const { newAdventure } = await event.request.json(); if (!body.detailAdventure) {
console.log(newAdventure); return error(400, {
const { name, location, date, id, description } = newAdventure; message: "No adventure data provided",
});
}
const { name, location, date, description, activityTypes, id, rating } =
body.detailAdventure;
if (!name) {
return error(400, {
message: "Name field is required!",
});
}
// update the adventure in the user's visited list // update the adventure in the user's visited list
await db await db
@ -151,6 +197,8 @@ export async function PUT(event: RequestEvent): Promise<Response> {
location: location, location: location,
date: date, date: date,
description: description, description: description,
rating: rating,
activityTypes: JSON.stringify(activityTypes),
}) })
.where( .where(
and( and(
@ -162,7 +210,7 @@ export async function PUT(event: RequestEvent): Promise<Response> {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
adventure: { id, name, location, date, description }, adventure: body.detailAdventure,
message: { message: "Adventure updated" }, message: { message: "Adventure updated" },
}), }),
{ {

View file

@ -11,12 +11,7 @@
}); });
async function add(event: CustomEvent<Adventure>) { async function add(event: CustomEvent<Adventure>) {
let newAdventure: Adventure = { let detailAdventure = event.detail;
name: event.detail.name,
location: event.detail.location,
type: "mylog",
id: -1,
};
const response = await fetch("/api/visits", { const response = await fetch("/api/visits", {
method: "POST", method: "POST",
@ -24,7 +19,7 @@
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
newAdventure, detailAdventure,
}), }),
}); });

View file

@ -8,7 +8,6 @@ export const load: PageServerLoad = async (event) => {
} }
const response = await event.fetch("/api/visits"); const response = await event.fetch("/api/visits");
const result = await response.json(); const result = await response.json();
// let array = result.adventures as Adventure[];
return { return {
result, result,
}; };

View file

@ -15,9 +15,6 @@
import { visitCount } from "$lib/utils/stores/visitCountStore"; import { visitCount } from "$lib/utils/stores/visitCountStore";
import MoreFieldsInput from "$lib/components/CreateNewAdventure.svelte"; import MoreFieldsInput from "$lib/components/CreateNewAdventure.svelte";
let newName = "";
let newLocation = "";
let isShowingMoreFields = false; let isShowingMoreFields = false;
let adventureToEdit: Adventure | undefined; let adventureToEdit: Adventure | undefined;
@ -60,14 +57,9 @@
} }
const createNewAdventure = (event: { detail: Adventure }) => { const createNewAdventure = (event: { detail: Adventure }) => {
let newAdventure: Adventure = { isShowingMoreFields = false;
type: "mylog", let detailAdventure = event.detail;
name: event.detail.name, console.log("Event" + event.detail.name);
location: event.detail.location,
date: event.detail.date,
description: event.detail.description,
id: -1,
};
fetch("/api/visits", { fetch("/api/visits", {
method: "POST", method: "POST",
@ -75,7 +67,7 @@
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
newAdventure, detailAdventure,
}), }),
}) })
.then((response) => { .then((response) => {
@ -89,21 +81,8 @@
return response.json(); return response.json();
}) })
.then((data) => { .then((data) => {
let newId = data.id;
// add to local array for instant view update // add to local array for instant view update
adventures = [ adventures = [...adventures, data.adventure];
...adventures,
{
id: newId,
type: "mylog",
name: event.detail.name,
location: event.detail.location,
date: event.detail.date,
description: event.detail.description,
},
];
newName = ""; // Reset newName and newLocation after adding adventure
newLocation = "";
showToast("Adventure added successfully!"); showToast("Adventure added successfully!");
visitCount.update((n) => n + 1); visitCount.update((n) => n + 1);
}) })
@ -114,34 +93,29 @@
}; };
function saveAdventure(event: { detail: Adventure }) { function saveAdventure(event: { detail: Adventure }) {
console.log("Event" + event.detail); console.log("Event", event.detail);
let detailAdventure = event.detail;
let newAdventure: Adventure = { // put request to /api/visits with id and adventure data
type: "mylog",
name: event.detail.name,
location: event.detail.location,
date: event.detail.date,
id: event.detail.id,
description: event.detail.description,
};
// put request to /api/visits with id and advneture data
fetch("/api/visits", { fetch("/api/visits", {
method: "PUT", method: "PUT",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
newAdventure, detailAdventure,
}), }),
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
console.log("Success:", data); console.log("Success:", data);
// update local array with new data // update local array with new data
adventures = adventures.map((adventure) => const index = adventures.findIndex(
adventure.id === event.detail.id ? event.detail : adventure (adventure) => adventure.id === detailAdventure.id
); );
if (index !== -1) {
adventures[index] = detailAdventure;
}
adventureToEdit = undefined; adventureToEdit = undefined;
showToast("Adventure edited successfully!"); showToast("Adventure edited successfully!");
}) })
@ -161,6 +135,12 @@
function shareLink() { function shareLink() {
let key = generateRandomString(); let key = generateRandomString();
// console log each adventure in the array
for (let i = 0; i < adventures.length; i++) {
console.log(adventures[i]);
}
let data = JSON.stringify(adventures); let data = JSON.stringify(adventures);
fetch("/api/share", { fetch("/api/share", {
method: "POST", method: "POST",
@ -269,7 +249,7 @@
<MoreFieldsInput <MoreFieldsInput
on:create={createNewAdventure} on:create={createNewAdventure}
on:close={handleClose} on:close={handleClose}
on:submit={saveAdventure} type="mylog"
/> />
{/if} {/if}

View file

@ -0,0 +1,14 @@
import { redirect } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
import type { Adventure } from "$lib/utils/types";
export const load: PageServerLoad = async (event) => {
if (!event.locals.user) {
return redirect(302, "/login");
}
const response = await event.fetch("/api/planner");
const result = await response.json();
return {
result,
};
};

View file

@ -1 +1,179 @@
<h1>Welcome to the planner</h1> <script lang="ts">
import type { Adventure } from "$lib/utils/types.js";
import { onMount } from "svelte";
import AdventureCard from "$lib/components/AdventureCard.svelte";
import EditModal from "$lib/components/EditModal.svelte";
import MoreFieldsInput from "$lib/components/CreateNewAdventure.svelte";
import {
saveAdventure,
removeAdventure,
} from "../../services/adventureService.js";
import SucessToast from "$lib/components/SucessToast.svelte";
import mapDrawing from "$lib/assets/adventure_map.svg";
export let data;
let plans: Adventure[] = [];
let isLoading = true;
onMount(async () => {
console.log(data);
plans = data.result;
isLoading = false;
});
let isShowingMoreFields: boolean = false;
let isShowingToast: boolean = false;
let toastAction: string = "";
let adventureToEdit: Adventure | undefined;
console.log(data);
function showToast(action: string) {
toastAction = action;
isShowingToast = true;
setTimeout(() => {
isShowingToast = false;
toastAction = "";
}, 3000);
}
function editPlan(event: { detail: number }) {
const adventure = plans.find((adventure) => adventure.id === event.detail);
if (adventure) {
adventureToEdit = adventure;
}
}
function handleClose() {
adventureToEdit = undefined;
isShowingMoreFields = false;
}
async function savePlan(event: { detail: Adventure }) {
let newArray = await saveAdventure(event.detail, plans);
if (newArray.length > 0) {
plans = newArray;
showToast("Adventure updated successfully!");
} else {
showToast("Failed to update adventure");
}
adventureToEdit = undefined;
}
async function remove(event: { detail: number }) {
let initialLength: number = plans.length;
let theAdventure = plans.find((adventure) => adventure.id === event.detail);
if (theAdventure) {
let newArray = await removeAdventure(theAdventure, plans);
if (newArray.length === initialLength - 1) {
plans = newArray;
showToast("Adventure removed successfully!");
} else {
showToast("Failed to remove adventure");
}
}
}
const createNewAdventure = (event: { detail: Adventure }) => {
isShowingMoreFields = false;
let detailAdventure = event.detail;
console.log("Event" + event.detail.name);
fetch("/api/planner", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
detailAdventure,
}),
})
.then((response) => {
if (!response.ok) {
return response.json().then((data) => {
throw new Error(
data.error || `Failed to add adventure - ${data?.message}`
);
});
}
return response.json();
})
.then((data) => {
// add to local array for instant view update
plans = [...plans, data.adventure];
// showToast("Adventure added successfully!");
// visitCount.update((n) => n + 1);
})
.catch((error) => {
console.error("Error:", error);
// showToast(error.message);
});
};
</script>
{#if isShowingToast}
<SucessToast action={toastAction} />
{/if}
<div class="flex justify-center items-center w-full mt-4 mb-4">
<article class="prose">
<h2 class="text-center">Add new Plan</h2>
</article>
</div>
<div class="flex flex-row items-center justify-center gap-4">
<button
type="button"
class="btn btn-secondary"
on:click={() => (isShowingMoreFields = !isShowingMoreFields)}
>
<iconify-icon icon="mdi:plus" class="text-2xl"></iconify-icon>
</button>
</div>
{#if plans.length != 0}
<div class="flex justify-center items-center w-full mt-4 mb-4">
<article class="prose">
<h1 class="text-center">My Adventure Plans</h1>
</article>
</div>
{/if}
{#if isLoading}
<div class="flex justify-center items-center w-full mt-16">
<span class="loading loading-spinner w-24 h-24"></span>
</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 plans as adventure (adventure.id)}
<AdventureCard
{adventure}
type="planner"
on:edit={editPlan}
on:remove={remove}
/>
{/each}
</div>
{#if plans.length == 0 && !isLoading}
<div class="flex flex-col items-center justify-center mt-16">
<article class="prose mb-4"><h2>Add some plans!</h2></article>
<img src={mapDrawing} width="25%" alt="Logo" />
</div>
{/if}
{#if adventureToEdit && adventureToEdit.id != undefined}
<EditModal bind:adventureToEdit on:submit={savePlan} on:close={handleClose} />
{/if}
{#if isShowingMoreFields}
<MoreFieldsInput
on:create={createNewAdventure}
on:close={handleClose}
type="planner"
/>
{/if}

View file

@ -1 +1,108 @@
import type { Adventure } from "$lib/utils/types"; import type { Adventure } from "$lib/utils/types";
// json that maps the type to api routes
const apiRoutes: { [key: string]: string } = {
planner: "/api/planner",
mylog: "/api/visits",
};
/**
* Saves an adventure by sending a PUT request to the corresponding API endpoint.
* If the request is successful, the local adventure array is updated with the new data.
* If the request fails, an empty array is returned to allow for handling errors.
* @param adventure - The adventure object to be saved.
* @param adventureArray - The array of adventures to be updated.
* @returns A promise that resolves to the updated adventure array.
*/
export async function saveAdventure(
adventure: Adventure,
adventureArray: Adventure[]
): Promise<Adventure[]> {
const detailAdventure = adventure;
const type = detailAdventure.type;
const url = apiRoutes[type];
// put request to /api/visits with id and adventure data
const response = await fetch(url, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
detailAdventure,
}),
});
if (response.ok) {
// update local array with new data
const index = adventureArray.findIndex(
(adventure) => adventure.id === detailAdventure.id
);
if (index !== -1) {
adventureArray[index] = detailAdventure;
}
// showToast("Adventure edited successfully!");
} else {
console.error("Error:", response.statusText);
adventureArray = [];
}
return adventureArray;
}
export async function removeAdventure(
adventure: Adventure,
adventureArray: Adventure[]
): Promise<Adventure[]> {
let url = apiRoutes[adventure.type];
// delete request to /api/visits with id
const response = await fetch(url, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: adventure.id }),
});
if (response.ok) {
// remove adventure from array where id matches
adventureArray = adventureArray.filter(
(existingAdventure) => existingAdventure.id !== adventure.id
);
// showToast("Adventure removed successfully!");
} else {
console.error("Error:", response.statusText);
adventureArray = [];
}
console.log(adventureArray);
return adventureArray;
}
/**
* function removeAdventure(event: { detail: number }) {
console.log("Event ID " + event.detail);
// send delete request to server at /api/visits
fetch("/api/visits", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: event.detail }),
})
.then((response) => response.json())
.then((data) => {
console.log("Success:", data);
// remove adventure from array where id matches
plans = plans.filter((adventure) => adventure.id !== event.detail);
// showToast("Adventure removed successfully!");
// visitCount.update((n) => n - 1);
})
.catch((error) => {
console.error("Error:", error);
});
}
*
*
*/