316 lines
8.6 KiB
Svelte
316 lines
8.6 KiB
Svelte
<script lang="ts">
|
|
export let data;
|
|
let adventures: Adventure[] = [];
|
|
let isLoading = true;
|
|
|
|
import AdventureCard from "$lib/components/AdventureCard.svelte";
|
|
import type { Adventure } from "$lib/utils/types";
|
|
import {
|
|
addAdventure,
|
|
clearAdventures,
|
|
getAdventures,
|
|
getNextId,
|
|
} from "../../services/adventureService";
|
|
import { onMount } from "svelte";
|
|
import { exportData } from "../../services/export";
|
|
import { importData } from "../../services/import";
|
|
import exportFile from "$lib/assets/exportFile.svg";
|
|
import deleteIcon from "$lib/assets/deleteIcon.svg";
|
|
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";
|
|
import { visitCount } from "$lib/utils/stores/visitCountStore";
|
|
|
|
let newName = "";
|
|
let newLocation = "";
|
|
|
|
let editId: number = NaN;
|
|
let editName: string = "";
|
|
let editLocation: string = "";
|
|
let editCreated: string = "";
|
|
|
|
let isShowingToast: boolean = false;
|
|
let toastAction: string = "";
|
|
|
|
// Sets the adventures array to the data from the server
|
|
onMount(async () => {
|
|
console.log(data);
|
|
adventures = data.result.adventures;
|
|
isLoading = false;
|
|
});
|
|
|
|
let count = 0;
|
|
visitCount.subscribe((value) => {
|
|
count = value;
|
|
});
|
|
|
|
function showToast(action: string) {
|
|
toastAction = action;
|
|
isShowingToast = true;
|
|
|
|
setTimeout(() => {
|
|
isShowingToast = false;
|
|
toastAction = "";
|
|
}, 3000);
|
|
}
|
|
|
|
const createNewAdventure = () => {
|
|
let currentDate = new Date();
|
|
let dateString = currentDate.toISOString().slice(0, 10); // Get date in "yyyy-mm-dd" format
|
|
// post to /api/visits
|
|
fetch("/api/visits", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
name: newName,
|
|
location: newLocation,
|
|
created: dateString,
|
|
}),
|
|
})
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
let newId = data.id;
|
|
// add to local array for instant view update
|
|
adventures = [
|
|
...adventures,
|
|
{
|
|
id: newId,
|
|
name: newName,
|
|
location: newLocation,
|
|
created: dateString,
|
|
},
|
|
];
|
|
newName = ""; // Reset newName and newLocation after adding adventure
|
|
newLocation = "";
|
|
showToast("Adventure added successfully!");
|
|
visitCount.update((n) => n + 1);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
});
|
|
};
|
|
|
|
function saveAdventure(event: { detail: Adventure }) {
|
|
console.log("Event" + event.detail);
|
|
// put request to /api/visits with id and advneture data
|
|
fetch("/api/visits", {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
id: event.detail.id,
|
|
name: event.detail.name,
|
|
location: event.detail.location,
|
|
created: event.detail.created,
|
|
}),
|
|
})
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
console.log("Success:", data);
|
|
// update local array with new data
|
|
adventures = adventures.map((adventure) =>
|
|
adventure.id === event.detail.id ? event.detail : adventure,
|
|
);
|
|
editId = NaN;
|
|
editName = "";
|
|
editLocation = "";
|
|
editCreated = "";
|
|
showToast("Adventure edited successfully!");
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
});
|
|
}
|
|
|
|
function editAdventure(event: { detail: number }) {
|
|
const adventure = adventures.find(
|
|
(adventure) => adventure.id === event.detail,
|
|
);
|
|
if (adventure) {
|
|
editId = adventure.id;
|
|
editName = adventure.name;
|
|
editLocation = adventure.location;
|
|
editCreated = adventure.created;
|
|
}
|
|
}
|
|
|
|
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);
|
|
showToast("Link copied to clipboard!");
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
});
|
|
}
|
|
|
|
function handleClose() {
|
|
editId = NaN;
|
|
editName = "";
|
|
editLocation = "";
|
|
editCreated = "";
|
|
}
|
|
|
|
function deleteData() {
|
|
fetch("/api/clearvisits", {
|
|
method: "DELETE",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
})
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
console.log("Success:", data);
|
|
// remove adventure from array where id matches
|
|
adventures = [];
|
|
showToast("Adventure removed successfully!");
|
|
visitCount.set(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
});
|
|
}
|
|
|
|
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
|
|
adventures = adventures.filter(
|
|
(adventure) => adventure.id !== event.detail,
|
|
);
|
|
showToast("Adventure removed successfully!");
|
|
visitCount.update((n) => n - 1);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
});
|
|
}
|
|
</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"
|
|
/>
|
|
<input
|
|
type="text"
|
|
bind:value={newLocation}
|
|
placeholder="Adventure Location"
|
|
class="input input-bordered w-full max-w-xs"
|
|
/>
|
|
<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 isLoading}
|
|
<div class="flex justify-center items-center w-full mt-16">
|
|
<span class="loading loading-spinner w-24 h-24"></span>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if isShowingToast}
|
|
<SucessToast action={toastAction} />
|
|
{/if}
|
|
|
|
{#if !Number.isNaN(editId)}
|
|
<EditModal
|
|
bind:editId
|
|
bind:editName
|
|
bind:editLocation
|
|
bind:editCreated
|
|
on:submit={saveAdventure}
|
|
on:close={handleClose}
|
|
/>
|
|
{/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 adventures as adventure (adventure.id)}
|
|
<AdventureCard
|
|
type="mylog"
|
|
id={adventure.id}
|
|
name={adventure.name}
|
|
location={adventure.location}
|
|
created={adventure.created}
|
|
on:edit={editAdventure}
|
|
on:remove={removeAdventure}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
|
|
{#if adventures.length == 0 && !isLoading}
|
|
<div class="flex flex-col items-center justify-center mt-16">
|
|
<article class="prose mb-4"><h2>Add some adventures!</h2></article>
|
|
<img src={mapDrawing} width="25%" alt="Logo" />
|
|
</div>
|
|
{/if}
|
|
|
|
{#if adventures.length != 0 && !isLoading}
|
|
<div class="flex justify-center items-center w-full mt-4">
|
|
<article class="prose">
|
|
<h2 class="text-center">Actions</h2>
|
|
</article>
|
|
</div>
|
|
<div
|
|
class="flex flex-row items-center justify-center mt-2 gap-4 mb-4 flex-wrap"
|
|
>
|
|
<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}
|