forked from vikunja/frontend
Compare commits
1 Commits
main
...
renovate/f
Author | SHA1 | Date | |
---|---|---|---|
89a9f5a779 |
|
@ -18,12 +18,6 @@
|
|||
"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"
|
||||
|
|
|
@ -28,20 +28,6 @@ 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 {
|
||||
|
|
62
package.json
62
package.json
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"homepage": "https://vikunja.io/",
|
||||
"funding": "https://opencollective.com/vikunja",
|
||||
"packageManager": "pnpm@7.28.0",
|
||||
"packageManager": "pnpm@7.27.0",
|
||||
"keywords": [
|
||||
"todo",
|
||||
"productivity",
|
||||
|
@ -53,24 +53,24 @@
|
|||
"@infectoone/vue-ganttastic": "2.1.4",
|
||||
"@intlify/unplugin-vue-i18n": "0.8.2",
|
||||
"@kyvg/vue3-notification": "2.9.0",
|
||||
"@sentry/tracing": "7.40.0",
|
||||
"@sentry/vue": "7.40.0",
|
||||
"@sentry/tracing": "7.37.2",
|
||||
"@sentry/vue": "7.37.2",
|
||||
"@types/is-touch-device": "1.0.0",
|
||||
"@types/lodash.clonedeep": "4.5.7",
|
||||
"@types/sortablejs": "1.15.0",
|
||||
"@vueuse/core": "9.13.0",
|
||||
"axios": "1.3.4",
|
||||
"blurhash": "2.0.5",
|
||||
"@vueuse/core": "9.12.0",
|
||||
"axios": "1.3.3",
|
||||
"blurhash": "2.0.4",
|
||||
"bulma-css-variables": "0.9.33",
|
||||
"camel-case": "4.1.2",
|
||||
"codemirror": "5.65.12",
|
||||
"codemirror": "5.65.11",
|
||||
"date-fns": "2.29.3",
|
||||
"dayjs": "1.11.7",
|
||||
"dompurify": "3.0.1",
|
||||
"dompurify": "3.0.0",
|
||||
"easymde": "2.18.0",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"flatpickr": "4.6.13",
|
||||
"flexsearch": "0.7.21",
|
||||
"flexsearch": "0.7.31",
|
||||
"floating-vue": "2.0.0-beta.20",
|
||||
"focus-within": "3.0.2",
|
||||
"highlight.js": "11.7.0",
|
||||
|
@ -78,11 +78,11 @@
|
|||
"klona": "2.0.6",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"marked": "4.2.12",
|
||||
"pinia": "2.0.32",
|
||||
"pinia": "2.0.30",
|
||||
"register-service-worker": "1.7.2",
|
||||
"snake-case": "3.0.4",
|
||||
"sortablejs": "1.15.0",
|
||||
"ufo": "1.1.1",
|
||||
"ufo": "1.0.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.4",
|
||||
"@cypress/vite-dev-server": "5.0.2",
|
||||
"@cypress/vue": "5.0.4",
|
||||
"@faker-js/faker": "7.6.0",
|
||||
"@histoire/plugin-screenshot": "0.15.8",
|
||||
"@histoire/plugin-vue": "0.15.8",
|
||||
"@histoire/plugin-screenshot": "0.15.3",
|
||||
"@histoire/plugin-vue": "0.15.3",
|
||||
"@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.14.6",
|
||||
"@types/node": "18.13.0",
|
||||
"@types/postcss-preset-env": "7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.54.0",
|
||||
"@typescript-eslint/parser": "5.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.52.0",
|
||||
"@typescript-eslint/parser": "5.52.0",
|
||||
"@vitejs/plugin-legacy": "4.0.1",
|
||||
"@vitejs/plugin-vue": "4.0.0",
|
||||
"@vue/eslint-config-typescript": "11.0.2",
|
||||
"@vue/test-utils": "2.3.0",
|
||||
"@vue/test-utils": "2.2.10",
|
||||
"@vue/tsconfig": "0.1.3",
|
||||
"autoprefixer": "10.4.13",
|
||||
"browserslist": "4.21.5",
|
||||
"caniuse-lite": "1.0.30001458",
|
||||
"caniuse-lite": "1.0.30001451",
|
||||
"csstype": "3.1.1",
|
||||
"cypress": "12.7.0",
|
||||
"esbuild": "0.17.11",
|
||||
"eslint": "8.35.0",
|
||||
"cypress": "12.5.1",
|
||||
"esbuild": "0.17.8",
|
||||
"eslint": "8.34.0",
|
||||
"eslint-plugin-vue": "9.9.0",
|
||||
"happy-dom": "8.9.0",
|
||||
"histoire": "0.15.8",
|
||||
"netlify-cli": "13.0.0",
|
||||
"happy-dom": "8.2.6",
|
||||
"histoire": "0.15.3",
|
||||
"netlify-cli": "12.12.0",
|
||||
"postcss": "8.4.21",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
"postcss-easings": "3.0.1",
|
||||
"postcss-preset-env": "8.0.1",
|
||||
"rollup": "3.18.0",
|
||||
"rollup": "3.15.0",
|
||||
"rollup-plugin-visualizer": "5.9.0",
|
||||
"sass": "1.58.3",
|
||||
"start-server-and-test": "2.0.0",
|
||||
"sass": "1.58.1",
|
||||
"start-server-and-test": "1.15.4",
|
||||
"typescript": "4.9.5",
|
||||
"vite": "4.1.4",
|
||||
"vite": "4.1.1",
|
||||
"vite-plugin-inject-preload": "1.3.0",
|
||||
"vite-plugin-pwa": "0.14.4",
|
||||
"vite-svg-loader": "4.0.0",
|
||||
"vitest": "0.29.2",
|
||||
"vue-tsc": "1.2.0",
|
||||
"vitest": "0.28.5",
|
||||
"vue-tsc": "1.1.0",
|
||||
"wait-on": "7.0.1",
|
||||
"workbox-cli": "6.5.4"
|
||||
}
|
||||
|
|
942
pnpm-lock.yaml
942
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -122,7 +122,6 @@
|
|||
<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)"
|
||||
|
@ -221,7 +220,7 @@ function updateActiveLists(namespace: INamespace, activeLists: IList[]) {
|
|||
// This is a bit hacky: since we do have to filter out the archived items from the list
|
||||
// for vue draggable updating it is not as simple as replacing it.
|
||||
// To work around this, we merge the active lists with the archived ones. Doing so breaks the order
|
||||
// because now all archived lists are sorted after the active ones. This is fine because they are sorted
|
||||
// because now all archived lists are sorted after the active ones. This is fine because they are sorted
|
||||
// later when showing them anyway, and it makes the merging happening here a lot easier.
|
||||
const lists = [
|
||||
...activeLists,
|
||||
|
@ -246,8 +245,8 @@ async function saveListPosition(e: SortableEvent) {
|
|||
// If the list was dragged to the last position, Safari will report e.newIndex as the size of the listsActive
|
||||
// array instead of using the position. Because the index is wrong in that case, dragging the list will fail.
|
||||
// To work around that we're explicitly checking that case here and decrease the index.
|
||||
const newIndex = e.newIndex === listsActive.length ? e.newIndex - 1 : e.newIndex
|
||||
|
||||
const newIndex = e.newIndex === listsActive.length ? e.newIndex - 1 : e.newIndex
|
||||
|
||||
const list = listsActive[newIndex]
|
||||
const listBefore = listsActive[newIndex - 1] ?? null
|
||||
const listAfter = listsActive[newIndex + 1] ?? null
|
||||
|
@ -342,20 +341,13 @@ $vikunja-nav-selected-width: 0.4rem;
|
|||
}
|
||||
|
||||
.menu-list-dropdown {
|
||||
opacity: 1;
|
||||
opacity: 0;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
@media(hover: hover) and (pointer: fine) {
|
||||
.menu-list-dropdown {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .menu-list-dropdown {
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover .menu-list-dropdown {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.menu-item-icon {
|
||||
|
@ -419,21 +411,18 @@ $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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-menu {
|
||||
margin-top: math.div($navbar-padding, 2);
|
||||
|
||||
|
||||
.menu-list {
|
||||
li {
|
||||
font-weight: 600;
|
||||
|
@ -488,24 +477,17 @@ $vikunja-nav-selected-width: 0.4rem;
|
|||
.favorite {
|
||||
margin-left: .25rem;
|
||||
transition: opacity $transition, color $transition;
|
||||
opacity: 1;
|
||||
opacity: 0;
|
||||
|
||||
&:hover,
|
||||
&.is-favorite {
|
||||
color: var(--warning);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media(hover: hover) and (pointer: fine) {
|
||||
.list-menu .favorite {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.list-menu:hover .favorite,
|
||||
.favorite.is-favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
.favorite.is-favorite,
|
||||
.list-menu:hover .favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.list-menu-title {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
@change="(event: Event) => updateData((event.target as HTMLInputElement).checked)"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label :for="checkBoxId" class="check" @click.prevent="check">
|
||||
<label :for="checkBoxId" class="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,11 +56,6 @@ function updateData(newChecked: boolean) {
|
|||
emit('update:modelValue', newChecked)
|
||||
emit('change', newChecked)
|
||||
}
|
||||
|
||||
function check() {
|
||||
checked.value = !checked.value
|
||||
updateData(checked.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ const listStore = useListStore()
|
|||
top: var(--list-card-padding);
|
||||
right: var(--list-card-padding);
|
||||
transition: opacity $transition, color $transition;
|
||||
opacity: 1;
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--warning);
|
||||
|
@ -160,14 +160,8 @@ const listStore = useListStore()
|
|||
}
|
||||
}
|
||||
|
||||
@media(hover: hover) and (pointer: fine) {
|
||||
.list-card .favorite {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.list-card:hover .favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
.list-card:hover .favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.background-fade-in {
|
||||
|
@ -179,4 +173,4 @@ const listStore = useListStore()
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -428,7 +428,7 @@ function searchTeams() {
|
|||
teamService.getAll({}, { s: t }),
|
||||
)
|
||||
const teamsResult = await Promise.all(teamSearchPromises)
|
||||
foundTeams.value = teamsResult.flat().map((team) => {
|
||||
foundTeams.value = teamsResult.flatMap((team) => {
|
||||
team.title = team.name
|
||||
return team
|
||||
})
|
||||
|
@ -458,13 +458,6 @@ async function doAction(type: ACTION_TYPE, item: DoAction) {
|
|||
params: { id: (item as DoAction<ITask>).id },
|
||||
})
|
||||
break
|
||||
case ACTION_TYPE.TEAM:
|
||||
closeQuickActions()
|
||||
await router.push({
|
||||
name: 'teams.edit',
|
||||
params: { id: (item as DoAction<ITeam>).id },
|
||||
})
|
||||
break
|
||||
case ACTION_TYPE.CMD:
|
||||
query.value = ''
|
||||
selectedCmd.value = item as DoAction<Command>
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
<template>
|
||||
<router-link
|
||||
:to="taskDetailRoute"
|
||||
:class="{'is-loading': taskService.loading}"
|
||||
class="task loader-container"
|
||||
>
|
||||
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
|
||||
<fancycheckbox
|
||||
:disabled="(isArchived || disabled) && !canMarkAsDone"
|
||||
@change="markAsDone"
|
||||
v-model="task.done"
|
||||
/>
|
||||
|
||||
|
||||
<ColorBubble
|
||||
v-if="showListColor && listColor !== '' && currentList.id !== task.listId"
|
||||
:color="listColor"
|
||||
class="mr-1"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
||||
<router-link
|
||||
:to="taskDetailRoute"
|
||||
:class="{ 'done': task.done, 'show-list': showList && taskList !== null}"
|
||||
class="tasktext"
|
||||
>
|
||||
|
@ -96,7 +93,7 @@
|
|||
</span>
|
||||
|
||||
<checklist-summary :task="task"/>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
<progress
|
||||
class="progress is-small"
|
||||
|
@ -117,14 +114,14 @@
|
|||
|
||||
<BaseButton
|
||||
:class="{'is-favorite': task.isFavorite}"
|
||||
@click.prevent="toggleFavorite"
|
||||
@click="toggleFavorite"
|
||||
class="favorite"
|
||||
>
|
||||
<icon icon="star" v-if="task.isFavorite"/>
|
||||
<icon :icon="['far', 'star']" v-else/>
|
||||
</BaseButton>
|
||||
<slot />
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -288,11 +285,7 @@ function hideDeferDueDatePopup(e) {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -338,8 +331,17 @@ function hideDeferDueDatePopup(e) {
|
|||
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--text);
|
||||
transition: color ease $transition-duration;
|
||||
|
||||
&:hover {
|
||||
color: var(--grey-900);
|
||||
}
|
||||
}
|
||||
|
||||
.favorite {
|
||||
opacity: 1;
|
||||
opacity: 0;
|
||||
text-align: center;
|
||||
width: 27px;
|
||||
transition: opacity $transition, color $transition;
|
||||
|
@ -354,26 +356,21 @@ function hideDeferDueDatePopup(e) {
|
|||
}
|
||||
}
|
||||
|
||||
.handle {
|
||||
&:hover .favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.handle {
|
||||
opacity: 0;
|
||||
transition: opacity $transition;
|
||||
margin-right: .25rem;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
@media(hover: hover) and (pointer: fine) {
|
||||
& .favorite,
|
||||
& .handle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .favorite,
|
||||
&:hover .handle {
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover .handle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
:deep(.fancycheckbox) {
|
||||
height: 18px;
|
||||
padding-top: 0;
|
||||
|
@ -425,4 +422,4 @@ function hideDeferDueDatePopup(e) {
|
|||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -5,22 +5,6 @@ import TaskCollectionService from '@/services/taskCollection'
|
|||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {error} from '@/message'
|
||||
|
||||
export type Order = 'asc' | 'desc' | 'none'
|
||||
|
||||
export interface SortBy {
|
||||
id?: Order
|
||||
index?: Order
|
||||
done?: Order
|
||||
title?: Order
|
||||
priority?: Order
|
||||
due_date?: Order
|
||||
start_date?: Order
|
||||
end_date?: Order
|
||||
percent_done?: Order
|
||||
created?: Order
|
||||
updated?: Order
|
||||
}
|
||||
|
||||
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
||||
export const getDefaultParams = () => ({
|
||||
sort_by: ['position', 'id'],
|
||||
|
@ -31,7 +15,7 @@ export const getDefaultParams = () => ({
|
|||
filter_concat: 'and',
|
||||
})
|
||||
|
||||
const SORT_BY_DEFAULT: SortBy = {
|
||||
const SORT_BY_DEFAULT = {
|
||||
id: 'desc',
|
||||
}
|
||||
|
||||
|
@ -60,7 +44,7 @@ const SORT_BY_DEFAULT: SortBy = {
|
|||
/**
|
||||
* This mixin provides a base set of methods and properties to get tasks on a list.
|
||||
*/
|
||||
export function useTaskList(listId, sortByDefault: SortBy = SORT_BY_DEFAULT) {
|
||||
export function useTaskList(listId, sortByDefault = SORT_BY_DEFAULT) {
|
||||
const params = ref({...getDefaultParams()})
|
||||
|
||||
const search = ref('')
|
||||
|
|
|
@ -113,8 +113,8 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
|
|||
window.API_URL = oldUrl
|
||||
throw e
|
||||
})
|
||||
.then(success => {
|
||||
if (success) {
|
||||
.then(r => {
|
||||
if (typeof r !== 'undefined') {
|
||||
localStorage.setItem('API_URL', window.API_URL)
|
||||
return window.API_URL
|
||||
}
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Vytvořit uložený filtr"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Smazat tento uložený filtr",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Opret nyt gemt filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Slet dette gemte filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Bitte gib den Titel für den Filter an."
|
||||
"action": "Neuen gespeicherten Filter erstellen"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Diesen gespeicherten Filter löschen",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Bitte gib den Titel für den Filter an."
|
||||
"action": "Neue gspeicherete Filter erstelle"
|
||||
},
|
||||
"delete": {
|
||||
"header": "De g'speicheret Filter chüble",
|
||||
|
|
|
@ -405,8 +405,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"create": {
|
||||
"title": "Nouveau filtre enregistré",
|
||||
"description": "Un filtre enregistré est une liste virtuelle qui est calculée à partir d’un ensemble de filtres à chaque fois qu’on y accède. Une fois créé, il apparaît dans un espace de noms spécial.",
|
||||
"action": "Créer un nouveau filtre enregistré",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Créer un nouveau filtre enregistré"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Supprimer ce filtre enregistré",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "È necessario un titolo per il filtro."
|
||||
"action": "Crea nuovo filtro salvato"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Elimina questo filtro salvato",
|
||||
|
@ -912,7 +911,7 @@
|
|||
}
|
||||
},
|
||||
"update": {
|
||||
"available": "È disponibile un aggiornamento!",
|
||||
"available": "There is an update available!",
|
||||
"do": "Aggiorna Adesso"
|
||||
},
|
||||
"menu": {
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Opprett nytt filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Slett dette lagrede filteret",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Utwórz nowy filtr stały"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Usuń ten filtr stały",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Por favor, insere um título para o filtro."
|
||||
"action": "Criar novo filtro memorizado"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Eliminar este filtro memorizado",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"create": {
|
||||
"title": "Создать сохранённый фильтр",
|
||||
"description": "Сохранённый фильтр — это виртуальный список, построенный из набора фильтров. При создании отображается в специальном пространстве имён.",
|
||||
"action": "Создать новый сохранённый фильтр",
|
||||
"titleRequired": "Укажите название фильтра."
|
||||
"action": "Создать новый сохранённый фильтр"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Удалить этот сохранённый фильтр",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Tạo thêm bộ lọc sẵn"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Xóa bộ lọc sẵn này",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"create": {
|
||||
"title": "新保存的过滤器",
|
||||
"description": "保存的过滤器是一个虚拟列表,在每次访问时从一组过滤器中计算出来。 创建后,它将出现在一个特殊的命名空间里。",
|
||||
"action": "创建新保存的过滤器",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "创建新保存的过滤器"
|
||||
},
|
||||
"delete": {
|
||||
"header": "删除此保存的过滤器",
|
||||
|
|
|
@ -404,8 +404,7 @@
|
|||
"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",
|
||||
"titleRequired": "Please provide a title for the filter."
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
|
|
|
@ -126,12 +126,6 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
|||
|
||||
/**
|
||||
* Returns an object with all route parameters and their values.
|
||||
* @example
|
||||
* getRouteReplacements(
|
||||
* '/tasks/{taskId}/assignees/{userId}',
|
||||
* { taskId: 7, userId: 2 },
|
||||
* )
|
||||
* // { "{taskId}": 7, "{userId}": 2 }
|
||||
*/
|
||||
getRouteReplacements(route : string, parameters : Record<string, unknown> = {}) {
|
||||
const replace$$1: Record<string, unknown> = {}
|
||||
|
@ -154,8 +148,6 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
|||
|
||||
/**
|
||||
* Returns a fully-ready-ready-to-make-a-request-to route with replaced parameters.
|
||||
* @example
|
||||
* getReplacedRoute('/lists/{listId}/tasks', { listId: 3 }) === '/lists/1/tasks'
|
||||
*/
|
||||
getReplacedRoute(path : string, pathparams : Record<string, unknown>) : string {
|
||||
const replacements = this.getRouteReplacements(path, pathparams)
|
||||
|
@ -311,7 +303,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
|||
* @param params Optional query parameters
|
||||
* @param page The page to get
|
||||
*/
|
||||
async getAll(model : Model = new AbstractModel({}), params = {}, page = 1): Promise<Model[]> {
|
||||
async getAll(model : Model = new AbstractModel({}), params = {}, page = 1) {
|
||||
if (this.paths.getAll === '') {
|
||||
throw new Error('This model is not able to get data.')
|
||||
}
|
||||
|
@ -331,7 +323,10 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
|||
return []
|
||||
}
|
||||
|
||||
return response.data.map(entry => this.modelGetAllFactory(entry))
|
||||
if (Array.isArray(response.data)) {
|
||||
return response.data.map(entry => this.modelGetAllFactory(entry))
|
||||
}
|
||||
return this.modelGetAllFactory(response.data)
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ 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'
|
||||
|
@ -134,38 +133,14 @@ 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,
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import {HTTPFactory} from '@/helpers/fetcher'
|
|||
import {objectToCamelCase} from '@/helpers/case'
|
||||
|
||||
import type {IProvider} from '@/types/IProvider'
|
||||
import type {MIGRATORS} from '@/views/migrate/migrators'
|
||||
|
||||
export interface ConfigState {
|
||||
version: string,
|
||||
|
@ -15,10 +14,10 @@ export interface ConfigState {
|
|||
linkSharingEnabled: boolean,
|
||||
maxFileSize: string,
|
||||
registrationEnabled: boolean,
|
||||
availableMigrators: Array<keyof typeof MIGRATORS>,
|
||||
availableMigrators: [],
|
||||
taskAttachmentsEnabled: boolean,
|
||||
totpEnabled: boolean,
|
||||
enabledBackgroundProviders: Array<'unsplash' | 'upload'>,
|
||||
enabledBackgroundProviders: [],
|
||||
legal: {
|
||||
imprintUrl: string,
|
||||
privacyPolicyUrl: string,
|
||||
|
@ -79,12 +78,11 @@ export const useConfigStore = defineStore('config', () => {
|
|||
function setConfig(config: ConfigState) {
|
||||
Object.assign(state, config)
|
||||
}
|
||||
async function update(): Promise<boolean> {
|
||||
async function update() {
|
||||
const HTTP = HTTPFactory()
|
||||
const {data: config} = await HTTP.get('info')
|
||||
setConfig(objectToCamelCase(config))
|
||||
const success = !!config
|
||||
return success
|
||||
return config
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -3,27 +3,25 @@
|
|||
:title="$t('filters.edit.title')"
|
||||
primary-icon=""
|
||||
:primary-label="$t('misc.save')"
|
||||
@primary="saveFilterWithValidation"
|
||||
@primary="saveFilter"
|
||||
:tertiary="$t('misc.delete')"
|
||||
@tertiary="$router.push({ name: 'filter.settings.delete', params: { id: listId } })"
|
||||
>
|
||||
<form @submit.prevent="saveFilterWithValidation()">
|
||||
<form @submit.prevent="saveFilter()">
|
||||
<div class="field">
|
||||
<label class="label" for="title">{{ $t('filters.attributes.title') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
v-model="filter.title"
|
||||
:class="{ 'disabled': filterService.loading, 'is-danger': !titleValid }"
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
:disabled="filterService.loading || undefined"
|
||||
@keyup.enter="saveFilter"
|
||||
class="input"
|
||||
id="Title"
|
||||
id="title"
|
||||
:placeholder="$t('filters.attributes.titlePlaceholder')"
|
||||
type="text"
|
||||
v-focus
|
||||
@focusout="validateTitleField"
|
||||
/>
|
||||
v-model="filter.title"/>
|
||||
</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>
|
||||
|
@ -67,11 +65,9 @@ import type {IList} from '@/modelTypes/IList'
|
|||
const props = defineProps<{ listId: IList['id'] }>()
|
||||
|
||||
const {
|
||||
saveFilterWithValidation,
|
||||
saveFilter,
|
||||
filter,
|
||||
filters,
|
||||
filterService,
|
||||
titleValid,
|
||||
validateTitleField,
|
||||
} = useSavedFilter(toRef(props, 'listId'))
|
||||
</script>
|
||||
|
|
|
@ -12,17 +12,15 @@
|
|||
<div class="control">
|
||||
<input
|
||||
v-model="filter.title"
|
||||
:class="{ 'disabled': filterService.loading, 'is-danger': !titleValid }"
|
||||
:class="{ 'disabled': filterService.loading}"
|
||||
: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>
|
||||
|
@ -53,8 +51,8 @@
|
|||
<template #footer>
|
||||
<x-button
|
||||
:loading="filterService.loading"
|
||||
:disabled="filterService.loading || !titleValid"
|
||||
@click="createFilterWithValidation()"
|
||||
:disabled="filterService.loading"
|
||||
@click="createFilter()"
|
||||
class="is-fullwidth"
|
||||
>
|
||||
{{ $t('filters.create.action') }}
|
||||
|
@ -73,9 +71,7 @@ import {useSavedFilter} from '@/services/savedFilter'
|
|||
const {
|
||||
filter,
|
||||
filters,
|
||||
createFilterWithValidation,
|
||||
createFilter,
|
||||
filterService,
|
||||
titleValid,
|
||||
validateTitleField,
|
||||
} = useSavedFilter()
|
||||
</script>
|
||||
|
|
|
@ -196,7 +196,7 @@ import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
|||
import Pagination from '@/components/misc/pagination.vue'
|
||||
import Popup from '@/components/misc/popup.vue'
|
||||
|
||||
import {useTaskList, SortBy} from '@/composables/useTaskList'
|
||||
import {useTaskList} from '@/composables/useTaskList'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
|
||||
const ACTIVE_COLUMNS_DEFAULT = {
|
||||
|
@ -222,6 +222,21 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
type Order = 'asc' | 'desc' | 'none'
|
||||
|
||||
interface SortBy {
|
||||
index: Order
|
||||
done?: Order
|
||||
title?: Order
|
||||
priority?: Order
|
||||
due_date?: Order
|
||||
start_date?: Order
|
||||
end_date?: Order
|
||||
percent_done?: Order
|
||||
created?: Order
|
||||
updated?: Order
|
||||
}
|
||||
|
||||
const SORT_BY_DEFAULT: SortBy = {
|
||||
index: 'desc',
|
||||
}
|
||||
|
@ -229,7 +244,7 @@ const SORT_BY_DEFAULT: SortBy = {
|
|||
const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT})
|
||||
const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT})
|
||||
|
||||
const taskList = useTaskList(toRef(props, 'listId'), sortBy.value)
|
||||
const taskList = useTaskList(toRef(props, 'listId'))
|
||||
|
||||
const {
|
||||
loading,
|
||||
|
|
|
@ -16,7 +16,7 @@ interface IMigratorRecord {
|
|||
[key: Migrator['id']]: Migrator
|
||||
}
|
||||
|
||||
export const MIGRATORS = {
|
||||
export const MIGRATORS: IMigratorRecord = {
|
||||
wunderlist: {
|
||||
id: 'wunderlist',
|
||||
name: 'Wunderlist',
|
||||
|
@ -49,4 +49,4 @@ export const MIGRATORS = {
|
|||
icon: tickTickIcon as string,
|
||||
isFileMigrator: true,
|
||||
},
|
||||
} as const satisfies IMigratorRecord
|
||||
} as const
|
||||
|
|
Loading…
Reference in New Issue
Block a user