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

Merge pull request #54 from seanmorley15/development

Development
This commit is contained in:
Sean Morley 2024-05-04 13:49:13 -04:00 committed by GitHub
commit 0fc9cc9efe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 142 additions and 154 deletions

View file

@ -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

View file

@ -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"
/>

View file

@ -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";

View file

@ -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

View file

@ -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>

View file

@ -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);
}