mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-19 04:49:37 +02:00
feat: enhance CategoryModal with add/edit functionality and improve localization support
This commit is contained in:
parent
c0f2d060db
commit
06a5bb06b3
13 changed files with 359 additions and 159 deletions
|
@ -1,149 +1,353 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Category } from '$lib/types';
|
import type { Category } from '$lib/types';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
let modal: HTMLDialogElement;
|
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
let modal: HTMLDialogElement;
|
||||||
|
|
||||||
export let categories: Category[] = [];
|
export let categories: Category[] = [];
|
||||||
|
|
||||||
let category_to_edit: Category | null = null;
|
let categoryToEdit: Category | null = null;
|
||||||
|
let newCategory = { display_name: '', icon: '' };
|
||||||
let is_changed: boolean = false;
|
let showAddForm = false;
|
||||||
|
let isChanged = false;
|
||||||
let has_loaded: boolean = false;
|
let hasLoaded = false;
|
||||||
|
let warningMessage: string | null = null;
|
||||||
|
let showEmojiPickerAdd = false;
|
||||||
|
let showEmojiPickerEdit = false;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
modal = document.getElementById('my_modal_1') as HTMLDialogElement;
|
await import('emoji-picker-element');
|
||||||
if (modal) {
|
modal = document.querySelector('#category-modal') as HTMLDialogElement;
|
||||||
modal.showModal();
|
modal.showModal();
|
||||||
}
|
await loadCategories();
|
||||||
let category_fetch = await fetch('/api/categories/categories');
|
|
||||||
categories = await category_fetch.json();
|
|
||||||
has_loaded = true;
|
|
||||||
// remove the general category if it exists
|
|
||||||
// categories = categories.filter((c) => c.name !== 'general');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function saveCategory() {
|
async function loadCategories() {
|
||||||
if (category_to_edit) {
|
try {
|
||||||
let edit_fetch = await fetch(`/api/categories/${category_to_edit.id}`, {
|
const res = await fetch('/api/categories/categories');
|
||||||
method: 'PUT',
|
if (res.ok) {
|
||||||
headers: {
|
categories = await res.json();
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(category_to_edit)
|
|
||||||
});
|
|
||||||
if (edit_fetch.ok) {
|
|
||||||
category_to_edit = null;
|
|
||||||
let the_category = (await edit_fetch.json()) as Category;
|
|
||||||
categories = categories.map((c) => {
|
|
||||||
if (c.id === the_category.id) {
|
|
||||||
return the_category;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
});
|
|
||||||
is_changed = true;
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to load categories:', err);
|
||||||
|
} finally {
|
||||||
|
hasLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function closeModal() {
|
||||||
dispatch('close');
|
dispatch('close');
|
||||||
|
modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(event: KeyboardEvent) {
|
function handleKeydown(event: KeyboardEvent) {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
dispatch('close');
|
closeModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCategory(category: Category) {
|
function handleEmojiSelectAdd(event: CustomEvent) {
|
||||||
return async () => {
|
newCategory.icon = event.detail.unicode;
|
||||||
let response = await fetch(`/api/categories/${category.id}`, {
|
showEmojiPickerAdd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEmojiSelectEdit(event: CustomEvent) {
|
||||||
|
if (categoryToEdit) {
|
||||||
|
categoryToEdit.icon = event.detail.unicode;
|
||||||
|
}
|
||||||
|
showEmojiPickerEdit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createCategory(event: Event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const nameTrimmed = newCategory.display_name.trim();
|
||||||
|
if (!nameTrimmed) {
|
||||||
|
warningMessage = $t('categories.name_required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
warningMessage = null;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
display_name: nameTrimmed,
|
||||||
|
name: nameTrimmed
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '_')
|
||||||
|
.replace(/[^a-z0-9_]/g, ''),
|
||||||
|
icon: newCategory.icon.trim() || '🌍'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/categories', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
const created = await res.json();
|
||||||
|
categories = [...categories, created];
|
||||||
|
isChanged = true;
|
||||||
|
newCategory = { display_name: '', icon: '' };
|
||||||
|
showAddForm = false;
|
||||||
|
showEmojiPickerAdd = false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to create category:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCategory(event: Event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!categoryToEdit) return;
|
||||||
|
|
||||||
|
const nameTrimmed = categoryToEdit.display_name.trim();
|
||||||
|
if (!nameTrimmed) {
|
||||||
|
warningMessage = $t('categories.name_required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
warningMessage = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/categories/${categoryToEdit.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ ...categoryToEdit, display_name: nameTrimmed })
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
const updated = await res.json();
|
||||||
|
categories = categories.map((c) => (c.id === updated.id ? updated : c));
|
||||||
|
categoryToEdit = null;
|
||||||
|
isChanged = true;
|
||||||
|
showEmojiPickerEdit = false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to save category:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEdit(category: Category) {
|
||||||
|
categoryToEdit = { ...category };
|
||||||
|
showAddForm = false;
|
||||||
|
showEmojiPickerAdd = false;
|
||||||
|
showEmojiPickerEdit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEdit() {
|
||||||
|
categoryToEdit = null;
|
||||||
|
showEmojiPickerEdit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeCategory(category: Category) {
|
||||||
|
if (category.name === 'general') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/categories/${category.id}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
if (response.ok) {
|
if (res.ok) {
|
||||||
categories = categories.filter((c) => c.id !== category.id);
|
categories = categories.filter((c) => c.id !== category.id);
|
||||||
is_changed = true;
|
isChanged = true;
|
||||||
}
|
}
|
||||||
};
|
} catch (err) {
|
||||||
|
console.error('Failed to delete category:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<dialog id="my_modal_1" class="modal">
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
<dialog id="category-modal" class="modal" on:keydown={handleKeydown}>
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
<div class="modal-box max-w-2xl">
|
||||||
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
|
<!-- Header -->
|
||||||
<h3 class="font-bold text-lg">{$t('categories.manage_categories')}</h3>
|
<div class="flex items-center justify-between mb-6">
|
||||||
|
<h2 class="text-xl font-bold">{$t('categories.manage_categories')}</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={closeModal}
|
||||||
|
class="btn btn-sm btn-circle btn-ghost"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if has_loaded}
|
<!-- Category List -->
|
||||||
{#each categories as category}
|
{#if hasLoaded}
|
||||||
<div class="flex justify-between items-center mt-2">
|
{#if categories.length > 0}
|
||||||
<span>{category.display_name} {category.icon}</span>
|
<div class="space-y-2 mb-6">
|
||||||
<div class="flex space-x-2">
|
{#each categories as category (category.id)}
|
||||||
<button on:click={() => (category_to_edit = category)} class="btn btn-primary btn-sm"
|
<div class="flex items-center justify-between p-3 bg-base-200 rounded-lg">
|
||||||
>Edit</button
|
<div class="flex items-center gap-3">
|
||||||
>
|
<span class="text-lg">{category.icon || '🌍'}</span>
|
||||||
{#if category.name != 'general'}
|
<span class="font-medium">{category.display_name}</span>
|
||||||
<button on:click={removeCategory(category)} class="btn btn-warning btn-sm"
|
</div>
|
||||||
>{$t('adventures.remove')}</button
|
<div class="flex gap-2">
|
||||||
>
|
<button
|
||||||
{:else}
|
type="button"
|
||||||
<button class="btn btn-warning btn-sm btn-disabled">{$t('adventures.remove')}</button>
|
on:click={() => startEdit(category)}
|
||||||
{/if}
|
class="btn btn-xs btn-outline"
|
||||||
</div>
|
>
|
||||||
|
{$t('lodging.edit')}
|
||||||
|
</button>
|
||||||
|
{#if category.name !== 'general'}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={() => removeCategory(category)}
|
||||||
|
class="btn btn-xs btn-error btn-outline"
|
||||||
|
>
|
||||||
|
{$t('adventures.remove')}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="text-center py-8 text-base-content/60">
|
||||||
|
{$t('categories.no_categories_found')}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
|
||||||
{#if categories.length === 0}
|
|
||||||
<p>{$t('categories.no_categories_found')}</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex items-center justify-center">
|
<div class="text-center py-8">
|
||||||
<span class="loading loading-spinner loading-lg m-4"></span>
|
<span class="loading loading-spinner loading-md"></span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if category_to_edit}
|
<!-- Edit Category Form -->
|
||||||
<h2 class="text-center text-xl font-semibold mt-2 mb-2">{$t('categories.edit_category')}</h2>
|
{#if categoryToEdit}
|
||||||
<div class="flex flex-row space-x-2 form-control">
|
<div class="bg-base-100 border border-base-300 rounded-lg p-4 mb-4">
|
||||||
<input
|
<h3 class="font-medium mb-4">{$t('categories.edit_category')}</h3>
|
||||||
type="text"
|
<form on:submit={saveCategory} class="space-y-4">
|
||||||
placeholder={$t('adventures.name')}
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
bind:value={category_to_edit.display_name}
|
<div>
|
||||||
class="input input-bordered w-full max-w-xs"
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||||
/>
|
<label class="label">
|
||||||
|
<span class="label-text">{$t('categories.category_name')}</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
bind:value={categoryToEdit.display_name}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">{$t('categories.icon')}</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered flex-1"
|
||||||
|
bind:value={categoryToEdit.icon}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={() => (showEmojiPickerEdit = !showEmojiPickerEdit)}
|
||||||
|
class="btn btn-square btn-outline"
|
||||||
|
>
|
||||||
|
😀
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input
|
{#if showEmojiPickerEdit}
|
||||||
type="text"
|
<div class="p-2 border rounded-lg bg-base-100">
|
||||||
placeholder={$t('categories.icon')}
|
<emoji-picker on:emoji-click={handleEmojiSelectEdit}></emoji-picker>
|
||||||
bind:value={category_to_edit.icon}
|
</div>
|
||||||
class="input input-bordered w-full max-w-xs"
|
{/if}
|
||||||
/>
|
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<button type="button" class="btn btn-ghost" on:click={cancelEdit}>
|
||||||
|
{$t('adventures.cancel')}
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary"> {$t('notes.save')} </button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" on:click={saveCategory}>{$t('notes.save')}</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button class="btn btn-primary mt-4" on:click={close}>{$t('about.close')}</button>
|
<!-- Add Category Section -->
|
||||||
|
<div class="collapse collapse-plus bg-base-200 mb-4">
|
||||||
|
<input type="checkbox" bind:checked={showAddForm} />
|
||||||
|
<div class="collapse-title font-medium">{$t('categories.add_new_category')}</div>
|
||||||
|
{#if showAddForm}
|
||||||
|
<div class="collapse-content">
|
||||||
|
<form on:submit={createCategory} class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">{$t('categories.category_name')}</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
bind:value={newCategory.display_name}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if is_changed}
|
<div>
|
||||||
<div role="alert" class="alert alert-info mt-6">
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||||
<svg
|
<label class="label">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<span class="label-text">{$t('categories.icon')}</span>
|
||||||
fill="none"
|
</label>
|
||||||
viewBox="0 0 24 24"
|
<div class="flex gap-2">
|
||||||
class="h-6 w-6 shrink-0 stroke-current"
|
<input
|
||||||
>
|
type="text"
|
||||||
<path
|
class="input input-bordered flex-1"
|
||||||
stroke-linecap="round"
|
bind:value={newCategory.icon}
|
||||||
stroke-linejoin="round"
|
placeholder="🌍"
|
||||||
stroke-width="2"
|
/>
|
||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
<button
|
||||||
></path>
|
type="button"
|
||||||
</svg>
|
on:click={() => (showEmojiPickerAdd = !showEmojiPickerAdd)}
|
||||||
|
class="btn btn-square btn-outline"
|
||||||
|
>
|
||||||
|
😀
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{#if showEmojiPickerAdd}
|
||||||
|
<div class="mt-2 p-2 border rounded-lg bg-base-100">
|
||||||
|
<emoji-picker on:emoji-click={handleEmojiSelectAdd}></emoji-picker>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary w-full">
|
||||||
|
{$t('collection.create')}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Messages -->
|
||||||
|
{#if warningMessage}
|
||||||
|
<div class="alert alert-warning mb-4">
|
||||||
|
<span>{warningMessage}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if isChanged}
|
||||||
|
<div class="alert alert-success mb-4">
|
||||||
<span>{$t('categories.update_after_refresh')}</span>
|
<span>{$t('categories.update_after_refresh')}</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button type="button" class="btn" on:click={closeModal}> {$t('about.close')} </button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.modal-box {
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -20,13 +20,6 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
let inputElement: HTMLInputElement | null = null;
|
let inputElement: HTMLInputElement | null = null;
|
||||||
|
|
||||||
let isScrolled = false;
|
|
||||||
|
|
||||||
// Handle scroll effect
|
|
||||||
function handleScroll() {
|
|
||||||
isScrolled = window.scrollY > 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
let theme = '';
|
let theme = '';
|
||||||
|
|
||||||
// Event listener for focusing input
|
// Event listener for focusing input
|
||||||
|
@ -44,13 +37,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
// Attach event listener on component mount
|
||||||
document.addEventListener('keydown', handleKeydown);
|
document.addEventListener('keydown', handleKeydown);
|
||||||
window.addEventListener('scroll', handleScroll);
|
|
||||||
theme = document.documentElement.getAttribute('data-theme') || '';
|
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
theme = document.documentElement.getAttribute('data-theme');
|
||||||
|
|
||||||
|
// Cleanup event listener on component destruction
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('keydown', handleKeydown);
|
document.removeEventListener('keydown', handleKeydown);
|
||||||
window.removeEventListener('scroll', handleScroll);
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -113,11 +108,7 @@
|
||||||
<AboutModal on:close={() => (isAboutModalOpen = false)} />
|
<AboutModal on:close={() => (isAboutModalOpen = false)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<nav
|
<div class="navbar bg-base-100">
|
||||||
class="navbar sticky top-0 z-50 transition-all duration-300 {isScrolled
|
|
||||||
? 'bg-base-100/80 backdrop-blur-lg shadow-lg border-b border-base-300/50'
|
|
||||||
: 'bg-base-100'}"
|
|
||||||
>
|
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<div class="dropdown z-50">
|
<div class="dropdown z-50">
|
||||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||||
|
@ -199,19 +190,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a href="/" class="btn btn-ghost hover:bg-transparent group">
|
<a class="btn btn-ghost p-0 text-2xl font-bold tracking-normal" href="/">
|
||||||
<div class="flex items-center space-x-3">
|
<span class="sm:inline hidden">AdventureLog</span>
|
||||||
<div class="relative">
|
<img src="/favicon.png" alt="Map Logo" class="w-10" />
|
||||||
<img
|
|
||||||
src="/favicon.png"
|
|
||||||
alt="AdventureLog"
|
|
||||||
class="w-10 h-10 transition-transform duration-300 group-hover:scale-110"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="absolute inset-0 bg-gradient-to-br from-blue-500/20 to-purple-500/20 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-center hidden lg:flex">
|
<div class="navbar-center hidden lg:flex">
|
||||||
|
@ -274,26 +255,19 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if data.user}
|
{#if data.user}
|
||||||
<form class="hidden md:flex relative group" on:submit={searchGo}>
|
<form class="flex gap-2">
|
||||||
<div class="relative">
|
<label class="input input-bordered flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={query}
|
bind:value={query}
|
||||||
bind:this={inputElement}
|
class="grow"
|
||||||
placeholder={$t('navbar.search')}
|
placeholder={$t('navbar.search')}
|
||||||
class="input input-bordered bg-neutral border-base-300 focus:border-primary focus:bg-base-100 transition-all duration-300 pr-20 w-64 group-focus-within:w-72"
|
bind:this={inputElement}
|
||||||
/>
|
/><kbd class="kbd">/</kbd>
|
||||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center space-x-2">
|
</label>
|
||||||
<kbd class="kbd kbd-sm opacity-60">/ </kbd>
|
<button on:click={searchGo} type="submit" class="btn btn-neutral flex items-center gap-1">
|
||||||
<button
|
<Magnify class="w-5 h-5" />
|
||||||
type="submit"
|
</button>
|
||||||
class="btn btn-sm btn-circle btn-ghost hover:btn-primary transition-all duration-200"
|
|
||||||
aria-label="Search"
|
|
||||||
>
|
|
||||||
<Magnify class="w-4 h-4 text-neutral-300" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -356,4 +330,4 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</div>
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "Kategorien verwalten",
|
"manage_categories": "Kategorien verwalten",
|
||||||
"no_categories_found": "Keine Kategorien gefunden.",
|
"no_categories_found": "Keine Kategorien gefunden.",
|
||||||
"select_category": "Kategorie wählen",
|
"select_category": "Kategorie wählen",
|
||||||
"update_after_refresh": "Die Abenteuerkarten werden aktualisiert, sobald Sie die Seite aktualisieren."
|
"update_after_refresh": "Die Abenteuerkarten werden aktualisiert, sobald Sie die Seite aktualisieren.",
|
||||||
|
"add_category": "Kategorie hinzufügen",
|
||||||
|
"add_new_category": "Neue Kategorie hinzufügen"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "Warum nicht gleich Ihr nächstes Abenteuer planen? Sie können ein neues Abenteuer hinzufügen, indem Sie auf den Button unten klicken.",
|
"add_some": "Warum nicht gleich Ihr nächstes Abenteuer planen? Sie können ein neues Abenteuer hinzufügen, indem Sie auf den Button unten klicken.",
|
||||||
|
|
|
@ -660,7 +660,9 @@
|
||||||
"icon": "Icon",
|
"icon": "Icon",
|
||||||
"update_after_refresh": "The adventure cards will be updated once you refresh the page.",
|
"update_after_refresh": "The adventure cards will be updated once you refresh the page.",
|
||||||
"select_category": "Select Category",
|
"select_category": "Select Category",
|
||||||
"category_name": "Category Name"
|
"category_name": "Category Name",
|
||||||
|
"add_category": "Add Category",
|
||||||
|
"add_new_category": "Add New Category"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"welcome_back": "Welcome back",
|
"welcome_back": "Welcome back",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "Administrar categorías",
|
"manage_categories": "Administrar categorías",
|
||||||
"no_categories_found": "No se encontraron categorías.",
|
"no_categories_found": "No se encontraron categorías.",
|
||||||
"select_category": "Seleccionar categoría",
|
"select_category": "Seleccionar categoría",
|
||||||
"update_after_refresh": "Las tarjetas de aventuras se actualizarán una vez que actualices la página."
|
"update_after_refresh": "Las tarjetas de aventuras se actualizarán una vez que actualices la página.",
|
||||||
|
"add_category": "Agregar categoría",
|
||||||
|
"add_new_category": "Agregar nueva categoría"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "¿Por qué no empezar a planificar tu próxima aventura? \nPuedes agregar una nueva aventura haciendo clic en el botón de abajo.",
|
"add_some": "¿Por qué no empezar a planificar tu próxima aventura? \nPuedes agregar una nueva aventura haciendo clic en el botón de abajo.",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "Gérer les catégories",
|
"manage_categories": "Gérer les catégories",
|
||||||
"no_categories_found": "Aucune catégorie trouvée.",
|
"no_categories_found": "Aucune catégorie trouvée.",
|
||||||
"select_category": "Sélectionnez une catégorie",
|
"select_category": "Sélectionnez une catégorie",
|
||||||
"update_after_refresh": "Les cartes d'aventure seront mises à jour une fois que vous aurez actualisé la page."
|
"update_after_refresh": "Les cartes d'aventure seront mises à jour une fois que vous aurez actualisé la page.",
|
||||||
|
"add_category": "Ajouter une catégorie",
|
||||||
|
"add_new_category": "Ajouter une nouvelle catégorie"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "Pourquoi ne pas commencer à planifier votre prochaine aventure ? \nVous pouvez ajouter une nouvelle aventure en cliquant sur le bouton ci-dessous.",
|
"add_some": "Pourquoi ne pas commencer à planifier votre prochaine aventure ? \nVous pouvez ajouter une nouvelle aventure en cliquant sur le bouton ci-dessous.",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "Gestisci categorie",
|
"manage_categories": "Gestisci categorie",
|
||||||
"no_categories_found": "Nessuna categoria trovata.",
|
"no_categories_found": "Nessuna categoria trovata.",
|
||||||
"select_category": "Seleziona Categoria",
|
"select_category": "Seleziona Categoria",
|
||||||
"update_after_refresh": "Le carte avventura verranno aggiornate una volta aggiornata la pagina."
|
"update_after_refresh": "Le carte avventura verranno aggiornate una volta aggiornata la pagina.",
|
||||||
|
"add_category": "Aggiungi categoria",
|
||||||
|
"add_new_category": "Aggiungi nuova categoria"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "Perché non iniziare a pianificare la tua prossima avventura? \nPuoi aggiungere una nuova avventura facendo clic sul pulsante in basso.",
|
"add_some": "Perché non iniziare a pianificare la tua prossima avventura? \nPuoi aggiungere una nuova avventura facendo clic sul pulsante in basso.",
|
||||||
|
|
|
@ -307,7 +307,9 @@
|
||||||
"manage_categories": "카테고리 관리",
|
"manage_categories": "카테고리 관리",
|
||||||
"no_categories_found": "카테고리가 없습니다.",
|
"no_categories_found": "카테고리가 없습니다.",
|
||||||
"select_category": "카테고리 선택",
|
"select_category": "카테고리 선택",
|
||||||
"update_after_refresh": "페이지를 새로고침해야 모험 카드가 업데이트됩니다."
|
"update_after_refresh": "페이지를 새로고침해야 모험 카드가 업데이트됩니다.",
|
||||||
|
"add_category": "카테고리 추가",
|
||||||
|
"add_new_category": "새 카테고리를 추가하십시오"
|
||||||
},
|
},
|
||||||
"checklist": {
|
"checklist": {
|
||||||
"add_item": "항목 추가",
|
"add_item": "항목 추가",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "Beheer categorieën",
|
"manage_categories": "Beheer categorieën",
|
||||||
"no_categories_found": "Geen categorieën gevonden.",
|
"no_categories_found": "Geen categorieën gevonden.",
|
||||||
"select_category": "Selecteer een categorie",
|
"select_category": "Selecteer een categorie",
|
||||||
"update_after_refresh": "De avonturenkaarten worden bijgewerkt zodra u de pagina vernieuwt."
|
"update_after_refresh": "De avonturenkaarten worden bijgewerkt zodra u de pagina vernieuwt.",
|
||||||
|
"add_category": "Categorie toevoegen",
|
||||||
|
"add_new_category": "Voeg een nieuwe categorie toe"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "Waarom begint u niet met het plannen van uw volgende avontuur? \nJe kunt een nieuw avontuur toevoegen door op de onderstaande knop te klikken.",
|
"add_some": "Waarom begint u niet met het plannen van uw volgende avontuur? \nJe kunt een nieuw avontuur toevoegen door op de onderstaande knop te klikken.",
|
||||||
|
|
|
@ -660,7 +660,9 @@
|
||||||
"icon": "Ikon",
|
"icon": "Ikon",
|
||||||
"update_after_refresh": "Eventyrkortene vil oppdateres når du oppdaterer siden.",
|
"update_after_refresh": "Eventyrkortene vil oppdateres når du oppdaterer siden.",
|
||||||
"select_category": "Velg kategori",
|
"select_category": "Velg kategori",
|
||||||
"category_name": "Kategorinavn"
|
"category_name": "Kategorinavn",
|
||||||
|
"add_category": "Legg til kategori",
|
||||||
|
"add_new_category": "Legg til ny kategori"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"welcome_back": "Velkommen tilbake",
|
"welcome_back": "Velkommen tilbake",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"icon": "Ikona",
|
"icon": "Ikona",
|
||||||
"update_after_refresh": "Karty podróży zostaną zaktualizowane po odświeżeniu strony.",
|
"update_after_refresh": "Karty podróży zostaną zaktualizowane po odświeżeniu strony.",
|
||||||
"select_category": "Wybierz kategorię",
|
"select_category": "Wybierz kategorię",
|
||||||
"category_name": "Nazwa kategorii"
|
"category_name": "Nazwa kategorii",
|
||||||
|
"add_category": "Dodaj kategorię",
|
||||||
|
"add_new_category": "Dodaj nową kategorię"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "Dlaczego nie zacząć planować kolejnej przygody? \nMożesz dodać nową przygodę, klikając przycisk poniżej.",
|
"add_some": "Dlaczego nie zacząć planować kolejnej przygody? \nMożesz dodać nową przygodę, klikając przycisk poniżej.",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "Hantera kategorier",
|
"manage_categories": "Hantera kategorier",
|
||||||
"no_categories_found": "Inga kategorier hittades.",
|
"no_categories_found": "Inga kategorier hittades.",
|
||||||
"select_category": "Välj Kategori",
|
"select_category": "Välj Kategori",
|
||||||
"update_after_refresh": "Äventyrskorten kommer att uppdateras när du uppdaterar sidan."
|
"update_after_refresh": "Äventyrskorten kommer att uppdateras när du uppdaterar sidan.",
|
||||||
|
"add_category": "Lägg till kategori",
|
||||||
|
"add_new_category": "Lägg till en ny kategori"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "Varför inte börja planera ditt nästa äventyr? \nDu kan lägga till ett nytt äventyr genom att klicka på knappen nedan.",
|
"add_some": "Varför inte börja planera ditt nästa äventyr? \nDu kan lägga till ett nytt äventyr genom att klicka på knappen nedan.",
|
||||||
|
|
|
@ -630,7 +630,9 @@
|
||||||
"manage_categories": "管理类别",
|
"manage_categories": "管理类别",
|
||||||
"no_categories_found": "未找到类别。",
|
"no_categories_found": "未找到类别。",
|
||||||
"select_category": "选择类别",
|
"select_category": "选择类别",
|
||||||
"update_after_refresh": "刷新页面后,冒险卡将更新。"
|
"update_after_refresh": "刷新页面后,冒险卡将更新。",
|
||||||
|
"add_category": "添加类别",
|
||||||
|
"add_new_category": "添加新类别"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"add_some": "为什么不开始计划你的下一次冒险呢?\n您可以通过单击下面的按钮添加新的冒险。",
|
"add_some": "为什么不开始计划你的下一次冒险呢?\n您可以通过单击下面的按钮添加新的冒险。",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue