mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-08-04 12:45:17 +02:00
commit
0fc9cc9efe
6 changed files with 142 additions and 154 deletions
27
README.md
27
README.md
|
@ -1,11 +1,15 @@
|
|||
# AdventureLog: Embark, Explore, Remember. 🌍
|
||||
|
||||
_**⚠️ AdventureLog is in early development and is not recommended for production use until version 1.0!**_
|
||||
### *"Never forget an adventure with AdventureLog - Your ultimate travel companion!"*
|
||||
-----
|
||||
|
||||
### _"Never forget an adventure with AdventureLog - Your ultimate travel companion!"_
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Docker 🐋 (Recomended)
|
||||
|
||||
1. Clone the repository
|
||||
2. Edit the `docker-compose.yml` file and change the database password
|
||||
3. Run `docker compose up -d` to build the image and start the container
|
||||
|
@ -14,3 +18,22 @@ _**⚠️ 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 ℹ️
|
||||
|
||||
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:
|
||||
|
||||
- Logging past adventures with fields like name, date, location, description, and rating.
|
||||
- Planning future adventures with similar fields.
|
||||
- Tagging different activity types for better organization.
|
||||
- Viewing countries, regions, and marking visited regions.
|
||||
|
||||
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.
|
||||
|
||||
## Roadmap 🛣️
|
||||
|
||||
- Improved mobile device support
|
||||
- Password reset functionality
|
||||
- Improved error handling
|
||||
- Handling of adventure cards with variable width
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
function create() {
|
||||
addActivityType();
|
||||
if (newAdventure.name.trim() === "") {
|
||||
alert("Name is required");
|
||||
return;
|
||||
}
|
||||
dispatch("create", newAdventure);
|
||||
console.log(newAdventure);
|
||||
}
|
||||
|
@ -70,6 +74,7 @@
|
|||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
required
|
||||
bind:value={newAdventure.name}
|
||||
class="input input-bordered w-full max-w-xs"
|
||||
/>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export let appVersion = "Web 0.1.6-alpha";
|
||||
export let appVersion = "Web 0.1.7-alpha";
|
||||
export let appTitle = "AdventureLog";
|
||||
export let copyrightYear = "2024"
|
||||
export let copyrightYear = "2024";
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import type { Adventure } from "$lib/utils/types";
|
||||
import { onMount } from "svelte";
|
||||
import exportFile from "$lib/assets/exportFile.svg";
|
||||
import ConfirmModal from "$lib/components/ConfirmModal.svelte";
|
||||
import deleteIcon from "$lib/assets/deleteIcon.svg";
|
||||
import SucessToast from "$lib/components/SucessToast.svelte";
|
||||
import mapDrawing from "$lib/assets/adventure_map.svg";
|
||||
|
@ -14,6 +15,11 @@
|
|||
import { generateRandomString } from "$lib";
|
||||
import { visitCount } from "$lib/utils/stores/visitCountStore";
|
||||
import MoreFieldsInput from "$lib/components/CreateNewAdventure.svelte";
|
||||
import {
|
||||
addAdventure,
|
||||
removeAdventure,
|
||||
saveAdventure,
|
||||
} from "../../services/adventureService.js";
|
||||
|
||||
let isShowingMoreFields = false;
|
||||
|
||||
|
@ -21,6 +27,7 @@
|
|||
|
||||
let isShowingToast: boolean = false;
|
||||
let toastAction: string = "";
|
||||
let confirmModalOpen: boolean = false;
|
||||
|
||||
// Sets the adventures array to the data from the server
|
||||
onMount(async () => {
|
||||
|
@ -56,72 +63,26 @@
|
|||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
const createNewAdventure = (event: { detail: Adventure }) => {
|
||||
const createNewAdventure = async (event: { detail: Adventure }) => {
|
||||
isShowingMoreFields = false;
|
||||
let detailAdventure = event.detail;
|
||||
console.log("Event" + event.detail.name);
|
||||
|
||||
fetch("/api/visits", {
|
||||
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
|
||||
adventures = [...adventures, data.adventure];
|
||||
showToast("Adventure added successfully!");
|
||||
visitCount.update((n) => n + 1);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
showToast(error.message);
|
||||
});
|
||||
let newArray = await addAdventure(event.detail, adventures);
|
||||
if (newArray.length > 0) {
|
||||
adventures = newArray;
|
||||
showToast("Adventure added successfully!");
|
||||
} else {
|
||||
showToast("Failed to add adventure");
|
||||
}
|
||||
};
|
||||
|
||||
function saveAdventure(event: { detail: Adventure }) {
|
||||
console.log("Event", event.detail);
|
||||
let detailAdventure = event.detail;
|
||||
|
||||
// put request to /api/visits with id and adventure data
|
||||
fetch("/api/visits", {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
detailAdventure,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
console.log("Success:", data);
|
||||
// update local array with new data
|
||||
const index = adventures.findIndex(
|
||||
(adventure) => adventure.id === detailAdventure.id
|
||||
);
|
||||
if (index !== -1) {
|
||||
adventures[index] = detailAdventure;
|
||||
}
|
||||
adventureToEdit = undefined;
|
||||
showToast("Adventure edited successfully!");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
});
|
||||
async function save(event: { detail: Adventure }) {
|
||||
let newArray = await saveAdventure(event.detail, adventures);
|
||||
if (newArray.length > 0) {
|
||||
adventures = newArray;
|
||||
showToast("Adventure updated successfully!");
|
||||
} else {
|
||||
showToast("Failed to update adventure");
|
||||
}
|
||||
adventureToEdit = undefined;
|
||||
}
|
||||
|
||||
function editAdventure(event: { detail: number }) {
|
||||
|
@ -163,6 +124,7 @@
|
|||
|
||||
function handleClose() {
|
||||
adventureToEdit = undefined;
|
||||
confirmModalOpen = false;
|
||||
isShowingMoreFields = false;
|
||||
}
|
||||
|
||||
|
@ -186,29 +148,20 @@
|
|||
});
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
async function remove(event: { detail: number }) {
|
||||
let initialLength: number = adventures.length;
|
||||
let theAdventure = adventures.find(
|
||||
(adventure) => adventure.id === event.detail
|
||||
);
|
||||
if (theAdventure) {
|
||||
let newArray = await removeAdventure(theAdventure, adventures);
|
||||
if (newArray.length === initialLength - 1) {
|
||||
adventures = newArray;
|
||||
showToast("Adventure removed successfully!");
|
||||
visitCount.update((n) => n - 1);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
});
|
||||
} else {
|
||||
showToast("Failed to remove adventure");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -254,11 +207,7 @@
|
|||
{/if}
|
||||
|
||||
{#if adventureToEdit && adventureToEdit.id != undefined}
|
||||
<EditModal
|
||||
bind:adventureToEdit
|
||||
on:submit={saveAdventure}
|
||||
on:close={handleClose}
|
||||
/>
|
||||
<EditModal bind:adventureToEdit on:submit={save} on:close={handleClose} />
|
||||
{/if}
|
||||
|
||||
<div
|
||||
|
@ -269,7 +218,7 @@
|
|||
{adventure}
|
||||
type="mylog"
|
||||
on:edit={editAdventure}
|
||||
on:remove={removeAdventure}
|
||||
on:remove={remove}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -293,7 +242,7 @@
|
|||
<button class="btn btn-neutral" on:click={exportData}>
|
||||
<img src={exportFile} class="inline-block -mt-1" alt="Logo" /> Save as File
|
||||
</button>
|
||||
<button class="btn btn-neutral" on:click={deleteData}>
|
||||
<button class="btn btn-neutral" on:click={() => (confirmModalOpen = true)}>
|
||||
<img src={deleteIcon} class="inline-block -mt-1" alt="Logo" /> Delete Data
|
||||
</button>
|
||||
<button class="btn btn-neutral" on:click={shareLink}>
|
||||
|
@ -303,6 +252,16 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if confirmModalOpen}
|
||||
<ConfirmModal
|
||||
on:close={handleClose}
|
||||
on:confirm={deleteData}
|
||||
title="Delete all Adventures"
|
||||
isWarning={false}
|
||||
message="Are you sure you want to delete all adventures?"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<svelte:head>
|
||||
<title>My Log | AdventureLog</title>
|
||||
<meta
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import {
|
||||
saveAdventure,
|
||||
removeAdventure,
|
||||
addAdventure,
|
||||
} from "../../services/adventureService.js";
|
||||
import SucessToast from "$lib/components/SucessToast.svelte";
|
||||
import mapDrawing from "$lib/assets/adventure_map.svg";
|
||||
|
@ -76,40 +77,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
const createNewAdventure = (event: { detail: Adventure }) => {
|
||||
const createNewAdventure = async (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);
|
||||
});
|
||||
let newArray = await addAdventure(event.detail, plans);
|
||||
if (newArray.length > 0) {
|
||||
plans = newArray;
|
||||
showToast("Adventure added successfully!");
|
||||
} else {
|
||||
showToast("Failed to add adventure");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { visitCount } from "$lib/utils/stores/visitCountStore";
|
||||
import type { Adventure } from "$lib/utils/types";
|
||||
|
||||
// json that maps the type to api routes
|
||||
/**
|
||||
* Object containing the API routes for the different types of adventures.
|
||||
*/
|
||||
const apiRoutes: { [key: string]: string } = {
|
||||
planner: "/api/planner",
|
||||
mylog: "/api/visits",
|
||||
|
@ -50,6 +53,12 @@ export async function saveAdventure(
|
|||
return adventureArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an adventure from the adventure array and sends a delete request to the server.
|
||||
* @param adventure - The adventure to be removed.
|
||||
* @param adventureArray - The array of adventures.
|
||||
* @returns A promise that resolves to the updated adventure array.
|
||||
*/
|
||||
export async function removeAdventure(
|
||||
adventure: Adventure,
|
||||
adventureArray: Adventure[]
|
||||
|
@ -81,28 +90,44 @@ export async function removeAdventure(
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 }),
|
||||
* Adds an adventure to the adventure array and sends a POST request to the specified URL.
|
||||
* @param {Adventure} adventure - The adventure to be added.
|
||||
* @param {Adventure[]} adventureArray - The array of adventures.
|
||||
* @returns {Promise<Adventure[]>} - A promise that resolves to the updated adventure array.
|
||||
*/
|
||||
export async function addAdventure(
|
||||
adventure: Adventure,
|
||||
adventureArray: Adventure[]
|
||||
): Promise<Adventure[]> {
|
||||
let url = apiRoutes[adventure.type];
|
||||
let detailAdventure = adventure;
|
||||
// post request to /api/visits with adventure data
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ detailAdventure }),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
adventureArray.push(data.adventure);
|
||||
if (data.adventure.type === "mylog") {
|
||||
incrementVisitCount(1);
|
||||
}
|
||||
})
|
||||
.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);
|
||||
});
|
||||
}
|
||||
*
|
||||
*
|
||||
*/
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
return [];
|
||||
});
|
||||
|
||||
return adventureArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the visit count by the specified amount.
|
||||
* @param {number} amount - The amount to increment the visit count by.
|
||||
*/
|
||||
export function incrementVisitCount(amount: number) {
|
||||
visitCount.update((n) => n + 1);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue