1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-21 22:09:36 +02:00

Add i18n support for transportation, notes, checklist, and collection components

This commit is contained in:
Sean Morley 2024-11-03 22:55:38 -05:00
parent c0aaec1436
commit 3df124b250
11 changed files with 305 additions and 213 deletions

View file

@ -3,6 +3,7 @@
import type { Checklist, Collection, User } from '$lib/types';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
import { t } from 'svelte-i18n';
import Launch from '~icons/mdi/launch';
import TrashCan from '~icons/mdi/trash-can';
@ -21,10 +22,10 @@
method: 'DELETE'
});
if (res.ok) {
addToast('success', 'Checklist deleted successfully');
addToast('success', $t('checklist.checklist_deleted'));
dispatch('delete', checklist.id);
} else {
addToast('Failed to delete checklist', 'error');
addToast($t('checklist.checklist_delete_error'), 'error');
}
}
</script>
@ -38,9 +39,12 @@
{checklist.name}
</h2>
</div>
<div class="badge badge-primary">Checklist</div>
<div class="badge badge-primary">{$t('adventures.checklist')}</div>
{#if checklist.items.length > 0}
<p>{checklist.items.length} {checklist.items.length > 1 ? 'Items' : 'Item'}</p>
<p>
{checklist.items.length}
{checklist.items.length > 1 ? $t('checklist.items') : $t('checklist.item')}
</p>
{/if}
{#if checklist.date && checklist.date !== ''}
<div class="inline-flex items-center">
@ -49,11 +53,8 @@
</div>
{/if}
<div class="card-actions justify-end">
<!-- <button class="btn btn-neutral mb-2" on:click={() => goto(`/notes/${note.id}`)}
><Launch class="w-6 h-6" />Open Details</button
> -->
<button class="btn btn-neutral-200 mb-2" on:click={editChecklist}>
<Launch class="w-6 h-6" />Open
<Launch class="w-6 h-6" />{$t('notes.open')}
</button>
{#if checklist.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))}
<button

View file

@ -1,10 +1,10 @@
<script lang="ts">
import { isValidUrl } from '$lib';
import type { Collection, Checklist, User, ChecklistItem } from '$lib/types';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
import { onMount } from 'svelte';
let modal: HTMLDialogElement;
import { t } from 'svelte-i18n';
export let checklist: Checklist | null = null;
export let collection: Collection;
@ -21,11 +21,11 @@
function addItem() {
if (newItem.trim() == '') {
warning = 'Item cannot be empty';
warning = $t('checklist.item_cannot_be_empty');
return;
}
if (newChecklist.items.find((item) => item.name.trim() == newItem)) {
warning = 'Item already exists';
warning = $t('checklist.item_already_exists');
return;
}
items = [
@ -130,15 +130,15 @@
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
<h3 class="font-bold text-lg mb-2">Checklist Editor</h3>
<h3 class="font-bold text-lg mb-2">{$t('checklist.checklist_editor')}</h3>
{#if initialName}
<p class="font-semibold text-md mb-2">Editing checklist {initialName}</p>
<p class="font-semibold text-md mb-2">{$t('checklist.editing_checklist')} {initialName}</p>
{/if}
{#if (checklist && user?.pk == checklist?.user_id) || (user && collection && collection.shared_with.includes(user.uuid)) || !checklist}
<form on:submit|preventDefault>
<div class="form-control mb-2">
<label for="name">Name</label>
<label for="name">{$t('adventures.name')}</label>
<input
type="text"
id="name"
@ -147,7 +147,7 @@
/>
</div>
<div class="form-control mb-2">
<label for="content">Date</label>
<label for="content">{$t('adventures.date')}</label>
<input
type="date"
id="date"
@ -163,7 +163,7 @@
<input
type="text"
id="new_item"
placeholder="New Item"
placeholder={$t('checklist.new_item')}
name="new_item"
bind:value={newItem}
class="input input-bordered w-full max-w-xs mt-1"
@ -179,12 +179,12 @@
class="btn btn-sm btn-primary absolute right-0 mt-2.5 mr-4"
on:click={addItem}
>
Add
{$t('adventures.add')}
</button>
</div>
{#if items.length > 0}
<div class="divider"></div>
<h2 class=" text-xl font-semibold mb-4 -mt-3">Items</h2>
<h2 class=" text-xl font-semibold mb-4 -mt-3">{$t('checklist.items')}</h2>
{/if}
{#each items as item, i}
@ -202,7 +202,7 @@
class="btn btn-sm btn-error absolute right-0 mt-2.5 mr-4"
on:click={() => removeItem(i)}
>
Remove
{$t('adventures.remove')}
</button>
</div>
{/each}
@ -226,8 +226,8 @@
</div>
{/if}
<button class="btn btn-primary mr-1" on:click={save}>Save</button>
<button class="btn btn-neutral" on:click={close}>Close</button>
<button class="btn btn-primary mr-1" on:click={save}>{$t('notes.save')}</button>
<button class="btn btn-neutral" on:click={close}>{$t('about.close')}</button>
{#if collection.is_public}
<div role="alert" class="alert mt-4">
@ -244,14 +244,14 @@
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>This checklist is public because it is in a public collection.</span>
<span>{$t('checklist.checklist_public')}</span>
</div>
{/if}
</form>
{:else}
<form>
<div class="form-control mb-2">
<label for="name">Name</label>
<label for="name">{$t('adventures.name')}</label>
<input
type="text"
id="name"
@ -261,7 +261,7 @@
/>
</div>
<div class="form-control mb-2">
<label for="content">Date</label>
<label for="content">{$t('adventures.date')}</label>
<input
type="date"
id="date"
@ -276,7 +276,7 @@
{#if items.length > 0}
<div class="divider"></div>
<h2 class=" text-xl font-semibold mb-4 -mt-3">Items</h2>
<h2 class=" text-xl font-semibold mb-4 -mt-3">{$t('checklist.items')}</h2>
{/if}
{#each items as item, i}
@ -299,7 +299,7 @@
</div>
{/each}
<button class="btn btn-neutral" on:click={close}>Close</button>
<button class="btn btn-neutral" on:click={close}>{$t('about.close')}</button>
</form>
{/if}
</div>

View file

@ -1,7 +1,8 @@
<script lang="ts">
export let collectionToEdit: Collection;
import { createEventDispatcher } from 'svelte';
import type { Adventure, Collection } from '$lib/types';
import type { Collection } from '$lib/types';
import { t } from 'svelte-i18n';
const dispatch = createEventDispatcher();
import { onMount } from 'svelte';
import { addToast } from '$lib/toasts';
@ -41,17 +42,17 @@
if (collectionToEdit.end_date && collectionToEdit.start_date) {
if (new Date(collectionToEdit.start_date) > new Date(collectionToEdit.end_date)) {
addToast('error', 'Start date must be before end date');
addToast('error', $t('adventures.start_before_end_error'));
return;
}
}
if (collectionToEdit.end_date && !collectionToEdit.start_date) {
addToast('error', 'Please provide a start date');
addToast('error', $t('adventures.no_start_date'));
return;
}
if (collectionToEdit.start_date && !collectionToEdit.end_date) {
addToast('error', 'Please provide an end date');
addToast('error', $t('adventures.no_end_date'));
return;
}
@ -66,12 +67,12 @@
console.log(data);
if (data) {
addToast('success', 'Adventure edited successfully!');
addToast('success', $t('collection.collection_edit_success'));
dispatch('saveEdit', collectionToEdit);
close();
} else {
addToast('warning', 'Error editing adventure');
console.log('Error editing adventure');
addToast('warning', $t('collection.error_editing_collection'));
console.log('Error editing collection');
}
}
}
@ -81,7 +82,7 @@
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
<h3 class="font-bold text-lg">Edit Collection: {originalName}</h3>
<h3 class="font-bold text-lg">{$t('adventures.edit_collection')}: {originalName}</h3>
<div
class="modal-action items-center"
style="display: flex; flex-direction: column; align-items: center; width: 100%;"
@ -97,7 +98,7 @@
bind:value={collectionToEdit.id}
class="input input-bordered w-full max-w-xs mt-1"
/>
<label for="name">Name</label><br />
<label for="name">{$t('adventures.name')}</label><br />
<input
type="text"
name="name"
@ -107,7 +108,9 @@
/>
</div>
<div class="mb-2">
<label for="date">Description <Notebook class="inline-block -mt-1 mb-1 w-6 h-6" /></label
<label for="date"
>{$t('adventures.description')}
<Notebook class="inline-block -mt-1 mb-1 w-6 h-6" /></label
><br />
<div class="flex">
<input
@ -127,7 +130,8 @@
> -->
</div>
<div class="mb-2">
<label for="start_date">Start Date <Calendar class="inline-block mb-1 w-6 h-6" /></label
<label for="start_date"
>{$t('adventures.start_date')} <Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="date"
@ -138,8 +142,9 @@
/>
</div>
<div class="mb-2">
<label for="end_date">End Date <Calendar class="inline-block mb-1 w-6 h-6" /></label><br
/>
<label for="end_date"
>{$t('adventures.end_date')} <Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="date"
id="end_date"
@ -149,7 +154,7 @@
/>
</div>
<div class="mb-2">
<label for="end_date">Link </label><br />
<label for="end_date">{$t('adventures.link')} </label><br />
<input
type="url"
id="link"
@ -160,8 +165,9 @@
</div>
</div>
<div class="mb-2">
<label for="is_public">Public <Earth class="inline-block -mt-1 mb-1 w-6 h-6" /></label><br
/>
<label for="is_public"
>{$t('adventures.public')} <Earth class="inline-block -mt-1 mb-1 w-6 h-6" /></label
><br />
<input
type="checkbox"
class="toggle toggle-primary"
@ -173,7 +179,7 @@
{#if collectionToEdit.is_public}
<div class="bg-neutral p-4 rounded-md shadow-sm">
<p class=" font-semibold">Share this Adventure!</p>
<p class=" font-semibold">{$t('adventures.share_adventure')}</p>
<div class="flex items-center justify-between">
<p class="text-card-foreground font-mono">
{window.location.origin}/collections/{collectionToEdit.id}
@ -187,7 +193,7 @@
}}
class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 px-4 py-2"
>
Copy Link
{$t('adventures.copy_link')}
</button>
</div>
</div>
@ -195,7 +201,7 @@
<button type="submit" class="btn btn-primary mr-4 mt-4" on:click={submit}>Edit</button>
<!-- 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}>{$t('about.close')}</button>
</form>
<div class="flex items-center justify-center flex-wrap gap-4 mt-4"></div>
</div>

View file

@ -6,6 +6,7 @@
import { onMount } from 'svelte';
import { addToast } from '$lib/toasts';
let modal: HTMLDialogElement;
import { t } from 'svelte-i18n';
console.log(transportationToEdit.id);
@ -64,7 +65,7 @@
transportationToEdit.date &&
transportationToEdit.date > transportationToEdit.end_date
) {
addToast('error', 'End date cannot be before start date');
addToast('error', $t('adventures.start_before_end_error'));
return;
}
// make sure end_date has a start_date
@ -83,11 +84,11 @@
transportationToEdit = result;
addToast('success', 'Transportation edited successfully!');
addToast('success', $t('transportation.transportation_edit_success'));
dispatch('saveEdit', transportationToEdit);
close();
} else {
addToast('error', 'Error editing transportaion');
addToast('error', $t('transportation.error_editing_transportation'));
}
}
</script>
@ -96,7 +97,7 @@
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
<h3 class="font-bold text-lg">Edit Transportation: {originalName}</h3>
<h3 class="font-bold text-lg">{$t('transportation.edit_transportation')}: {originalName}</h3>
<div
class="modal-action items-center"
style="display: flex; flex-direction: column; align-items: center; width: 100%;"
@ -122,26 +123,28 @@
class="input input-bordered w-full max-w-xs mt-1"
/>
<div class="mb-2">
<label for="type">Type <PlaneCar class="inline-block mb-1 w-6 h-6" /></label><br />
<label for="type"
>{$t('transportation.type')} <PlaneCar class="inline-block mb-1 w-6 h-6" /></label
><br />
<select
class="select select-bordered w-full max-w-xs"
name="type"
id="type"
bind:value={transportationToEdit.type}
>
<option disabled selected>Transport Type</option>
<option value="car">Car</option>
<option value="plane">Plane</option>
<option value="train">Train</option>
<option value="bus">Bus</option>
<option value="boat">Boat</option>
<option value="bike">Bike</option>
<option value="walking">Walking</option>
<option value="other">Other</option>
<option disabled selected>{$t('transportation.type')}</option>
<option value="car">{$t('transportation.modes.car')}</option>
<option value="plane">{$t('transportation.modes.plane')}</option>
<option value="train">{$t('transportation.modes.train')}</option>
<option value="bus">{$t('transportation.modes.bus')}</option>
<option value="boat">{$t('transportation.modes.boat')}</option>
<option value="bike">{$t('transportation.modes.bike')}</option>
<option value="walking">{$t('transportation.modes.walking')}</option>
<option value="other">{$t('transportation.modes.other')}</option>
</select>
</div>
<label for="name">Name</label><br />
<label for="name">{$t('adventures.name')}</label><br />
<input
type="text"
name="name"
@ -151,7 +154,9 @@
/>
</div>
<div class="mb-2">
<label for="date">Description <Notebook class="inline-block -mt-1 mb-1 w-6 h-6" /></label
<label for="date"
>{$t('adventures.description')}
<Notebook class="inline-block -mt-1 mb-1 w-6 h-6" /></label
><br />
<div class="flex">
<input
@ -164,9 +169,10 @@
</div>
<div class="mb-2">
<label for="start_date"
>{transportationToEdit.date ? 'Start ' : ''}Date & Time <Calendar
class="inline-block mb-1 w-6 h-6"
/></label
>{transportationToEdit.date ? `${$t('transportation.start')} ` : ''}{$t(
'transportation.date_and_time'
)}
<Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="datetime-local"
@ -181,7 +187,8 @@
{#if transportationToEdit.date}
<div class="mb-2">
<label for="end_date"
>End Date & Time <Calendar class="inline-block mb-1 w-6 h-6" /></label
>{$t('transportation.end_date_time')}
<Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="datetime-local"
@ -195,7 +202,9 @@
</div>
{/if}
<div class="mb-2">
<label for="rating">Rating <Star class="inline-block mb-1 w-6 h-6" /></label><br />
<label for="rating"
>{$t('adventures.rating')} <Star class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="number"
max="5"
@ -207,7 +216,9 @@
/>
</div>
<div class="mb-2">
<label for="rating">Link <LinkVariant class="inline-block mb-1 w-6 h-6" /></label><br />
<label for="rating"
>{$t('adventures.link')} <LinkVariant class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="url"
id="link"
@ -220,7 +231,8 @@
{#if transportationToEdit.type == 'plane'}
<div class="mb-2">
<label for="flight_number"
>Flight Number <Airplane class="inline-block mb-1 w-6 h-6" /></label
>{$t('transportation.flight_number')}
<Airplane class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="text"
@ -232,7 +244,9 @@
</div>
{/if}
<div class="mb-2">
<label for="rating">From Location <MapMarker class="inline-block mb-1 w-6 h-6" /></label
<label for="rating"
>{$t('transportation.from_location')}
<MapMarker class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="text"
@ -243,7 +257,9 @@
/>
</div>
<div class="mb-2">
<label for="rating">To Location <MapMarker class="inline-block mb-1 w-6 h-6" /></label
<label for="rating"
>{$t('transportation.to_location')}
<MapMarker class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="text"
@ -255,9 +271,9 @@
</div>
</div>
<button type="submit" class="btn btn-primary mr-4 mt-4">Edit</button>
<button type="submit" class="btn btn-primary mr-4 mt-4">{$t('transportation.edit')}</button>
<!-- 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}>{$t('about.close')}</button>
</form>
<div class="flex items-center justify-center flex-wrap gap-4 mt-4"></div>
</div>

View file

@ -2,7 +2,7 @@
import { createEventDispatcher } from 'svelte';
import type { Adventure, Collection } from '$lib/types';
import { onMount } from 'svelte';
import { enhance } from '$app/forms';
import { t } from 'svelte-i18n';
import { addToast } from '$lib/toasts';
import Calendar from '~icons/mdi/calendar';
@ -45,18 +45,18 @@
// make sure that start_date is before end_date
if (new Date(newCollection.start_date ?? '') > new Date(newCollection.end_date ?? '')) {
addToast('error', 'Start date must be before end date');
addToast('error', $t('adventures.start_before_end_error'));
return;
}
// make sure end date has a start date
if (newCollection.end_date && !newCollection.start_date) {
addToast('error', 'Please provide a start date');
addToast('error', $t('adventures.no_start_date'));
return;
}
if (newCollection.start_date && !newCollection.end_date) {
addToast('error', 'Please provide an end date');
addToast('error', $t('adventures.no_end_date'));
return;
}
@ -80,10 +80,10 @@
newCollection.user_id = user_id;
console.log(newCollection);
dispatch('create', newCollection);
addToast('success', 'Collection created successfully!');
addToast('success', $t('collection.collection_created'));
close();
} else {
addToast('error', 'Error creating collection');
addToast('error', $t('collection.error_creating_collection'));
}
}
}
@ -95,7 +95,7 @@
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
<h3 class="font-bold text-lg">New Collection</h3>
<h3 class="font-bold text-lg">{$t('collection.new_collection')}</h3>
<div
class="modal-action items-center"
style="display: flex; flex-direction: column; align-items: center; width: 100%;"
@ -107,7 +107,7 @@
action="/collections?/create"
>
<div class="mb-2">
<label for="name">Name</label><br />
<label for="name">{$t('adventures.name')}</label><br />
<input
type="text"
id="name"
@ -119,7 +119,9 @@
</div>
<div class="mb-2">
<label for="description"
>Description<iconify-icon icon="mdi:notebook" class="text-lg ml-1 -mb-0.5"
>$t('adventures.description')<iconify-icon
icon="mdi:notebook"
class="text-lg ml-1 -mb-0.5"
></iconify-icon></label
><br />
<div class="flex">
@ -132,7 +134,8 @@
/>
</div>
<div class="mb-2">
<label for="start_date">Start Date <Calendar class="inline-block mb-1 w-6 h-6" /></label
<label for="start_date"
>{$t('adventures.start_date')} <Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="date"
@ -143,8 +146,9 @@
/>
</div>
<div class="mb-2">
<label for="end_date">End Date <Calendar class="inline-block mb-1 w-6 h-6" /></label><br
/>
<label for="end_date"
>{$t('adventures.end_date')} <Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="date"
id="end_date"
@ -154,7 +158,7 @@
/>
</div>
<div class="mb-2">
<label for="end_date">Link </label><br />
<label for="end_date">{$t('adventures.link')} </label><br />
<input
type="url"
id="link"
@ -164,8 +168,10 @@
/>
</div>
<div class="mb-2">
<button type="submit" class="btn btn-primary mr-4 mt-4">Create</button>
<button type="button" class="btn mt-4" on:click={close}>Close</button>
<button type="submit" class="btn btn-primary mr-4 mt-4">
{$t('collection.create')}
</button>
<button type="button" class="btn mt-4" on:click={close}>{$t('about.close')}</button>
</div>
</div>
</form>

View file

@ -1,27 +1,11 @@
<script lang="ts">
// let newTransportation: Transportation = {
// id:NaN,
// user_id: NaN,
// type: '',
// name: '',
// description: null,
// rating: NaN,
// link: null,
// date: null,
// flight_number: null,
// from_location: null,
// to_location: null,
// is_public: false,
// collection: null,
// created_at: '',
// updated_at: ''
// };
import { createEventDispatcher } from 'svelte';
import type { Collection, Transportation } from '$lib/types';
const dispatch = createEventDispatcher();
import { onMount } from 'svelte';
import { addToast } from '$lib/toasts';
let modal: HTMLDialogElement;
import { t } from 'svelte-i18n';
export let collection: Collection;
@ -74,7 +58,7 @@
// make sure there is a start date if there is an end date
if (formData.get('end_date') && !formData.get('date')) {
addToast('error', 'Please provide a start date');
addToast('error', $t('transportation.provide_start_date'));
return;
}
@ -86,11 +70,11 @@
if (response.ok) {
const result = await response.json();
addToast('success', 'Transportation added successfully!');
addToast('success', $t('transportation.transportation_added'));
dispatch('add', result);
close();
} else {
addToast('error', 'Error editing transportation');
addToast('error', $t('transportation.error_editing_transportation'));
}
}
</script>
@ -99,22 +83,13 @@
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
<h3 class="font-bold text-lg">New Transportation</h3>
<h3 class="font-bold text-lg">{$t('transportation.new_transportation')}</h3>
<div
class="modal-action items-center"
style="display: flex; flex-direction: column; align-items: center; width: 100%;"
>
<form method="post" style="width: 100%;" on:submit={handleSubmit}>
<div class="mb-2">
<!-- <input
type="text"
id="id"
name="id"
hidden
readonly
bind:value={newTransportation.id}
class="input input-bordered w-full max-w-xs mt-1"
/> -->
<input
type="text"
id="collection"
@ -134,26 +109,28 @@
class="input input-bordered w-full max-w-xs mt-1"
/>
<div class="mb-2">
<label for="type">Type <PlaneCar class="inline-block mb-1 w-6 h-6" /></label><br />
<label for="type"
>{$t('transportation.type')} <PlaneCar class="inline-block mb-1 w-6 h-6" /></label
><br />
<select
class="select select-bordered w-full max-w-xs"
name="type"
id="type"
bind:value={type}
>
<option disabled selected>Transport Type</option>
<option value="car">Car</option>
<option value="plane">Plane</option>
<option value="train">Train</option>
<option value="bus">Bus</option>
<option value="boat">Boat</option>
<option value="bike">Bike</option>
<option value="walking">Walking</option>
<option value="other">Other</option>
<option disabled selected>{$t('transportation.type')}</option>
<option value="car">{$t('transportation.modes.car')}</option>
<option value="plane">{$t('transportation.modes.plane')}</option>
<option value="train">{$t('transportation.modes.train')}</option>
<option value="bus">{$t('transportation.modes.bus')}</option>
<option value="boat">{$t('transportation.modes.boat')}</option>
<option value="bike">{$t('transportation.modes.bike')}</option>
<option value="walking">{$t('transportation.modes.walking')}</option>
<option value="other">{$t('transportation.modes.other')}</option>
</select>
</div>
<label for="name">Name</label><br />
<label for="name">{$t('adventures.name')}</label><br />
<input
type="text"
name="name"
@ -162,7 +139,9 @@
/>
</div>
<div class="mb-2">
<label for="date">Description <Notebook class="inline-block -mt-1 mb-1 w-6 h-6" /></label
<label for="date"
>{$t('adventures.description')}
<Notebook class="inline-block -mt-1 mb-1 w-6 h-6" /></label
><br />
<div class="flex">
<input
@ -174,7 +153,8 @@
</div>
<div class="mb-2">
<label for="start_date"
>Start Date & Time <Calendar class="inline-block mb-1 w-6 h-6" /></label
>{$t('transportation.date_time')}
<Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="datetime-local"
@ -188,7 +168,8 @@
<div class="mb-2">
<label for="end_date"
>End Date & Time <Calendar class="inline-block mb-1 w-6 h-6" /></label
>{$t('transportation.end_date_time')}
<Calendar class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="datetime-local"
@ -201,7 +182,9 @@
</div>
<div class="mb-2">
<label for="rating">Rating <Star class="inline-block mb-1 w-6 h-6" /></label><br />
<label for="rating"
>{$t('adventures.rating')} <Star class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="number"
max="5"
@ -212,7 +195,9 @@
/>
</div>
<div class="mb-2">
<label for="rating">Link <LinkVariant class="inline-block mb-1 w-6 h-6" /></label><br />
<label for="rating"
>{$t('adventures.link')} <LinkVariant class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="url"
id="link"
@ -224,7 +209,8 @@
{#if type == 'plane'}
<div class="mb-2">
<label for="flight_number"
>Flight Number <Airplane class="inline-block mb-1 w-6 h-6" /></label
>{$t('transportation.flight_number')}
<Airplane class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="text"
@ -235,7 +221,9 @@
</div>
{/if}
<div class="mb-2">
<label for="rating">From Location <MapMarker class="inline-block mb-1 w-6 h-6" /></label
<label for="rating"
>{$t('transportation.from_location')}
<MapMarker class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="text"
@ -245,7 +233,9 @@
/>
</div>
<div class="mb-2">
<label for="rating">To Location <MapMarker class="inline-block mb-1 w-6 h-6" /></label
<label for="rating"
>{$t('transportation.to_location')}
<MapMarker class="inline-block mb-1 w-6 h-6" /></label
><br />
<input
type="text"
@ -256,9 +246,9 @@
</div>
</div>
<button type="submit" class="btn btn-primary mr-4 mt-4">Edit</button>
<button type="submit" class="btn btn-primary mr-4 mt-4">{$t('transportation.edit')}</button>
<!-- 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}>{$t('about.close')}</button>
</form>
<div class="flex items-center justify-center flex-wrap gap-4 mt-4"></div>
</div>

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { t } from 'svelte-i18n';
import { addToast } from '$lib/toasts';
import type { Collection, Note, User } from '$lib/types';
import { createEventDispatcher } from 'svelte';
@ -22,10 +22,10 @@
method: 'DELETE'
});
if (res.ok) {
addToast('success', 'Note deleted successfully');
addToast('success', $t('notes.note_deleted'));
dispatch('delete', note.id);
} else {
addToast('Failed to delete note', 'error');
addToast($t('notes.note_delete_error'), 'error');
}
}
</script>
@ -39,9 +39,12 @@
{note.name}
</h2>
</div>
<div class="badge badge-primary">Note</div>
<div class="badge badge-primary">{$t('adventures.note')}</div>
{#if note.links && note.links.length > 0}
<p>{note.links.length} {note.links.length > 1 ? 'Links' : 'Link'}</p>
<p>
{note.links.length}
{note.links.length > 1 ? $t('adventures.links') : $t('adventures.link')}
</p>
{/if}
{#if note.date && note.date !== ''}
<div class="inline-flex items-center">
@ -54,7 +57,7 @@
><Launch class="w-6 h-6" />Open Details</button
> -->
<button class="btn btn-neutral-200 mb-2" on:click={editNote}>
<Launch class="w-6 h-6" />Open
<Launch class="w-6 h-6" />{$t('notes.open')}
</button>
{#if note.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))}
<button

View file

@ -4,7 +4,7 @@
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
import { onMount } from 'svelte';
import ShareModal from './ShareModal.svelte';
import { t } from 'svelte-i18n';
let modal: HTMLDialogElement;
export let note: Note | null = null;
@ -98,8 +98,7 @@
}
} else {
let data = await res.json();
console.error('Failed to save note', data);
console.error('Failed to save note');
console.error($t('notes.failed_to_save'), data);
}
}
}
@ -109,15 +108,15 @@
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
<h3 class="font-bold text-lg">Note Editor</h3>
<h3 class="font-bold text-lg">{$t('notes.note_editor')}</h3>
{#if initialName}
<p class="font-semibold text-md mb-2">Editing note {initialName}</p>
<p class="font-semibold text-md mb-2">{$t('notes.editing_note')} {initialName}</p>
{/if}
{#if (note && user?.pk == note?.user_id) || (collection && user && collection.shared_with.includes(user.uuid)) || !note}
<form on:submit|preventDefault>
<div class="form-control mb-2">
<label for="name">Name</label>
<label for="name">{$t('adventures.name')}</label>
<input
type="text"
id="name"
@ -126,7 +125,7 @@
/>
</div>
<div class="form-control mb-2">
<label for="content">Date</label>
<label for="content">{$t('adventures.date')}</label>
<input
type="date"
id="date"
@ -138,7 +137,7 @@
/>
</div>
<div class="form-control mb-2">
<label for="content">Content</label>
<label for="content">{$t('notes.content')}</label>
<textarea
id="content"
class="textarea textarea-bordered"
@ -147,11 +146,11 @@
></textarea>
</div>
<div class="form-control mb-2">
<label for="content">Links</label>
<label for="content">{$t('adventures.links')}</label>
<input
type="url"
class="input input-bordered w-full mb-1"
placeholder="Add a link (e.g. https://example.com)"
placeholder="${$t('notes.add_a_link')} (e.g. https://example.com)"
bind:value={newLink}
on:keydown={(e) => {
if (e.key === 'Enter') {
@ -160,7 +159,9 @@
}
}}
/>
<button type="button" class="btn btn-sm btn-primary" on:click={addLink}>Add</button>
<button type="button" class="btn btn-sm btn-primary" on:click={addLink}
>{$t('adventures.add')}</button
>
</div>
{#if newNote.links.length > 0}
<ul class="list-none">
@ -174,7 +175,7 @@
newNote.links = newNote.links.filter((_, index) => index !== i);
}}
>
Remove
{$t('adventures.remove')}
</button>
</li>
{/each}
@ -200,8 +201,8 @@
</div>
{/if}
<button class="btn btn-primary mr-1" on:click={save}>Save</button>
<button class="btn btn-neutral" on:click={close}>Close</button>
<button class="btn btn-primary mr-1" on:click={save}>{$t('notes.save')}</button>
<button class="btn btn-neutral" on:click={close}>{$t('about.close')}</button>
{#if collection.is_public}
<div role="alert" class="alert mt-4">
@ -218,14 +219,14 @@
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span>This note is public because it is in a public collection.</span>
<span>{$t('notes.note_public')}</span>
</div>
{/if}
</form>
{:else}
<form>
<div class="form-control mb-2">
<label for="name">Name</label>
<label for="name">{$t('adventures.public')}</label>
<input
type="text"
id="name"
@ -235,7 +236,7 @@
/>
</div>
<div class="form-control mb-2">
<label for="content">Date</label>
<label for="content">{$t('adventures.date')}</label>
<input
type="date"
id="date"
@ -248,7 +249,7 @@
/>
</div>
<div class="form-control mb-2">
<label for="content">Content</label>
<label for="content">{$t('notes.content')}</label>
<textarea
id="content"
class="textarea textarea-bordered"
@ -258,7 +259,7 @@
></textarea>
</div>
<div class="form-control mb-2">
<label for="content">Links</label>
<label for="content">{$t('adventures.links')}</label>
</div>
{#if newNote.links.length > 0}
<ul class="list-none">
@ -270,7 +271,7 @@
</ul>
{/if}
<button class="btn btn-neutral" on:click={close}>Close</button>
<button class="btn btn-neutral" on:click={close}>{$t('about.close')}</button>
</form>
{/if}
</div>

View file

@ -4,6 +4,7 @@
import FileDocumentEdit from '~icons/mdi/file-document-edit';
import type { Collection, Transportation, User } from '$lib/types';
import { addToast } from '$lib/toasts';
import { t } from 'svelte-i18n';
import ArrowDownThick from '~icons/mdi/arrow-down-thick';
@ -25,10 +26,9 @@
}
});
if (!res.ok) {
console.log('Error deleting transportation');
console.log($t('transportation.transportation_delete_error'));
} else {
console.log('Collection deleted');
addToast('info', 'Transportation deleted successfully!');
addToast('info', $t('transportation.transportation_deleted'));
dispatch('delete', transportation.id);
}
}
@ -39,7 +39,7 @@
>
<div class="card-body">
<h2 class="card-title overflow-ellipsis">{transportation.name}</h2>
<div class="badge badge-secondary">{transportation.type}</div>
<div class="badge badge-secondary">{$t(`transportation.modes.${transportation.type}`)}</div>
<div>
{#if transportation.from_location}
<p class="break-words text-wrap">{transportation.from_location}</p>

View file

@ -79,7 +79,6 @@
"count_txt": "results matching your search",
"sort": "Sort",
"order_direction": "Order Direction",
"order_by": "Order By",
"ascending": "Ascending",
"descending": "Descending",
"updated": "Updated",
@ -119,6 +118,7 @@
"public_adventure": "Public Adventure",
"location_information": "Location Information",
"link": "Link",
"links": "Links",
"description": "Description",
"sources": "Sources",
"collection_adventures": "Include Collection Adventures",
@ -162,6 +162,7 @@
"start_before_end_error": "Start date must be before end date",
"activity": "Activity",
"actions": "Actions",
"no_end_date": "Please enter an end date",
"see_adventures": "See Adventures",
"image_fetch_failed": "Failed to fetch image",
"no_location": "Please enter a location",
@ -183,6 +184,23 @@
"visited_region_check_desc": "By selecting this, the server will check all of your visited adventures and mark the regions they are located in as visited in world travel.",
"update_visited_regions": "Update Visited Regions",
"update_visited_regions_disclaimer": "This may take a while depending on the number of adventures you have visited.",
"link_new": "Link New...",
"add_new": "Add New...",
"transportation": "Transportation",
"note": "Note",
"checklist": "Checklist",
"collection_archived": "This collection has been archived.",
"visit_link": "Visit Link",
"collection_completed": "You've completed this collection!",
"collection_stats": "Collection Stats",
"keep_exploring": "Keep Exploring!",
"linked_adventures": "Linked Adventures",
"notes": "Notes",
"checklists": "Checklists",
"transportations": "Transportations",
"itineary_by_date": "Itinerary by Date",
"nothing_planned": "Nothing planned for this day. Enjoy the journey!<",
"days": "days",
"activities": {
"general": "General 🌍",
"outdoor": "Outdoor 🏞️",
@ -261,5 +279,71 @@
"password_does_not_match": "Passwords do not match",
"password_is_required": "Password is required",
"invalid_token": "Token is invalid or has expired"
},
"collection": {
"collection_created": "Collection created successfully!",
"error_creating_collection": "Error creating collection",
"new_collection": "New Collection",
"create": "Create",
"collection_edit_success": "Collection edited successfully!",
"error_editing_collection": "Error editing collection",
"edit_collection": "Edit Collection"
},
"notes": {
"note_deleted": "Note deleted successfully!",
"note_delete_error": "Error deleting note",
"open": "Open",
"failed_to_save": "Failed to save note",
"note_editor": "Note Editor",
"editing_note": "Editing note",
"content": "Content",
"save": "Save",
"note_public": "This note is public because it is in a public collection.",
"add_a_link": "Add a link"
},
"checklist": {
"checklist_deleted": "Checklist deleted successfully!",
"checklist_delete_error": "Error deleting checklist",
"failed_to_save": "Failed to save checklist",
"checklist_editor": "Checklist Editor",
"editing_checklist": "Editing checklist",
"item": "Item",
"items": "Items",
"add_item": "Add Item",
"new_item": "New Item",
"save": "Save",
"checklist_public": "This checklist is public because it is in a public collection.",
"item_cannot_be_empty": "Item cannot be empty",
"item_already_exists": "Item already exists"
},
"transportation": {
"transportation_deleted": "Transportation deleted successfully!",
"transportation_delete_error": "Error deleting transportation",
"provide_start_date": "Please provide a start date",
"transport_type": "Transport Type",
"type": "Type",
"transportation_added": "Transportation added successfully!",
"error_editing_transportation": "Error editing transportation",
"new_transportation": "New Transportation",
"date_time": "Start Date & Time",
"end_date_time": "End Date & Time",
"flight_number": "Flight Number",
"from_location": "From Location",
"to_location": "To Location",
"edit": "Edit",
"modes": {
"car": "Car",
"plane": "Plane",
"train": "Train",
"bus": "Bus",
"boat": "Boat",
"bike": "Bike",
"walking": "Walking",
"other": "Other"
},
"transportation_edit_success": "Transportation edited successfully!",
"edit_transportation": "Edit Transportation",
"start": "Start",
"date_and_time": "Date & Time"
}
}

View file

@ -4,6 +4,7 @@
import type { PageData } from './$types';
import { goto } from '$app/navigation';
import Lost from '$lib/assets/undraw_lost.svg';
import { t } from 'svelte-i18n';
import Plus from '~icons/mdi/plus';
import AdventureCard from '$lib/components/AdventureCard.svelte';
@ -106,19 +107,6 @@
}
}
function changeType(event: CustomEvent<string>) {
adventures = adventures.map((adventure) => {
if (adventure.id == event.detail) {
if (adventure.type == 'visited') {
adventure.type = 'planned';
} else {
adventure.type = 'visited';
}
}
return adventure;
});
}
let adventureToEdit: Adventure | null = null;
let transportationToEdit: Transportation;
let isAdventureModalOpen: boolean = false;
@ -255,14 +243,15 @@
<img src={Lost} alt="Lost" class="w-1/2" />
</div>
<h1 class="mt-4 text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
Adventure not Found
{$t('adventures.not_found')}
</h1>
<p class="mt-4 text-muted-foreground">
The adventure you were looking for could not be found. Please try a different adventure or
check back later.
{$t('adventures.not_found_desc')}
</p>
<div class="mt-6">
<button class="btn btn-primary" on:click={() => goto('/')}>Homepage</button>
<button class="btn btn-primary" on:click={() => goto('/')}
>{$t('adventures.homepage')}</button
>
</div>
</div>
</div>
@ -287,17 +276,17 @@
class="dropdown-content z-[1] menu p-4 shadow bg-base-300 text-base-content rounded-box w-52 gap-4"
>
{#if collection.user_id === data.user.pk}
<p class="text-center font-bold text-lg">Link new...</p>
<p class="text-center font-bold text-lg">{$t('adventures.link_new')}</p>
<button
class="btn btn-primary"
on:click={() => {
isShowingLinkModal = true;
}}
>
Adventure</button
{$t('adventures.adventure')}</button
>
{/if}
<p class="text-center font-bold text-lg">Add new...</p>
<p class="text-center font-bold text-lg">{$t('adventures.add_new')}</p>
<button
class="btn btn-primary"
on:click={() => {
@ -305,7 +294,7 @@
adventureToEdit = null;
}}
>
Adventure</button
{$t('adventures.adventure')}</button
>
<button
@ -315,7 +304,7 @@
newType = '';
}}
>
Transportation</button
{$t('adventures.transportation')}</button
>
<button
class="btn btn-primary"
@ -325,7 +314,7 @@
noteToEdit = null;
}}
>
Note</button
{$t('adventures.note')}</button
>
<button
class="btn btn-primary"
@ -335,7 +324,7 @@
checklistToEdit = null;
}}
>
Checklist</button
{$t('adventures.checklist')}</button
>
<!-- <button
@ -363,7 +352,7 @@
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<span>This collection has been archived.</span>
<span>{$t('adventures.collection_archived')}</span>
</div>
</div>
{/if}
@ -373,7 +362,7 @@
{#if collection.link}
<div class="flex items-center justify-center mb-2">
<a href={collection.link} target="_blank" rel="noopener noreferrer" class="btn btn-primary">
Visit Link
{$t('adventures.visit_link')}
</a>
</div>
{/if}
@ -385,12 +374,12 @@
<div class="flex items-center justify-center mb-4">
<div class="stats shadow bg-base-300">
<div class="stat">
<div class="stat-title">Collection Stats</div>
<div class="stat-title">{$t('adventures.collection_stats')}</div>
<div class="stat-value">{numVisited}/{numAdventures} Visited</div>
{#if numAdventures === numVisited}
<div class="stat-desc">You've completed this collection! 🎉!</div>
<div class="stat-desc">{$t('adventures.collection_completed')}</div>
{:else}
<div class="stat-desc">Keep exploring!</div>
<div class="stat-desc">{$t('adventures.keep_exploring')}</div>
{/if}
</div>
</div>
@ -401,7 +390,7 @@
<NotFound error={undefined} />
{/if}
{#if adventures.length > 0}
<h1 class="text-center font-bold text-4xl mt-4 mb-2">Linked Adventures</h1>
<h1 class="text-center font-bold text-4xl mt-4 mb-2">{$t('adventures.linked_adventures')}</h1>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each adventures as adventure}
@ -411,7 +400,6 @@
on:delete={deleteAdventure}
type={adventure.type}
{adventure}
on:typeChange={changeType}
{collection}
/>
{/each}
@ -419,7 +407,7 @@
{/if}
{#if transportations.length > 0}
<h1 class="text-center font-bold text-4xl mt-4 mb-4">Transportation</h1>
<h1 class="text-center font-bold text-4xl mt-4 mb-4">{$t('adventures.transportations')}</h1>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each transportations as transportation}
<TransportationCard
@ -439,7 +427,7 @@
{/if}
{#if notes.length > 0}
<h1 class="text-center font-bold text-4xl mt-4 mb-4">Notes</h1>
<h1 class="text-center font-bold text-4xl mt-4 mb-4">{$t('adventures.notes')}</h1>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each notes as note}
<NoteCard
@ -459,7 +447,7 @@
{/if}
{#if checklists.length > 0}
<h1 class="text-center font-bold text-4xl mt-4 mb-4">Checklists</h1>
<h1 class="text-center font-bold text-4xl mt-4 mb-4">{$t('adventures.checklists')}</h1>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each checklists as checklist}
<ChecklistCard
@ -480,9 +468,12 @@
{#if collection.start_date && collection.end_date}
<div class="divider"></div>
<h1 class="text-center font-bold text-4xl mt-4">Itinerary by Date</h1>
<h1 class="text-center font-bold text-4xl mt-4">{$t('adventures.itineary_by_date')}</h1>
{#if numberOfDays}
<p class="text-center text-lg pl-16 pr-16">Duration: {numberOfDays} days</p>
<p class="text-center text-lg pl-16 pr-16">
{$t('adventures.duration')}: {numberOfDays}
{$t('adventures.days')}
</p>
{/if}
<p class="text-center text-lg pl-16 pr-16">
Dates: {new Date(collection.start_date).toLocaleDateString(undefined, { timeZone: 'UTC' })} - {new Date(
@ -531,7 +522,6 @@
on:delete={deleteAdventure}
type={adventure.type}
{adventure}
on:typeChange={changeType}
/>
{/each}
{/if}
@ -582,7 +572,7 @@
{/if}
{#if dayAdventures.length == 0 && dayTransportations.length == 0 && dayNotes.length == 0 && dayChecklists.length == 0}
<p class="text-center text-lg mt-2">Nothing planned for this day. Enjoy the journey!</p>
<p class="text-center text-lg mt-2">{$t('adventures.nothing_planned')}</p>
{/if}
</div>
{/each}
@ -605,11 +595,6 @@
<p class="font-semibold text-black text-md">
{adventure.type.charAt(0).toUpperCase() + adventure.type.slice(1)}
</p>
<!-- <p>
{adventure.
? new Date(adventure.date).toLocaleDateString(undefined, { timeZone: 'UTC' })
: ''}
</p> -->
</Popup>
</DefaultMarker>
{/if}
@ -622,7 +607,7 @@
<title
>{data.props.adventure && data.props.adventure.name
? `${data.props.adventure.name}`
: 'Collection'}</title
: $t('adventures.collection')}</title
>
<meta
name="description"