Compare commits

...

64 Commits

Author SHA1 Message Date
renovate 1392d7f101 fix(deps): update dependency ufo to v1.1.1 2023-02-27 21:43:42 +00:00
renovate e5758e21c7 chore(deps): update typescript-eslint monorepo to v5.54.0 2023-02-27 18:04:10 +00:00
Dominik Pschenitschni e0f06999be
feat: improve recommended vscode settings 2023-02-27 16:52:48 +01:00
renovate 3b72acff27 fix(deps): update sentry-javascript monorepo to v7.39.0 2023-02-27 15:49:34 +00:00
renovate df1c44aabe chore(deps): update dependency @types/node to v18.14.2 2023-02-27 15:04:16 +00:00
kolaente fe764a46e9
fix(task): allow clicking on the whole task to open the task detail view
Resolves #3172
2023-02-27 16:00:08 +01:00
renovate 000e3080a5 chore(deps): update dependency start-server-and-test to v2 2023-02-27 13:05:45 +00:00
renovate f4c568e961 chore(deps): update dependency start-server-and-test to v1.15.5 2023-02-27 08:55:40 +00:00
renovate ef70ead3f0 chore(deps): update dependency caniuse-lite to v1.0.30001458 2023-02-27 08:55:03 +00:00
renovate 7a326d6e03 chore(deps): update dependency happy-dom to v8.9.0 2023-02-27 08:54:32 +00:00
renovate afb6383a85 chore(deps): update dependency netlify-cli to v13 2023-02-27 01:06:23 +00:00
renovate e49969dcad chore(deps): update dependency rollup to v3.17.3 2023-02-26 13:26:09 +00:00
renovate 81e1d70847 chore(deps): update dependency eslint to v8.35.0 2023-02-26 10:04:19 +00:00
renovate 5226517954 chore(deps): update pnpm to v7.28.0 2023-02-25 17:03:54 +00:00
renovate d5d0f9a8e2 chore(deps): update dependency vitest to v0.29.1 2023-02-25 11:04:09 +00:00
renovate 2337b6c9f3 chore(deps): update dependency vue-tsc to v1.2.0 2023-02-25 10:18:01 +00:00
renovate 289802b13d chore(deps): update dependency cypress to v12.7.0 2023-02-25 03:04:10 +00:00
renovate 5de9a2880f chore(deps): update dependency @cypress/vite-dev-server to v5.0.4 2023-02-24 04:04:04 +00:00
Frederick [Bot] 62f6895950 [skip ci] Updated translations via Crowdin 2023-02-24 00:06:10 +00:00
renovate c198b9a164 chore(deps): update dependency @types/node to v18.14.1 2023-02-23 12:04:39 +00:00
renovate d5f5e2a412 fix(deps): update dependency axios to v1.3.4 2023-02-22 22:04:25 +00:00
kolaente cabee68bbb
fix(docker): make sure the service worker and webmanifest are never cached 2023-02-22 12:18:46 +01:00
kolaente 2fd2214a2e
fix(menu): don't show drag handle for not draggable menu items 2023-02-22 12:17:33 +01:00
kolaente 64735e0c3d
fix(filter): don't allow marking a filter as favorite
Resolves https://community.vikunja.io/t/error-favouriting-filters-lists/1161/1
2023-02-22 12:13:48 +01:00
kolaente 1f40b68108
fix(filter): validate title before creating or editing a filter
Resolves #3152
2023-02-22 11:04:31 +01:00
renovate 4033c28a67 chore(deps): update dependency vue-tsc to v1.1.7 2023-02-22 04:04:05 +00:00
renovate be20a01dd6 chore(deps): update dependency vite to v4.1.4 2023-02-21 20:04:08 +00:00
renovate 8f5a628e54 chore(deps): update node.js to v18.14.2 2023-02-21 19:03:24 +00:00
renovate e0c00b306e fix(deps): update dependency pinia to v2.0.32 2023-02-21 08:04:09 +00:00
renovate 10eaacc552 chore(deps): update dependency vue-tsc to v1.1.5 2023-02-20 20:04:05 +00:00
renovate 4b1465955a chore(deps): update typescript-eslint monorepo to v5.53.0 2023-02-20 19:16:38 +00:00
renovate 1711318212 chore(deps): update dependency esbuild to v0.17.10 2023-02-20 18:04:33 +00:00
renovate b042547aaa chore(deps): update dependency netlify-cli to v12.13.2 2023-02-20 12:29:07 +00:00
renovate ed0db956eb chore(deps): update dependency happy-dom to v8.6.0 2023-02-20 12:04:07 +00:00
renovate a66f8a6484 chore(deps): update dependency rollup to v3.17.2 2023-02-20 11:34:43 +00:00
renovate 47e895149e chore(deps): update dependency vue-tsc to v1.1.4 2023-02-20 11:34:01 +00:00
renovate 8e00014feb fix(deps): update dependency pinia to v2.0.31 2023-02-20 11:33:30 +00:00
renovate 6146340034 fix(deps): update dependency codemirror to v5.65.12 2023-02-20 11:04:16 +00:00
renovate c7b761b0eb chore(deps): update dependency caniuse-lite to v1.0.30001457 2023-02-20 09:32:51 +00:00
renovate a1e84b3460 chore(deps): update dependency @vue/test-utils to v2.3.0 2023-02-20 09:32:12 +00:00
renovate 038debaa22 chore(deps): update dependency vite to v4.1.3 2023-02-20 09:04:17 +00:00
renovate 88faf04251 chore(deps): update dependency esbuild to v0.17.9 2023-02-19 18:04:07 +00:00
renovate 04be2b9745 chore(deps): update dependency rollup to v3.17.1 2023-02-18 20:04:05 +00:00
renovate 815e8cce0e chore(deps): update dependency sass to v1.58.3 2023-02-18 13:04:11 +00:00
renovate d12f9247ff chore(deps): update dependency vue-tsc to v1.1.3 2023-02-18 12:38:10 +00:00
renovate 85e7a17934 chore(deps): update pnpm to v7.27.1 2023-02-18 12:37:43 +00:00
renovate 59c5d43348 chore(deps): update dependency rollup to v3.17.0 2023-02-18 12:37:10 +00:00
renovate c011f9aa52 fix(deps): update dependency @vueuse/core to v9.13.0 2023-02-18 12:36:29 +00:00
renovate b9f5319a4f chore(deps): update histoire to v0.15.8 2023-02-18 12:04:22 +00:00
renovate f120ba4169 chore(deps): update dependency @types/node to v18.14.0 2023-02-17 21:04:08 +00:00
renovate b2b70f4a9d chore(deps): update dependency @cypress/vite-dev-server to v5.0.3 2023-02-17 15:04:01 +00:00
renovate 9facffe3e9 fix(deps): update dependency blurhash to v2.0.5 2023-02-17 14:38:30 +00:00
renovate c31aff1d88 chore(deps): update histoire to v0.15.7 2023-02-17 14:23:27 +00:00
renovate 60dea80462 chore(deps): update dependency rollup to v3.16.0 2023-02-17 14:20:21 +00:00
renovate cd10ccfbc0 fix(deps): update sentry-javascript monorepo to v7.38.0 2023-02-17 14:04:40 +00:00
renovate 8647402038 chore(deps): update dependency vite to v4.1.2 2023-02-17 11:04:08 +00:00
renovate 990fd46302 chore(deps): update node.js to v18.14.1 2023-02-17 10:17:25 +00:00
renovate cf0aafd9e6 fix(deps): update dependency ufo to v1.1.0 2023-02-17 09:15:10 +00:00
renovate 70d2535e93 chore(deps): update dependency sass to v1.58.2 2023-02-17 02:04:07 +00:00
Frederick [Bot] 0c6f1a4083 [skip ci] Updated translations via Crowdin 2023-02-17 00:06:13 +00:00
renovate 29eb42932a chore(deps): update dependency vue-tsc to v1.1.2 2023-02-16 17:04:00 +00:00
renovate 736e9051d8 chore(deps): update histoire to v0.15.4 2023-02-16 10:04:01 +00:00
renovate 4a4c401558 chore(deps): update dependency cypress to v12.6.0 (#3115)
Reviewed-on: vikunja/frontend#3115
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-02-15 22:45:51 +00:00
renovate 9198abe24d chore(deps): pin node.js to 18.14.0 2023-02-15 22:02:55 +00:00
33 changed files with 637 additions and 533 deletions

2
.nvmrc
View File

@ -1 +1 @@
v18
18.14.2

View File

@ -18,6 +18,12 @@
"javascriptreact",
"vue"
],
"volar.completion.preferredTagNameCase": "pascal",
// disable vetur in case it is installed
"vetur.validation.template": false,
// i18n ally
"i18n-ally.localesPaths": [
"src/i18n/lang"

View File

@ -28,6 +28,20 @@ server {
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
try_files $uri /index.html =404;
}
# Disable caching for sw
location = /sw.js {
autoindex off;
expires off;
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
}
# Disable caching for webmanifest
location = /manifest.webmanifest {
autoindex off;
expires off;
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
}
# favicon.ico
location = /favicon.ico {

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@7.27.0",
"packageManager": "pnpm@7.28.0",
"keywords": [
"todo",
"productivity",
@ -53,17 +53,17 @@
"@infectoone/vue-ganttastic": "2.1.4",
"@intlify/unplugin-vue-i18n": "0.8.2",
"@kyvg/vue3-notification": "2.9.0",
"@sentry/tracing": "7.37.2",
"@sentry/vue": "7.37.2",
"@sentry/tracing": "7.39.0",
"@sentry/vue": "7.39.0",
"@types/is-touch-device": "1.0.0",
"@types/lodash.clonedeep": "4.5.7",
"@types/sortablejs": "1.15.0",
"@vueuse/core": "9.12.0",
"axios": "1.3.3",
"blurhash": "2.0.4",
"@vueuse/core": "9.13.0",
"axios": "1.3.4",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
"codemirror": "5.65.11",
"codemirror": "5.65.12",
"date-fns": "2.29.3",
"dayjs": "1.11.7",
"dompurify": "3.0.0",
@ -78,11 +78,11 @@
"klona": "2.0.6",
"lodash.debounce": "4.0.8",
"marked": "4.2.12",
"pinia": "2.0.30",
"pinia": "2.0.32",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
"sortablejs": "1.15.0",
"ufo": "1.0.1",
"ufo": "1.1.1",
"vue": "3.2.47",
"vue-advanced-cropper": "2.8.8",
"vue-flatpickr-component": "11.0.2",
@ -93,11 +93,11 @@
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.3",
"@cypress/vite-dev-server": "5.0.2",
"@cypress/vite-dev-server": "5.0.4",
"@cypress/vue": "5.0.4",
"@faker-js/faker": "7.6.0",
"@histoire/plugin-screenshot": "0.15.3",
"@histoire/plugin-vue": "0.15.3",
"@histoire/plugin-screenshot": "0.15.8",
"@histoire/plugin-vue": "0.15.8",
"@rushstack/eslint-patch": "1.2.0",
"@types/codemirror": "5.60.7",
"@types/dompurify": "2.4.0",
@ -105,41 +105,41 @@
"@types/focus-within": "1.0.1",
"@types/lodash.debounce": "4.0.7",
"@types/marked": "4.0.8",
"@types/node": "18.13.0",
"@types/node": "18.14.2",
"@types/postcss-preset-env": "7.7.0",
"@typescript-eslint/eslint-plugin": "5.52.0",
"@typescript-eslint/parser": "5.52.0",
"@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.54.0",
"@vitejs/plugin-legacy": "4.0.1",
"@vitejs/plugin-vue": "4.0.0",
"@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.2.10",
"@vue/test-utils": "2.3.0",
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.13",
"browserslist": "4.21.5",
"caniuse-lite": "1.0.30001451",
"caniuse-lite": "1.0.30001458",
"csstype": "3.1.1",
"cypress": "12.5.1",
"esbuild": "0.17.8",
"eslint": "8.34.0",
"cypress": "12.7.0",
"esbuild": "0.17.10",
"eslint": "8.35.0",
"eslint-plugin-vue": "9.9.0",
"happy-dom": "8.2.6",
"histoire": "0.15.3",
"netlify-cli": "12.12.0",
"happy-dom": "8.9.0",
"histoire": "0.15.8",
"netlify-cli": "13.0.0",
"postcss": "8.4.21",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "3.0.1",
"postcss-preset-env": "8.0.1",
"rollup": "3.15.0",
"rollup": "3.17.3",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.58.1",
"start-server-and-test": "1.15.4",
"sass": "1.58.3",
"start-server-and-test": "2.0.0",
"typescript": "4.9.5",
"vite": "4.1.1",
"vite": "4.1.4",
"vite-plugin-inject-preload": "1.3.0",
"vite-plugin-pwa": "0.14.4",
"vite-svg-loader": "4.0.0",
"vitest": "0.28.5",
"vue-tsc": "1.1.0",
"vitest": "0.29.1",
"vue-tsc": "1.2.0",
"wait-on": "7.0.1",
"workbox-cli": "6.5.4"
}

File diff suppressed because it is too large Load Diff

View File

@ -122,6 +122,7 @@
<span class="list-menu-title">{{ getListTitle(l) }}</span>
</BaseButton>
<BaseButton
v-if="l.id > 0"
class="favorite"
:class="{'is-favorite': l.isFavorite}"
@click="listStore.toggleListFavorite(l)"
@ -411,12 +412,16 @@ $vikunja-nav-selected-width: 0.4rem;
opacity: 0;
transition: opacity $transition;
margin-right: .25rem;
cursor: grab;
}
&:hover .handle {
opacity: 1;
}
}
&:not(.dragging-disabled) .handle {
cursor: grab;
}
}
}

View File

@ -7,7 +7,7 @@
@change="(event: Event) => updateData((event.target as HTMLInputElement).checked)"
type="checkbox"
/>
<label :for="checkBoxId" class="check">
<label :for="checkBoxId" class="check" @click.prevent="check">
<svg height="18px" viewBox="0 0 18 18" width="18px">
<path
d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
@ -56,6 +56,11 @@ function updateData(newChecked: boolean) {
emit('update:modelValue', newChecked)
emit('change', newChecked)
}
function check() {
checked.value = !checked.value
updateData(checked.value)
}
</script>

View File

@ -1,5 +1,9 @@
<template>
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
<router-link
:to="taskDetailRoute"
:class="{'is-loading': taskService.loading}"
class="task loader-container"
>
<fancycheckbox
:disabled="(isArchived || disabled) && !canMarkAsDone"
@change="markAsDone"
@ -12,8 +16,7 @@
class="mr-1"
/>
<router-link
:to="taskDetailRoute"
<div
:class="{ 'done': task.done, 'show-list': showList && taskList !== null}"
class="tasktext"
>
@ -93,7 +96,7 @@
</span>
<checklist-summary :task="task"/>
</router-link>
</div>
<progress
class="progress is-small"
@ -114,14 +117,14 @@
<BaseButton
:class="{'is-favorite': task.isFavorite}"
@click="toggleFavorite"
@click.prevent="toggleFavorite"
class="favorite"
>
<icon icon="star" v-if="task.isFavorite"/>
<icon :icon="['far', 'star']" v-else/>
</BaseButton>
<slot />
</div>
</router-link>
</template>
<script setup lang="ts">
@ -284,8 +287,12 @@ function hideDeferDueDatePopup(e) {
cursor: pointer;
border-radius: $radius;
border: 2px solid transparent;
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
background-color: var(--grey-100);
}
@ -331,15 +338,6 @@ function hideDeferDueDatePopup(e) {
}
a {
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
}
}
.favorite {
opacity: 0;
text-align: center;

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Nový uložený filtr",
"description": "Uložený filtr je virtuální seznam, který se počítá ze sady filtrů pokaždé, když je přístupný. Jakmile bude vytvořen, objeví se ve speciálním prostoru.",
"action": "Vytvořit uložený filtr"
"action": "Vytvořit uložený filtr",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Smazat tento uložený filtr",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Nyt Gemt Filter",
"description": "Et gemt filter er en virtuel liste, som beregnes ud fra et sæt filtre, hver gang det er tilgået. Når den er oprettet, vises den i et særligt navneområde.",
"action": "Opret nyt gemt filter"
"action": "Opret nyt gemt filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Slet dette gemte filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Neuer gespeicherter Filter",
"description": "Ein gespeicherter Filter ist eine virtuelle Liste, die bei jedem Zugriff aus einem Satz von Filtern errechnet wird. Einmal erstellt, erscheint diese in einem speziellen Namespace.",
"action": "Neuen gespeicherten Filter erstellen"
"action": "Neuen gespeicherten Filter erstellen",
"titleRequired": "Bitte gib den Titel für den Filter an."
},
"delete": {
"header": "Diesen gespeicherten Filter löschen",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Neuer gespeicherter Filter",
"description": "En gspeicherete Filter isch e virtuelli Liste, welche vomene Satz a Filter zemmegsetzt wird, sobald me uf sie zuegriift. Wenn sie mal erstellt worde isch, erhaltet si ihren eigene Namensruum.",
"action": "Neue gspeicherete Filter erstelle"
"action": "Neue gspeicherete Filter erstelle",
"titleRequired": "Bitte gib den Titel für den Filter an."
},
"delete": {
"header": "De g'speicheret Filter chüble",

View File

@ -405,7 +405,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Nouveau filtre enregistré",
"description": "Un filtre enregistré est une liste virtuelle qui est calculée à partir dun ensemble de filtres à chaque fois quon y accède. Une fois créé, il apparaît dans un espace de noms spécial.",
"action": "Créer un nouveau filtre enregistré"
"action": "Créer un nouveau filtre enregistré",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Supprimer ce filtre enregistré",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Nuovo Filtro Salvato",
"description": "Un filtro salvato è una lista virtuale che viene calcolata da un insieme di filtri di volta in volta. Una volta creato, apparirà in un namespace speciale.",
"action": "Crea nuovo filtro salvato"
"action": "Crea nuovo filtro salvato",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Elimina questo filtro salvato",
@ -911,7 +912,7 @@
}
},
"update": {
"available": "There is an update available!",
"available": "È disponibile un aggiornamento!",
"do": "Aggiorna Adesso"
},
"menu": {

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Nytt lagret filter",
"description": "Et lagret filter er en virtuell liste som beregnes fra et sett med filtre hver gang det åpnes. Når du er opprettet, vil det vises i et eget navneområde.",
"action": "Opprett nytt filter"
"action": "Opprett nytt filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Slett dette lagrede filteret",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Nowy filtr stały",
"description": "Filtr stały to wirtualna lista, która jest kalkulowana na podstawie zestawu filtrów przy każdym wejściu w nią. Po utworzeniu pojawi się w specjalnej sekcji.",
"action": "Utwórz nowy filtr stały"
"action": "Utwórz nowy filtr stały",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Usuń ten filtr stały",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Novo filtro salvo",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Novo Filtro Memorizado",
"description": "Um filtro memorizado é uma lista virtual que é compilada a partir de um conjunto de filtros de cada vez que é acedido. Uma vez criado, irá aparecer num espaço especial.",
"action": "Criar novo filtro memorizado"
"action": "Criar novo filtro memorizado",
"titleRequired": "Por favor, insere um título para o filtro."
},
"delete": {
"header": "Eliminar este filtro memorizado",

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Создать сохранённый фильтр",
"description": "Сохранённый фильтр — это виртуальный список, построенный из набора фильтров. При создании отображается в специальном пространстве имён.",
"action": "Создать новый сохранённый фильтр"
"action": "Создать новый сохранённый фильтр",
"titleRequired": "Укажите название фильтра."
},
"delete": {
"header": "Удалить этот сохранённый фильтр",

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -404,7 +404,8 @@
"create": {
"title": "Bộ lọc đã lưu mới",
"description": "Bộ lọc sẵn là một danh sách ảo được chọn từ một tập hợp các bộ lọc. Sau khi được tạo, nó sẽ xuất hiện trong một không gian làm việc đặc biệt.",
"action": "Tạo thêm bộ lọc sẵn"
"action": "Tạo thêm bộ lọc sẵn",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Xóa bộ lọc sẵn này",

View File

@ -404,7 +404,8 @@
"create": {
"title": "新保存的过滤器",
"description": "保存的过滤器是一个虚拟列表,在每次访问时从一组过滤器中计算出来。 创建后,它将出现在一个特殊的命名空间里。",
"action": "创建新保存的过滤器"
"action": "创建新保存的过滤器",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "删除此保存的过滤器",

View File

@ -404,7 +404,8 @@
"create": {
"title": "New Saved Filter",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
"action": "Create new saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Delete this saved filter",

View File

@ -2,6 +2,7 @@ import {computed, ref, shallowReactive, unref, watch} from 'vue'
import {useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
import type {MaybeRef} from '@vueuse/core'
import {useDebounceFn} from '@vueuse/core'
import type {IList} from '@/modelTypes/IList'
import type {ISavedFilter} from '@/modelTypes/ISavedFilter'
@ -133,14 +134,38 @@ export function useSavedFilter(listId?: MaybeRef<IList['id']>) {
router.push({name: 'namespaces.index'})
}
const titleValid = ref(true)
const validateTitleField = useDebounceFn(() => {
titleValid.value = filter.value.title !== ''
}, 100)
async function createFilterWithValidation() {
if (!titleValid.value) {
return
}
return createFilter()
}
async function saveFilterWithValidation() {
if (!titleValid.value) {
return
}
return saveFilter()
}
return {
createFilter,
createFilterWithValidation,
saveFilter,
saveFilterWithValidation,
deleteFilter,
filter,
filters,
filterService,
titleValid,
validateTitleField,
}
}

View File

@ -3,25 +3,27 @@
:title="$t('filters.edit.title')"
primary-icon=""
:primary-label="$t('misc.save')"
@primary="saveFilter"
@primary="saveFilterWithValidation"
:tertiary="$t('misc.delete')"
@tertiary="$router.push({ name: 'filter.settings.delete', params: { id: listId } })"
>
<form @submit.prevent="saveFilter()">
<form @submit.prevent="saveFilterWithValidation()">
<div class="field">
<label class="label" for="title">{{ $t('filters.attributes.title') }}</label>
<div class="control">
<input
:class="{ 'disabled': filterService.loading}"
v-model="filter.title"
:class="{ 'disabled': filterService.loading, 'is-danger': !titleValid }"
:disabled="filterService.loading || undefined"
@keyup.enter="saveFilter"
class="input"
id="title"
id="Title"
:placeholder="$t('filters.attributes.titlePlaceholder')"
type="text"
v-focus
v-model="filter.title"/>
@focusout="validateTitleField"
/>
</div>
<p class="help is-danger" v-if="!titleValid">{{ $t('filters.create.titleRequired') }}</p>
</div>
<div class="field">
<label class="label" for="description">{{ $t('filters.attributes.description') }}</label>
@ -65,9 +67,11 @@ import type {IList} from '@/modelTypes/IList'
const props = defineProps<{ listId: IList['id'] }>()
const {
saveFilter,
saveFilterWithValidation,
filter,
filters,
filterService,
titleValid,
validateTitleField,
} = useSavedFilter(toRef(props, 'listId'))
</script>

View File

@ -12,15 +12,17 @@
<div class="control">
<input
v-model="filter.title"
:class="{ 'disabled': filterService.loading}"
:class="{ 'disabled': filterService.loading, 'is-danger': !titleValid }"
:disabled="filterService.loading || undefined"
class="input"
id="Title"
:placeholder="$t('filters.attributes.titlePlaceholder')"
type="text"
v-focus
@focusout="validateTitleField"
/>
</div>
<p class="help is-danger" v-if="!titleValid">{{ $t('filters.create.titleRequired') }}</p>
</div>
<div class="field">
<label class="label" for="description">{{ $t('filters.attributes.description') }}</label>
@ -51,8 +53,8 @@
<template #footer>
<x-button
:loading="filterService.loading"
:disabled="filterService.loading"
@click="createFilter()"
:disabled="filterService.loading || !titleValid"
@click="createFilterWithValidation()"
class="is-fullwidth"
>
{{ $t('filters.create.action') }}
@ -71,7 +73,9 @@ import {useSavedFilter} from '@/services/savedFilter'
const {
filter,
filters,
createFilter,
createFilterWithValidation,
filterService,
titleValid,
validateTitleField,
} = useSavedFilter()
</script>