Merge branch 'main' into feature/ganttastic
# Conflicts: # pnpm-lock.yaml # src/modelTypes/ITask.ts # src/stores/auth.ts # src/types/vue-flatpickr-component.d.ts
This commit is contained in:
commit
b18640d688
|
@ -193,4 +193,15 @@ describe('List View Kanban', () => {
|
|||
cy.get('.kanban .bucket')
|
||||
.should('not.contain', task.title)
|
||||
})
|
||||
|
||||
it('Shows a button to filter the kanban board', () => {
|
||||
const data = TaskFactory.create(10, {
|
||||
list_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.get('.list-kanban .filter-container .base-button')
|
||||
.should('exist')
|
||||
})
|
||||
})
|
|
@ -128,4 +128,24 @@ describe('Home Page Task Overview', () => {
|
|||
.last()
|
||||
.should('contain.text', newTaskTitle)
|
||||
})
|
||||
|
||||
it('Should show the cta buttons for new list when there are no tasks', () => {
|
||||
TaskFactory.truncate()
|
||||
|
||||
cy.visit('/')
|
||||
|
||||
cy.get('.home.app-content .content')
|
||||
.should('contain.text', 'You can create a new list for your new tasks:')
|
||||
.should('contain.text', 'Or import your lists and tasks from other services into Vikunja:')
|
||||
})
|
||||
|
||||
it('Should not show the cta buttons for new list when there are tasks', () => {
|
||||
seedTasks()
|
||||
|
||||
cy.visit('/')
|
||||
|
||||
cy.get('.home.app-content .content')
|
||||
.should('not.contain.text', 'You can create a new list for your new tasks:')
|
||||
.should('not.contain.text', 'Or import your lists and tasks from other services into Vikunja:')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -12,15 +12,51 @@ import {LabelTaskFactory} from '../../factories/label_task'
|
|||
import {BucketFactory} from '../../factories/bucket'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
import {TaskAttachmentFactory} from '../../factories/task_attachments'
|
||||
|
||||
function addLabelToTaskAndVerify(labelTitle: string) {
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add Labels')
|
||||
.click()
|
||||
cy.get('.task-view .details.labels-list .multiselect input')
|
||||
.type(labelTitle)
|
||||
cy.get('.task-view .details.labels-list .multiselect .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 4000 })
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
|
||||
.should('exist')
|
||||
.should('contain', labelTitle)
|
||||
}
|
||||
|
||||
function uploadAttachmentAndVerify(taskId: number) {
|
||||
cy.intercept(`${Cypress.env('API_URL')}/tasks/${taskId}/attachments`).as('uploadAttachment')
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add Attachments')
|
||||
.click()
|
||||
cy.get('input[type=file]', {timeout: 1000})
|
||||
.selectFile('cypress/fixtures/image.jpg', {force: true}) // The input is not visible, but on purpose
|
||||
cy.wait('@uploadAttachment')
|
||||
|
||||
cy.get('.attachments .attachments .files a.attachment')
|
||||
.should('exist')
|
||||
}
|
||||
|
||||
describe('Task', () => {
|
||||
let namespaces
|
||||
let lists
|
||||
let buckets
|
||||
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1)
|
||||
namespaces = NamespaceFactory.create(1)
|
||||
lists = ListFactory.create(1)
|
||||
buckets = BucketFactory.create(1, {
|
||||
list_id: lists[0].id,
|
||||
})
|
||||
TaskFactory.truncate()
|
||||
UserListFactory.truncate()
|
||||
})
|
||||
|
@ -80,6 +116,7 @@ describe('Task', () => {
|
|||
describe('Task Detail View', () => {
|
||||
beforeEach(() => {
|
||||
TaskCommentFactory.truncate()
|
||||
LabelTaskFactory.truncate()
|
||||
})
|
||||
|
||||
it('Shows all task details', () => {
|
||||
|
@ -344,21 +381,31 @@ describe('Task', () => {
|
|||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add Labels')
|
||||
addLabelToTaskAndVerify(labels[0].title)
|
||||
})
|
||||
|
||||
it('Can add a label to a task and it shows up on the kanban board afterwards', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: lists[0].id,
|
||||
bucket_id: buckets[0].id,
|
||||
})
|
||||
const labels = LabelFactory.create(1)
|
||||
LabelTaskFactory.truncate()
|
||||
|
||||
cy.visit(`/lists/${lists[0].id}/kanban`)
|
||||
|
||||
cy.get('.bucket .task')
|
||||
.contains(tasks[0].title)
|
||||
.click()
|
||||
cy.get('.task-view .details.labels-list .multiselect input')
|
||||
.type(labels[0].title)
|
||||
cy.get('.task-view .details.labels-list .multiselect .search-results')
|
||||
.children()
|
||||
.first()
|
||||
|
||||
addLabelToTaskAndVerify(labels[0].title)
|
||||
|
||||
cy.get('.modal-content .close')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 4000 })
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
|
||||
.should('exist')
|
||||
.should('contain', labels[0].title)
|
||||
|
||||
cy.get('.bucket .task')
|
||||
.should('contain.text', labels[0].title)
|
||||
})
|
||||
|
||||
it('Can remove a label from a task', () => {
|
||||
|
@ -417,5 +464,117 @@ describe('Task', () => {
|
|||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Can set a priority for a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Set Priority')
|
||||
.click()
|
||||
cy.get('.task-view .columns.details .column')
|
||||
.contains('Priority')
|
||||
.get('.select select')
|
||||
.select('Urgent')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
|
||||
cy.get('.task-view .columns.details .column')
|
||||
.contains('Priority')
|
||||
.get('.select select')
|
||||
.should('have.value', '4')
|
||||
})
|
||||
|
||||
it('Can set the progress for a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Set Progress')
|
||||
.click()
|
||||
cy.get('.task-view .columns.details .column')
|
||||
.contains('Progress')
|
||||
.get('.select select')
|
||||
.select('50%')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
|
||||
cy.wait(200)
|
||||
|
||||
cy.get('.task-view .columns.details .column')
|
||||
.contains('Progress')
|
||||
.get('.select select')
|
||||
.should('be.visible')
|
||||
.should('have.value', '0.5')
|
||||
})
|
||||
|
||||
it('Can add an attachment to a task', () => {
|
||||
TaskAttachmentFactory.truncate()
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
uploadAttachmentAndVerify(tasks[0].id)
|
||||
})
|
||||
|
||||
it('Can add an attachment to a task and see it appearing on kanban', () => {
|
||||
TaskAttachmentFactory.truncate()
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: lists[0].id,
|
||||
bucket_id: buckets[0].id,
|
||||
})
|
||||
const labels = LabelFactory.create(1)
|
||||
LabelTaskFactory.truncate()
|
||||
|
||||
cy.visit(`/lists/${lists[0].id}/kanban`)
|
||||
|
||||
cy.get('.bucket .task')
|
||||
.contains(tasks[0].title)
|
||||
.click()
|
||||
|
||||
uploadAttachmentAndVerify(tasks[0].id)
|
||||
|
||||
cy.get('.modal-content .close')
|
||||
.click()
|
||||
|
||||
cy.get('.bucket .task .footer .icon svg.fa-paperclip')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Can check items off a checklist', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
description: `
|
||||
This is a checklist:
|
||||
|
||||
* [ ] one item
|
||||
* [ ] another item
|
||||
* [ ] third item
|
||||
* [ ] fourth item
|
||||
* [x] and this one is already done
|
||||
`,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .checklist-summary')
|
||||
.should('contain.text', '1 of 5 tasks')
|
||||
cy.get('.editor .content ul > li input[type=checkbox]')
|
||||
.eq(2)
|
||||
.click()
|
||||
|
||||
cy.get('.editor .content ul > li input[type=checkbox]')
|
||||
.eq(2)
|
||||
.should('be.checked')
|
||||
cy.get('.editor .content input[type=checkbox]')
|
||||
.should('have.length', 5)
|
||||
cy.get('.task-view .checklist-summary')
|
||||
.should('contain.text', '2 of 5 tasks')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -55,4 +55,9 @@ context('Login', () => {
|
|||
|
||||
testAndAssertFailed(fixture)
|
||||
})
|
||||
|
||||
it('Should redirect to /login when no user is logged in', () => {
|
||||
cy.visit('/')
|
||||
cy.url().should('include', '/login')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import {Factory} from '../support/factory'
|
||||
import {formatISO} from 'date-fns'
|
||||
|
||||
export class TaskAttachmentFactory extends Factory {
|
||||
static table = 'task_attachments'
|
||||
|
||||
static factory() {
|
||||
const now = new Date()
|
||||
|
||||
return {
|
||||
id: '{increment}',
|
||||
task_id: 1,
|
||||
file_id: 1,
|
||||
created: formatISO(now),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackageNames": ["netlify-cli"],
|
||||
"matchPackageNames": ["netlify-cli", "happy-dom"],
|
||||
"extends": ["schedule:weekly"]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -44,8 +44,8 @@
|
|||
variant="secondary"
|
||||
:shadow="false"
|
||||
>
|
||||
<img :src="userAvatar" alt="" class="avatar" width="40" height="40"/>
|
||||
<span class="username">{{ userInfo.name !== '' ? userInfo.name : userInfo.username }}</span>
|
||||
<img :src="authStore.avatarUrl" alt="" class="avatar" width="40" height="40"/>
|
||||
<span class="username">{{ authStore.userDisplayName }}</span>
|
||||
<span class="icon is-small">
|
||||
<icon icon="chevron-down"/>
|
||||
</span>
|
||||
|
@ -80,7 +80,7 @@
|
|||
{{ $t('about.title') }}
|
||||
</dropdown-item>
|
||||
<dropdown-item
|
||||
@click="logout()"
|
||||
@click="authStore.logout()"
|
||||
>
|
||||
{{ $t('user.auth.logout') }}
|
||||
</dropdown-item>
|
||||
|
@ -117,8 +117,6 @@ const canWriteCurrentList = computed(() => baseStore.currentList.maxRight > Righ
|
|||
const menuActive = computed(() => baseStore.menuActive)
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const userInfo = computed(() => authStore.info)
|
||||
const userAvatar = computed(() => authStore.avatarUrl)
|
||||
|
||||
const configStore = useConfigStore()
|
||||
const imprintUrl = computed(() => configStore.legal.imprintUrl)
|
||||
|
@ -136,10 +134,6 @@ onMounted(async () => {
|
|||
listTitle.value.style.setProperty('--nav-username-width', `${usernameWidth}px`)
|
||||
})
|
||||
|
||||
function logout() {
|
||||
authStore.logout()
|
||||
}
|
||||
|
||||
function openQuickActions() {
|
||||
baseStore.setQuickActionsActive(true)
|
||||
}
|
||||
|
|
|
@ -285,9 +285,9 @@ function handleCheckboxClick(e: Event) {
|
|||
console.debug('no index found')
|
||||
return
|
||||
}
|
||||
console.debug(index, text.value.slice(index, 9))
|
||||
|
||||
const listPrefix = text.value.slice(index, 1)
|
||||
const listPrefix = text.value.substring(index, index + 1)
|
||||
|
||||
console.debug({index, listPrefix, checked, text: text.value})
|
||||
|
||||
text.value = replaceAt(text.value, index, `${listPrefix} ${checked ? '[x]' : '[ ]'} `)
|
||||
bubble()
|
||||
|
|
|
@ -55,13 +55,13 @@
|
|||
>
|
||||
{{ $t('menu.archive') }}
|
||||
</dropdown-item>
|
||||
<task-subscription
|
||||
<Subscription
|
||||
class="has-no-shadow"
|
||||
:is-button="false"
|
||||
entity="list"
|
||||
:entity-id="list.id"
|
||||
:model-value="list.subscription"
|
||||
@update:model-value="sub => subscription = sub"
|
||||
@update:model-value="setSubscriptionInStore"
|
||||
type="dropdown"
|
||||
/>
|
||||
<dropdown-item
|
||||
|
@ -81,10 +81,12 @@ import {ref, computed, watchEffect, type PropType} from 'vue'
|
|||
import {isSavedFilter} from '@/helpers/savedFilter'
|
||||
import Dropdown from '@/components/misc/dropdown.vue'
|
||||
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
||||
import TaskSubscription from '@/components/misc/subscription.vue'
|
||||
import Subscription from '@/components/misc/subscription.vue'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {ISubscription} from '@/modelTypes/ISubscription'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
|
||||
const props = defineProps({
|
||||
list: {
|
||||
|
@ -93,6 +95,8 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const listStore = useListStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const subscription = ref<ISubscription | null>(null)
|
||||
watchEffect(() => {
|
||||
subscription.value = props.list.subscription ?? null
|
||||
|
@ -100,4 +104,14 @@ watchEffect(() => {
|
|||
|
||||
const configStore = useConfigStore()
|
||||
const backgroundsEnabled = computed(() => configStore.enabledBackgroundProviders?.length > 0)
|
||||
|
||||
function setSubscriptionInStore(sub: ISubscription) {
|
||||
subscription.value = sub
|
||||
const updatedList = {
|
||||
...props.list,
|
||||
subscription: sub,
|
||||
}
|
||||
listStore.setList(updatedList)
|
||||
namespaceStore.setListInNamespaceById(updatedList)
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -210,6 +210,7 @@ import ListService from '@/services/list'
|
|||
import NamespaceService from '@/services/namespace'
|
||||
import EditLabels from '@/components/tasks/partials/editLabels.vue'
|
||||
|
||||
import {dateIsValid, formatISO} from '@/helpers/time/formatDate'
|
||||
import {objectToSnakeCase} from '@/helpers/case'
|
||||
import {getDefaultParams} from '@/composables/taskList'
|
||||
import {camelCase} from 'camel-case'
|
||||
|
@ -391,7 +392,14 @@ export default defineComponent({
|
|||
this.params.filter_value.push(dateTo)
|
||||
}
|
||||
|
||||
this.filters[camelCase(filterName)] = {dateFrom, dateTo}
|
||||
this.filters[camelCase(filterName)] = {
|
||||
// Passing the dates as string values avoids an endless loop between values changing
|
||||
// in the datepicker (bubbling up to here) and changing here and bubbling down to the
|
||||
// datepicker (because there's a new date instance every time this function gets called).
|
||||
// See https://kolaente.dev/vikunja/frontend/issues/2384
|
||||
dateFrom: dateIsValid(dateFrom) ? formatISO(dateFrom) : dateFrom,
|
||||
dateTo: dateIsValid(dateTo) ? formatISO(dateTo) : dateTo,
|
||||
}
|
||||
this.change()
|
||||
return
|
||||
}
|
||||
|
@ -511,12 +519,12 @@ export default defineComponent({
|
|||
if (typeof this.filters[filterName] === 'undefined' || this.filters[filterName] === '') {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Don't load things if we already have something loaded.
|
||||
// This is not the most ideal solution because it prevents a re-population when filters are changed
|
||||
// from the outside. It is still fine because we're not changing them from the outside, other than
|
||||
// loading them initially.
|
||||
if(this[kind].length > 0) {
|
||||
if (this[kind].length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
:loading="loading"
|
||||
>
|
||||
<div class="p-4">
|
||||
<slot />
|
||||
<slot/>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
@ -30,10 +30,12 @@
|
|||
{{ $t('misc.cancel') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
v-if="hasPrimaryAction"
|
||||
variant="primary"
|
||||
@click.prevent.stop="primary()"
|
||||
:icon="primaryIcon"
|
||||
:disabled="primaryDisabled || loading"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ primaryLabel || $t('misc.create') }}
|
||||
</x-button>
|
||||
|
@ -60,6 +62,10 @@ defineProps({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hasPrimaryAction: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
tertiary: {
|
||||
type: String,
|
||||
default: '',
|
||||
|
|
|
@ -61,7 +61,7 @@ const route = useRoute()
|
|||
|
||||
const baseStore = useBaseStore()
|
||||
|
||||
const ready = ref(false)
|
||||
const ready = computed(() => baseStore.ready)
|
||||
const online = useOnline()
|
||||
|
||||
const error = ref('')
|
||||
|
@ -70,11 +70,11 @@ const showLoading = computed(() => !ready.value && error.value === '')
|
|||
async function load() {
|
||||
try {
|
||||
await baseStore.loadApp()
|
||||
const redirectTo = getAuthForRoute(route)
|
||||
baseStore.setReady(true)
|
||||
const redirectTo = await getAuthForRoute(route)
|
||||
if (typeof redirectTo !== 'undefined') {
|
||||
await router.push(redirectTo)
|
||||
}
|
||||
ready.value = true
|
||||
} catch (e: unknown) {
|
||||
error.value = String(e)
|
||||
}
|
||||
|
|
|
@ -69,17 +69,38 @@ const emit = defineEmits(['update:modelValue'])
|
|||
const subscriptionService = shallowRef(new SubscriptionService())
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const tooltipText = computed(() => {
|
||||
if (disabled.value) {
|
||||
return t('task.subscription.subscribedThroughParent', {
|
||||
entity: props.entity,
|
||||
parent: subscriptionEntity.value,
|
||||
})
|
||||
if (props.entity === 'list' && subscriptionEntity.value === 'namespace') {
|
||||
return t('task.subscription.subscribedListThroughParentNamespace')
|
||||
}
|
||||
if (props.entity === 'task' && subscriptionEntity.value === 'namespace') {
|
||||
return t('task.subscription.subscribedTaskThroughParentNamespace')
|
||||
}
|
||||
if (props.entity === 'task' && subscriptionEntity.value === 'list') {
|
||||
return t('task.subscription.subscribedTaskThroughParentList')
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
return props.modelValue !== null ?
|
||||
t('task.subscription.subscribed', {entity: props.entity}) :
|
||||
t('task.subscription.notSubscribed', {entity: props.entity})
|
||||
switch (props.entity) {
|
||||
case 'namespace':
|
||||
return props.modelValue !== null ?
|
||||
t('task.subscription.subscribedNamespace') :
|
||||
t('task.subscription.notSubscribedNamespace')
|
||||
case 'list':
|
||||
return props.modelValue !== null ?
|
||||
t('task.subscription.subscribedList') :
|
||||
t('task.subscription.notSubscribedList')
|
||||
case 'task':
|
||||
return props.modelValue !== null ?
|
||||
t('task.subscription.subscribedTask') :
|
||||
t('task.subscription.notSubscribedTask')
|
||||
}
|
||||
|
||||
return ''
|
||||
})
|
||||
|
||||
const buttonText = computed(() => props.modelValue ? t('task.subscription.unsubscribe') : t('task.subscription.subscribe'))
|
||||
|
@ -105,7 +126,20 @@ async function subscribe() {
|
|||
})
|
||||
await subscriptionService.value.create(subscription)
|
||||
emit('update:modelValue', subscription)
|
||||
success({message: t('task.subscription.subscribeSuccess', {entity: props.entity})})
|
||||
|
||||
let message = ''
|
||||
switch (props.entity) {
|
||||
case 'namespace':
|
||||
message = t('task.subscription.subscribeSuccessNamespace')
|
||||
break
|
||||
case 'list':
|
||||
message = t('task.subscription.subscribeSuccessList')
|
||||
break
|
||||
case 'task':
|
||||
message = t('task.subscription.subscribeSuccessTask')
|
||||
break
|
||||
}
|
||||
success({message})
|
||||
}
|
||||
|
||||
async function unsubscribe() {
|
||||
|
@ -115,6 +149,19 @@ async function unsubscribe() {
|
|||
})
|
||||
await subscriptionService.value.delete(subscription)
|
||||
emit('update:modelValue', null)
|
||||
success({message: t('task.subscription.unsubscribeSuccess', {entity: props.entity})})
|
||||
|
||||
let message = ''
|
||||
switch (props.entity) {
|
||||
case 'namespace':
|
||||
message = t('task.subscription.unsubscribeSuccessNamespace')
|
||||
break
|
||||
case 'list':
|
||||
message = t('task.subscription.unsubscribeSuccessList')
|
||||
break
|
||||
case 'task':
|
||||
message = t('task.subscription.unsubscribeSuccessTask')
|
||||
break
|
||||
}
|
||||
success({message})
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -33,14 +33,13 @@
|
|||
>
|
||||
{{ $t('menu.archive') }}
|
||||
</dropdown-item>
|
||||
<task-subscription
|
||||
v-if="subscription"
|
||||
<Subscription
|
||||
class="has-no-shadow"
|
||||
:is-button="false"
|
||||
entity="namespace"
|
||||
:entity-id="namespace.id"
|
||||
:model-value="subscription"
|
||||
@update:model-value="sub => subscription = sub"
|
||||
@update:model-value="setSubscriptionInStore"
|
||||
type="dropdown"
|
||||
/>
|
||||
<dropdown-item
|
||||
|
@ -59,9 +58,10 @@ import {ref, onMounted, type PropType} from 'vue'
|
|||
|
||||
import Dropdown from '@/components/misc/dropdown.vue'
|
||||
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
||||
import TaskSubscription from '@/components/misc/subscription.vue'
|
||||
import Subscription from '@/components/misc/subscription.vue'
|
||||
import type {INamespace} from '@/modelTypes/INamespace'
|
||||
import type {ISubscription} from '@/modelTypes/ISubscription'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
|
||||
const props = defineProps({
|
||||
namespace: {
|
||||
|
@ -70,8 +70,20 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const namespaceStore = useNamespaceStore()
|
||||
|
||||
const subscription = ref<ISubscription | null>(null)
|
||||
onMounted(() => {
|
||||
subscription.value = props.namespace.subscription
|
||||
})
|
||||
|
||||
function setSubscriptionInStore(sub: ISubscription) {
|
||||
subscription.value = sub
|
||||
namespaceStore.setNamespaces([
|
||||
{
|
||||
...props.namespace,
|
||||
subscription: sub,
|
||||
},
|
||||
])
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<input
|
||||
v-if="editEnabled"
|
||||
:disabled="attachmentService.loading || undefined"
|
||||
:disabled="loading || undefined"
|
||||
@change="uploadNewAttachment()"
|
||||
id="files"
|
||||
multiple
|
||||
|
@ -35,7 +35,15 @@
|
|||
:key="a.id"
|
||||
@click="viewOrDownload(a)"
|
||||
>
|
||||
<div class="filename">{{ a.file.name }}</div>
|
||||
<div class="filename">
|
||||
{{ a.file.name }}
|
||||
<span
|
||||
v-if="task.coverImageAttachmentId === a.id"
|
||||
class="is-task-cover"
|
||||
>
|
||||
{{ $t('task.attachment.usedAsCover') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
<p class="attachment-info-meta">
|
||||
<i18n-t keypath="task.attachment.createdBy" scope="global">
|
||||
|
@ -78,6 +86,17 @@
|
|||
>
|
||||
{{ $t('misc.delete') }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-if="editEnabled"
|
||||
class="attachment-info-meta-button"
|
||||
@click.prevent.stop="setCoverImage(task.coverImageAttachmentId === a.id ? null : a)"
|
||||
>
|
||||
{{
|
||||
task.coverImageAttachmentId === a.id
|
||||
? $t('task.attachment.unsetAsCover')
|
||||
: $t('task.attachment.setAsCover')
|
||||
}}
|
||||
</BaseButton>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -85,7 +104,7 @@
|
|||
|
||||
<x-button
|
||||
v-if="editEnabled"
|
||||
:disabled="attachmentService.loading"
|
||||
:disabled="loading"
|
||||
@click="filesRef?.click()"
|
||||
class="mb-4"
|
||||
icon="cloud-upload-alt"
|
||||
|
@ -118,7 +137,7 @@
|
|||
<template #header>
|
||||
<span>{{ $t('task.attachment.delete') }}</span>
|
||||
</template>
|
||||
|
||||
|
||||
<template #text>
|
||||
<p>
|
||||
{{ $t('task.attachment.deleteText1', {filename: attachmentToDelete.file.name}) }}<br/>
|
||||
|
@ -138,13 +157,14 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowReactive, computed, type PropType} from 'vue'
|
||||
import {ref, shallowReactive, computed} from 'vue'
|
||||
import {useDropZone} from '@vueuse/core'
|
||||
|
||||
import User from '@/components/misc/user.vue'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
import AttachmentService from '@/services/attachment'
|
||||
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
|
||||
import type AttachmentModel from '@/models/attachment'
|
||||
import type {IAttachment} from '@/modelTypes/IAttachment'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
|
@ -155,38 +175,44 @@ import {uploadFiles, generateAttachmentUrl} from '@/helpers/attachments'
|
|||
import {getHumanSize} from '@/helpers/getHumanSize'
|
||||
import {useCopyToClipboard} from '@/composables/useCopyToClipboard'
|
||||
import {error, success} from '@/message'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: Number as PropType<ITask['id']>,
|
||||
required: true,
|
||||
},
|
||||
initialAttachments: {
|
||||
type: Array,
|
||||
},
|
||||
editEnabled: {
|
||||
default: true,
|
||||
},
|
||||
const taskStore = useTaskStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
task: ITask,
|
||||
initialAttachments?: IAttachment[],
|
||||
editEnabled: boolean,
|
||||
}>(), {
|
||||
editEnabled: true,
|
||||
})
|
||||
|
||||
// FIXME: this should go through the store
|
||||
const emit = defineEmits(['task-changed'])
|
||||
|
||||
const attachmentService = shallowReactive(new AttachmentService())
|
||||
|
||||
const attachmentStore = useAttachmentStore()
|
||||
const attachments = computed(() => attachmentStore.attachments)
|
||||
|
||||
const loading = computed(() => attachmentService.loading || taskStore.isLoading)
|
||||
|
||||
function onDrop(files: File[] | null) {
|
||||
if (files && files.length !== 0) {
|
||||
uploadFilesToTask(files)
|
||||
}
|
||||
}
|
||||
|
||||
const { isOverDropZone } = useDropZone(document, onDrop)
|
||||
const {isOverDropZone} = useDropZone(document, onDrop)
|
||||
|
||||
function downloadAttachment(attachment: IAttachment) {
|
||||
attachmentService.download(attachment)
|
||||
}
|
||||
|
||||
const filesRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
function uploadNewAttachment() {
|
||||
const files = filesRef.value?.files
|
||||
|
||||
|
@ -198,7 +224,7 @@ function uploadNewAttachment() {
|
|||
}
|
||||
|
||||
function uploadFilesToTask(files: File[] | FileList) {
|
||||
uploadFiles(attachmentService, props.taskId, files)
|
||||
uploadFiles(attachmentService, props.task.id, files)
|
||||
}
|
||||
|
||||
const attachmentToDelete = ref<AttachmentModel | null>(null)
|
||||
|
@ -217,16 +243,15 @@ async function deleteAttachment() {
|
|||
attachmentStore.removeById(attachmentToDelete.value.id)
|
||||
success(r)
|
||||
setAttachmentToDelete(null)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
error(e)
|
||||
}
|
||||
}
|
||||
|
||||
const attachmentImageBlobUrl = ref<string | null>(null)
|
||||
const SUPPORTED_SUFFIX = ['.jpg', '.png', '.bmp', '.gif']
|
||||
|
||||
async function viewOrDownload(attachment: AttachmentModel) {
|
||||
if (SUPPORTED_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix)) ) {
|
||||
if (SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix))) {
|
||||
attachmentImageBlobUrl.value = await attachmentService.getBlobUrl(attachment)
|
||||
} else {
|
||||
downloadAttachment(attachment)
|
||||
|
@ -234,8 +259,15 @@ async function viewOrDownload(attachment: AttachmentModel) {
|
|||
}
|
||||
|
||||
const copy = useCopyToClipboard()
|
||||
|
||||
function copyUrl(attachment: IAttachment) {
|
||||
copy(generateAttachmentUrl(props.taskId, attachment.id))
|
||||
copy(generateAttachmentUrl(props.task.id, attachment.id))
|
||||
}
|
||||
|
||||
async function setCoverImage(attachment: IAttachment | null) {
|
||||
const task = await taskStore.setCoverImage(props.task, attachment)
|
||||
emit('task-changed', task)
|
||||
success({message: t('task.attachment.successfullyChangedCoverImage')})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -316,7 +348,7 @@ function copyUrl(attachment: IAttachment) {
|
|||
height: auto;
|
||||
text-shadow: var(--shadow-md);
|
||||
animation: bounce 2s infinite;
|
||||
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
animation: none;
|
||||
}
|
||||
|
@ -338,7 +370,7 @@ function copyUrl(attachment: IAttachment) {
|
|||
.attachment-info-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
||||
:deep(.user) {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
|
@ -348,7 +380,7 @@ function copyUrl(attachment: IAttachment) {
|
|||
@media screen and (max-width: $mobile) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
|
||||
:deep(.user) {
|
||||
margin: .5rem 0;
|
||||
}
|
||||
|
@ -394,5 +426,13 @@ function copyUrl(attachment: IAttachment) {
|
|||
}
|
||||
}
|
||||
|
||||
.is-task-cover {
|
||||
background: var(--primary);
|
||||
color: var(--white);
|
||||
padding: .25rem .35rem;
|
||||
border-radius: 4px;
|
||||
font-size: .75rem;
|
||||
}
|
||||
|
||||
@include modal-transition();
|
||||
</style>
|
|
@ -1,34 +1,29 @@
|
|||
<template>
|
||||
<div
|
||||
tabindex="-1"
|
||||
@focus="focus"
|
||||
<Multiselect
|
||||
:loading="listUserService.loading"
|
||||
:placeholder="$t('task.assignee.placeholder')"
|
||||
:multiple="true"
|
||||
@search="findUser"
|
||||
:search-results="foundUsers"
|
||||
@select="addAssignee"
|
||||
label="name"
|
||||
:select-placeholder="$t('task.assignee.selectPlaceholder')"
|
||||
v-model="assignees"
|
||||
ref="userSearchInputRef"
|
||||
>
|
||||
<Multiselect
|
||||
:loading="listUserService.loading"
|
||||
:placeholder="$t('task.assignee.placeholder')"
|
||||
:multiple="true"
|
||||
@search="findUser"
|
||||
:search-results="foundUsers"
|
||||
@select="addAssignee"
|
||||
label="name"
|
||||
:select-placeholder="$t('task.assignee.selectPlaceholder')"
|
||||
v-model="assignees"
|
||||
ref="multiselect"
|
||||
>
|
||||
<template #tag="{item: user}">
|
||||
<span class="assignee">
|
||||
<user :avatar-size="32" :show-username="false" :user="user"/>
|
||||
<BaseButton @click="removeAssignee(user)" class="remove-assignee" v-if="!disabled">
|
||||
<icon icon="times"/>
|
||||
</BaseButton>
|
||||
</span>
|
||||
</template>
|
||||
</Multiselect>
|
||||
</div>
|
||||
<template #tag="{item: user}">
|
||||
<span class="assignee">
|
||||
<user :avatar-size="32" :show-username="false" :user="user"/>
|
||||
<BaseButton @click="removeAssignee(user)" class="remove-assignee" v-if="!disabled">
|
||||
<icon icon="times"/>
|
||||
</BaseButton>
|
||||
</span>
|
||||
</template>
|
||||
</Multiselect>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowReactive, watch, type PropType} from 'vue'
|
||||
import {ref, shallowReactive, watch, nextTick, type PropType} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import User from '@/components/misc/user.vue'
|
||||
|
@ -41,32 +36,34 @@ import {success} from '@/message'
|
|||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import {getDisplayName} from '@/models/user'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
listId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
default: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: Array as PropType<IUser[]>,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
listId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
default: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: Array as PropType<IUser[]>,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const listUserService = shallowReactive(new ListUserService())
|
||||
const foundUsers = ref([])
|
||||
const foundUsers = ref<IUser[]>([])
|
||||
const assignees = ref<IUser[]>([])
|
||||
let isAdding = false
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
|
@ -80,9 +77,19 @@ watch(
|
|||
)
|
||||
|
||||
async function addAssignee(user: IUser) {
|
||||
await taskStore.addAssignee({user: user, taskId: props.taskId})
|
||||
emit('update:modelValue', assignees.value)
|
||||
success({message: t('task.assignee.assignSuccess')})
|
||||
if (isAdding) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
nextTick(() => isAdding = true)
|
||||
|
||||
await taskStore.addAssignee({user: user, taskId: props.taskId})
|
||||
emit('update:modelValue', assignees.value)
|
||||
success({message: t('task.assignee.assignSuccess')})
|
||||
} finally {
|
||||
nextTick(() => isAdding = false)
|
||||
}
|
||||
}
|
||||
|
||||
async function removeAssignee(user: IUser) {
|
||||
|
@ -103,13 +110,14 @@ async function findUser(query: string) {
|
|||
return
|
||||
}
|
||||
|
||||
const response = await listUserService.getAll({listId: props.listId}, {s: query})
|
||||
const response = await listUserService.getAll({listId: props.listId}, {s: query}) as IUser[]
|
||||
|
||||
// Filter the results to not include users who are already assigned
|
||||
foundUsers.value = response.filter(({id}) => !includesById(assignees.value, id))
|
||||
foundUsers.value = response
|
||||
.filter(({id}) => !includesById(assignees.value, id))
|
||||
.map(u => {
|
||||
// Users may not have a display name set, so we fall back on the username in that case
|
||||
u.name = u.name === '' ? u.username : u.name
|
||||
u.name = getDisplayName(u)
|
||||
return u
|
||||
})
|
||||
}
|
||||
|
@ -117,11 +125,6 @@ async function findUser(query: string) {
|
|||
function clearAllFoundUsers() {
|
||||
foundUsers.value = []
|
||||
}
|
||||
|
||||
const multiselect = ref()
|
||||
function focus() {
|
||||
multiselect.value.focus()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -11,62 +11,70 @@
|
|||
@click.ctrl="() => toggleTaskDone(task)"
|
||||
@click.meta="() => toggleTaskDone(task)"
|
||||
>
|
||||
<span class="task-id">
|
||||
<Done class="kanban-card__done" :is-done="task.done" variant="small"/>
|
||||
<template v-if="task.identifier === ''">
|
||||
#{{ task.index }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ task.identifier }}
|
||||
</template>
|
||||
</span>
|
||||
<span
|
||||
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
|
||||
class="due-date"
|
||||
v-if="task.dueDate > 0"
|
||||
v-tooltip="formatDateLong(task.dueDate)">
|
||||
<span class="icon">
|
||||
<icon :icon="['far', 'calendar-alt']"/>
|
||||
<img
|
||||
v-if="coverImageBlobUrl"
|
||||
:src="coverImageBlobUrl"
|
||||
alt=""
|
||||
class="cover-image"
|
||||
/>
|
||||
<div class="p-2">
|
||||
<span class="task-id">
|
||||
<Done class="kanban-card__done" :is-done="task.done" variant="small"/>
|
||||
<template v-if="task.identifier === ''">
|
||||
#{{ task.index }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ task.identifier }}
|
||||
</template>
|
||||
</span>
|
||||
<time :datetime="formatISO(task.dueDate)">
|
||||
{{ formatDateSince(task.dueDate) }}
|
||||
</time>
|
||||
</span>
|
||||
<h3>{{ task.title }}</h3>
|
||||
<progress
|
||||
class="progress is-small"
|
||||
v-if="task.percentDone > 0"
|
||||
:value="task.percentDone * 100" max="100">
|
||||
{{ task.percentDone * 100 }}%
|
||||
</progress>
|
||||
<div class="footer">
|
||||
<labels :labels="task.labels"/>
|
||||
<priority-label :priority="task.priority" :done="task.done"/>
|
||||
<div class="assignees" v-if="task.assignees.length > 0">
|
||||
<user
|
||||
v-for="u in task.assignees"
|
||||
:avatar-size="24"
|
||||
:key="task.id + 'assignee' + u.id"
|
||||
:show-username="false"
|
||||
:user="u"
|
||||
/>
|
||||
<span
|
||||
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
|
||||
class="due-date"
|
||||
v-if="task.dueDate > 0"
|
||||
v-tooltip="formatDateLong(task.dueDate)">
|
||||
<span class="icon">
|
||||
<icon :icon="['far', 'calendar-alt']"/>
|
||||
</span>
|
||||
<time :datetime="formatISO(task.dueDate)">
|
||||
{{ formatDateSince(task.dueDate) }}
|
||||
</time>
|
||||
</span>
|
||||
<h3>{{ task.title }}</h3>
|
||||
<progress
|
||||
class="progress is-small"
|
||||
v-if="task.percentDone > 0"
|
||||
:value="task.percentDone * 100" max="100">
|
||||
{{ task.percentDone * 100 }}%
|
||||
</progress>
|
||||
<div class="footer">
|
||||
<labels :labels="task.labels"/>
|
||||
<priority-label :priority="task.priority" :done="task.done"/>
|
||||
<div class="assignees" v-if="task.assignees.length > 0">
|
||||
<user
|
||||
v-for="u in task.assignees"
|
||||
:avatar-size="24"
|
||||
:key="task.id + 'assignee' + u.id"
|
||||
:show-username="false"
|
||||
:user="u"
|
||||
/>
|
||||
</div>
|
||||
<checklist-summary :task="task"/>
|
||||
<span class="icon" v-if="task.attachments.length > 0">
|
||||
<icon icon="paperclip"/>
|
||||
</span>
|
||||
<span v-if="task.description" class="icon">
|
||||
<icon icon="align-left"/>
|
||||
</span>
|
||||
<span class="icon" v-if="task.repeatAfter.amount > 0">
|
||||
<icon icon="history"/>
|
||||
</span>
|
||||
</div>
|
||||
<checklist-summary :task="task"/>
|
||||
<span class="icon" v-if="task.attachments.length > 0">
|
||||
<icon icon="paperclip"/>
|
||||
</span>
|
||||
<span v-if="task.description" class="icon">
|
||||
<icon icon="align-left"/>
|
||||
</span>
|
||||
<span class="icon" v-if="task.repeatAfter.amount > 0">
|
||||
<icon icon="history"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, computed} from 'vue'
|
||||
import {ref, computed, watch} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
import PriorityLabel from '@/components/tasks/partials/priorityLabel.vue'
|
||||
|
@ -77,6 +85,8 @@ import ChecklistSummary from './checklist-summary.vue'
|
|||
|
||||
import {TASK_DEFAULT_COLOR, getHexColor} from '@/models/task'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
|
||||
import AttachmentService from '@/services/attachment'
|
||||
|
||||
import {formatDateLong, formatISO, formatDateSince} from '@/helpers/time/formatDate'
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
|
@ -114,6 +124,29 @@ function openTaskDetail() {
|
|||
state: {backdropView: router.currentRoute.value.fullPath},
|
||||
})
|
||||
}
|
||||
|
||||
const coverImageBlobUrl = ref<string | null>(null)
|
||||
|
||||
async function maybeDownloadCoverImage() {
|
||||
if (!props.task.coverImageAttachmentId) {
|
||||
coverImageBlobUrl.value = null
|
||||
return
|
||||
}
|
||||
|
||||
const attachment = props.task.attachments.find(a => a.id === props.task.coverImageAttachmentId)
|
||||
if (!attachment || !SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix))) {
|
||||
return
|
||||
}
|
||||
|
||||
const attachmentService = new AttachmentService()
|
||||
coverImageBlobUrl.value = await attachmentService.getBlobUrl(attachment)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.task.coverImageAttachmentId,
|
||||
maybeDownloadCoverImage,
|
||||
{immediate: true},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -125,12 +158,11 @@ $task-background: var(--white);
|
|||
cursor: pointer;
|
||||
box-shadow: var(--shadow-xs);
|
||||
display: block;
|
||||
border: 3px solid transparent;
|
||||
|
||||
font-size: .9rem;
|
||||
padding: .4rem;
|
||||
border-radius: $radius;
|
||||
background: $task-background;
|
||||
overflow: hidden;
|
||||
|
||||
&.loader-container.is-loading::after {
|
||||
width: 1.5rem;
|
||||
|
|
|
@ -37,7 +37,11 @@
|
|||
@create="createAndRelateTask"
|
||||
>
|
||||
<template #searchResult="{option: task}">
|
||||
<span v-if="typeof task !== 'string'" class="search-result">
|
||||
<span
|
||||
v-if="typeof task !== 'string'"
|
||||
class="search-result"
|
||||
:class="{'is-strikethrough': task.done}"
|
||||
>
|
||||
<span
|
||||
class="different-list"
|
||||
v-if="task.listId !== listId"
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import {describe, it, expect} from 'vitest'
|
||||
import {hourToSalutation} from './useDateTimeSalutation'
|
||||
|
||||
const dateWithHour = (hours: number): Date => {
|
||||
const date = new Date()
|
||||
date.setHours(hours)
|
||||
return date
|
||||
}
|
||||
|
||||
describe('Salutation', () => {
|
||||
it('shows the right salutation in the night', () => {
|
||||
const salutation = hourToSalutation(dateWithHour(4))
|
||||
expect(salutation).toBe('home.welcomeNight')
|
||||
})
|
||||
it('shows the right salutation in the morning', () => {
|
||||
const salutation = hourToSalutation(dateWithHour(8))
|
||||
expect(salutation).toBe('home.welcomeMorning')
|
||||
})
|
||||
it('shows the right salutation in the day', () => {
|
||||
const salutation = hourToSalutation(dateWithHour(13))
|
||||
expect(salutation).toBe('home.welcomeDay')
|
||||
})
|
||||
it('shows the right salutation in the night', () => {
|
||||
const salutation = hourToSalutation(dateWithHour(20))
|
||||
expect(salutation).toBe('home.welcomeEvening')
|
||||
})
|
||||
it('shows the right salutation in the night again', () => {
|
||||
const salutation = hourToSalutation(dateWithHour(23))
|
||||
expect(salutation).toBe('home.welcomeNight')
|
||||
})
|
||||
})
|
|
@ -1,31 +0,0 @@
|
|||
import {computed} from 'vue'
|
||||
import {useNow} from '@vueuse/core'
|
||||
|
||||
const TRANSLATION_KEY_PREFIX = 'home.welcome'
|
||||
|
||||
export function hourToSalutation(now: Date) {
|
||||
const hours = now.getHours()
|
||||
|
||||
if (hours < 5) {
|
||||
return `${TRANSLATION_KEY_PREFIX}Night`
|
||||
}
|
||||
|
||||
if (hours < 11) {
|
||||
return `${TRANSLATION_KEY_PREFIX}Morning`
|
||||
}
|
||||
|
||||
if (hours < 18) {
|
||||
return `${TRANSLATION_KEY_PREFIX}Day`
|
||||
}
|
||||
|
||||
if (hours < 23) {
|
||||
return `${TRANSLATION_KEY_PREFIX}Evening`
|
||||
}
|
||||
|
||||
return `${TRANSLATION_KEY_PREFIX}Night`
|
||||
}
|
||||
|
||||
export function useDateTimeSalutation() {
|
||||
const now = useNow()
|
||||
return computed(() => hourToSalutation(now.value))
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import {computed} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useNow} from '@vueuse/core'
|
||||
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {hourToDaytime} from '@/helpers/hourToDaytime'
|
||||
|
||||
export type Daytime = 'night' | 'morning' | 'day' | 'evening'
|
||||
|
||||
export function useDaytimeSalutation() {
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const now = useNow()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const name = computed(() => authStore.userDisplayName)
|
||||
const daytime = computed(() => hourToDaytime(now.value))
|
||||
|
||||
const salutations = {
|
||||
'night': () => t('home.welcomeNight', {username: name.value}),
|
||||
'morning': () => t('home.welcomeMorning', {username: name.value}),
|
||||
'day': () => t('home.welcomeDay', {username: name.value}),
|
||||
'evening': () => t('home.welcomeEvening', {username: name.value}),
|
||||
} as Record<Daytime, () => string>
|
||||
|
||||
return computed(() => name.value ? salutations[daytime.value]() : undefined)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import {useRouter} from 'vue-router'
|
||||
import {getLastVisited, clearLastVisited} from '@/helpers/saveLastVisited'
|
||||
|
||||
export function useRedirectToLastVisited() {
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function redirectIfSaved() {
|
||||
const last = getLastVisited()
|
||||
if (last !== null) {
|
||||
router.push({
|
||||
name: last.name,
|
||||
params: last.params,
|
||||
query: last.query,
|
||||
})
|
||||
clearLastVisited()
|
||||
return
|
||||
}
|
||||
|
||||
router.push({name: 'home'})
|
||||
}
|
||||
|
||||
return {
|
||||
redirectIfSaved,
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ export function useRenewTokenOnFocus() {
|
|||
authStore.renewToken()
|
||||
|
||||
// Check if the token is still valid if the window gets focus again to maybe renew it
|
||||
useEventListener('focus', () => {
|
||||
useEventListener('focus', async () => {
|
||||
if (!authenticated.value) {
|
||||
return
|
||||
}
|
||||
|
@ -29,8 +29,8 @@ export function useRenewTokenOnFocus() {
|
|||
// If the token expiry is negative, it is already expired and we have no choice but to redirect
|
||||
// the user to the login page
|
||||
if (expiresIn < 0) {
|
||||
authStore.checkAuth()
|
||||
router.push({name: 'user.login'})
|
||||
await authStore.checkAuth()
|
||||
await router.push({name: 'user.login'})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import {describe, it, expect} from 'vitest'
|
||||
import {hourToDaytime} from "./hourToDaytime"
|
||||
|
||||
function dateWithHour(hours: number): Date {
|
||||
const newDate = new Date()
|
||||
newDate.setHours(hours, 0, 0,0 )
|
||||
return newDate
|
||||
}
|
||||
|
||||
describe('Salutation', () => {
|
||||
it('shows the right salutation in the night', () => {
|
||||
const salutation = hourToDaytime(dateWithHour(4))
|
||||
expect(salutation).toBe('night')
|
||||
})
|
||||
it('shows the right salutation in the morning', () => {
|
||||
const salutation = hourToDaytime(dateWithHour(8))
|
||||
expect(salutation).toBe('morning')
|
||||
})
|
||||
it('shows the right salutation in the day', () => {
|
||||
const salutation = hourToDaytime(dateWithHour(13))
|
||||
expect(salutation).toBe('day')
|
||||
})
|
||||
it('shows the right salutation in the night', () => {
|
||||
const salutation = hourToDaytime(dateWithHour(20))
|
||||
expect(salutation).toBe('evening')
|
||||
})
|
||||
it('shows the right salutation in the night again', () => {
|
||||
const salutation = hourToDaytime(dateWithHour(23))
|
||||
expect(salutation).toBe('night')
|
||||
})
|
||||
})
|
|
@ -0,0 +1,14 @@
|
|||
import type { Daytime } from '@/composables/useDaytimeSalutation'
|
||||
|
||||
export function hourToDaytime(now: Date): Daytime {
|
||||
const hours = now.getHours()
|
||||
|
||||
const daytimeMap = {
|
||||
night: hours < 5 || hours > 23,
|
||||
morning: hours < 11,
|
||||
day: hours < 18,
|
||||
evening: hours < 23,
|
||||
} as Record<Daytime, boolean>
|
||||
|
||||
return (Object.keys(daytimeMap) as Daytime[]).find((daytime) => daytimeMap[daytime]) || 'night'
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
const LAST_VISITED_KEY = 'lastVisited'
|
||||
|
||||
export const saveLastVisited = (name: string, params: object) => {
|
||||
localStorage.setItem(LAST_VISITED_KEY, JSON.stringify({name, params}))
|
||||
export const saveLastVisited = (name: string, params: object, query: object) => {
|
||||
localStorage.setItem(LAST_VISITED_KEY, JSON.stringify({name, params, query}))
|
||||
}
|
||||
|
||||
export const getLastVisited = () => {
|
||||
|
|
|
@ -8,7 +8,7 @@ import {i18n} from '@/i18n'
|
|||
|
||||
const locales = {en: enGB, de, ch: de, fr, ru}
|
||||
|
||||
const dateIsValid = date => {
|
||||
export function dateIsValid(date) {
|
||||
if (date === null) {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Dobrou noc {username}",
|
||||
"welcomeMorning": "Dobré ráno {username}",
|
||||
"welcomeDay": "Ahoj {username}",
|
||||
"welcomeEvening": "Dobrý večer {username}",
|
||||
"welcomeNight": "Dobrou noc {username}!",
|
||||
"welcomeMorning": "Dobré ráno {username}!",
|
||||
"welcomeDay": "Ahoj {username}!",
|
||||
"welcomeEvening": "Dobrý večer {username}!",
|
||||
"lastViewed": "Naposledy zobrazeno",
|
||||
"list": {
|
||||
"newText": "Můžete vytvořit nový seznam pro své nové úkoly:",
|
||||
|
@ -77,7 +77,7 @@
|
|||
"newName": "Nové jméno",
|
||||
"savedSuccess": "Nastavení bylo úspěšně aktualizováno.",
|
||||
"emailReminders": "Posílat mi připomenutí pro úkoly e-mailem",
|
||||
"overdueReminders": "Send me a summary of my undone overdue tasks every day",
|
||||
"overdueReminders": "Pošlete mi každý den shrnutí mých zpožděných úkolů",
|
||||
"discoverableByName": "Nechat ostatní uživatele mě najít podle jména",
|
||||
"discoverableByEmail": "Nechat ostatní uživatele mě najít podle e-mailu",
|
||||
"playSoundWhenDone": "Přehrát zvuk při označení úkolů jako hotovo",
|
||||
|
@ -87,7 +87,7 @@
|
|||
"language": "Jazyk",
|
||||
"defaultList": "Výchozí seznam",
|
||||
"timezone": "Časové pásmo",
|
||||
"overdueTasksRemindersTime": "Overdue tasks reminder email time"
|
||||
"overdueTasksRemindersTime": "Čas odeslání emailu o zpožděných úkolech"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Dvoufaktorové ověření",
|
||||
|
@ -169,10 +169,18 @@
|
|||
"title": "Název seznamu",
|
||||
"color": "Barva",
|
||||
"lists": "Seznamy",
|
||||
"list": {
|
||||
"title": "Seznam",
|
||||
"add": "Přidat",
|
||||
"addPlaceholder": "Přidat nový úkol…",
|
||||
"empty": "Tento seznam je nyní prázdný.",
|
||||
"newTaskCta": "Vytvořit nový úkol.",
|
||||
"editTask": "Upravit úkol"
|
||||
},
|
||||
"search": "Začni psát pro vyhledání seznamu…",
|
||||
"searchSelect": "Klikněte nebo stiskněte Enter pro výběr tohoto seznamu",
|
||||
"shared": "Sdílené seznamy",
|
||||
"noDescriptionAvailable": "No list description is available.",
|
||||
"noDescriptionAvailable": "Popis seznamu není k dispozici.",
|
||||
"create": {
|
||||
"header": "Nový seznam",
|
||||
"titlePlaceholder": "Název seznamu přijde sem…",
|
||||
|
@ -244,8 +252,8 @@
|
|||
"removeText": "Jste si jisti, že chcete odstranit tento sdílený odkaz? K tomuto seznamu již nebude možné přistupovat s tímto sdíleným odkazem. Tuto akci nelze vrátit zpět!",
|
||||
"createSuccess": "Sdílený odkaz byl úspěšně vytvořen.",
|
||||
"deleteSuccess": "Sdílený odkaz byl úspěšně smazán",
|
||||
"view": "View",
|
||||
"sharedBy": "Shared by {0}"
|
||||
"view": "Zobrazit",
|
||||
"sharedBy": "Sdíleno {0}"
|
||||
},
|
||||
"userTeam": {
|
||||
"typeUser": "uživatel | uživatelé",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Smazat"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Seznam",
|
||||
"add": "Přidat",
|
||||
"addPlaceholder": "Přidat nový úkol…",
|
||||
"empty": "Tento seznam je nyní prázdný.",
|
||||
"newTaskCta": "Vytvořit nový úkol.",
|
||||
"editTask": "Upravit úkol"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Zobrazit úkoly, které nemají nastavené datum",
|
||||
|
@ -297,23 +297,23 @@
|
|||
"title": "Kanban",
|
||||
"limit": "Limit: {limit}",
|
||||
"noLimit": "Nenastaveno",
|
||||
"doneBucket": "Kbelík \"Hotovo\"",
|
||||
"doneBucketHint": "Všechny úkoly přesunuté do tohoto kbelíku budou automaticky označeny jako dokončené.",
|
||||
"doneBucketHintExtended": "Všechny úkoly přesunuté do kbelíku \"Hotovo\" budou označeny jako dokončené automaticky. Všechny úkoly označené jako dokončené odjinud sem budou přesunuty také.",
|
||||
"doneBucketSavedSuccess": "Kbelík \"Hotovo\" byl úspěšně uložen.",
|
||||
"deleteLast": "Nelze odstranit poslední kbelík.",
|
||||
"doneBucket": "Sloupec \"Hotovo\"",
|
||||
"doneBucketHint": "Všechny úkoly přesunuté do tohoto sloupce budou automaticky označeny jako dokončené.",
|
||||
"doneBucketHintExtended": "Všechny úkoly přesunuté do sloupce \"Hotovo\" budou označeny jako dokončené automaticky. Všechny úkoly označené jako dokončené jinde sem budou přesunuty také.",
|
||||
"doneBucketSavedSuccess": "Sloupec \"Hotovo\" byl úspěšně uložen.",
|
||||
"deleteLast": "Nelze odstranit poslední sloupec.",
|
||||
"addTaskPlaceholder": "Zadejte nový název úkolu…",
|
||||
"addTask": "Přidat úkol",
|
||||
"addAnotherTask": "Přidat další úkol",
|
||||
"addBucket": "Vytvořit nový kbelík",
|
||||
"addBucketPlaceholder": "Zadejte název nového kbelíku…",
|
||||
"deleteHeaderBucket": "Smazat kbelík",
|
||||
"deleteBucketText1": "Opravdu chcete smazat tento kbelík?",
|
||||
"deleteBucketText2": "Toto neodstraní žádné úkoly, ale přesune je do výchozího kbelíku.",
|
||||
"deleteBucketSuccess": "Kbelík byl úspěšně smazán.",
|
||||
"bucketTitleSavedSuccess": "Název kbelíku byl úspěšně uložen.",
|
||||
"bucketLimitSavedSuccess": "Limit kbelíku byl úspěšně uložen.",
|
||||
"collapse": "Sbalit tento kbelík"
|
||||
"addBucket": "Vytvořit nový sloupec",
|
||||
"addBucketPlaceholder": "Zadejte název nového sloupce…",
|
||||
"deleteHeaderBucket": "Smazat sloupec",
|
||||
"deleteBucketText1": "Opravdu chcete smazat tento sloupec?",
|
||||
"deleteBucketText2": "Toto nesmaže žádné úkoly, ale přesune je do výchozího sloupce.",
|
||||
"deleteBucketSuccess": "Sloupec byl úspěšně smazán.",
|
||||
"bucketTitleSavedSuccess": "Název sloupce byl úspěšně uložen.",
|
||||
"bucketLimitSavedSuccess": "Limit sloupce byl úspěšně uložen.",
|
||||
"collapse": "Sbalit tento sloupec"
|
||||
},
|
||||
"pseudo": {
|
||||
"favorites": {
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Aktualizováno"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Zde se nemůžete odhlásit, protože jste přihlášeni k odběru {entity} prostřednictvím jeho {parent}.",
|
||||
"subscribed": "Aktuálně jste přihlášeni k odběru {entity} a budete dostávat oznámení o změnách.",
|
||||
"notSubscribed": "Nejste přihlášeni k odběru {entity} a nebudete dostávat upozornění na změny.",
|
||||
"subscribedListThroughParentNamespace": "Zde se nemůžete odhlásit, protože jste přihlášeni k odběru tohoto seznamu prostřednictvím jeho prostoru.",
|
||||
"subscribedTaskThroughParentNamespace": "Zde se nemůžete odhlásit, protože jste přihlášeni k odběru tohoto úkolu prostřednictvím jeho prostoru.",
|
||||
"subscribedTaskThroughParentList": "Zde se nemůžete odhlásit, protože jste přihlášeni k odběru tohoto úkolu prostřednictvím jeho seznamu.",
|
||||
"subscribedNamespace": "Nyní jste přihlášeni k odběru tohoto prostoru a budete dostávat oznámení o změnách.",
|
||||
"notSubscribedNamespace": "Nejste přihlášeni k odběru tohoto prostoru, takže nebudete dostávat upozornění na změny.",
|
||||
"subscribedList": "Nyní jste přihlášeni k odběru tohoto seznamu a budete dostávat oznámení o změnách.",
|
||||
"notSubscribedList": "Nejste přihlášeni k odběru tohoto seznamu, takže nebudete dostávat upozornění na změny.",
|
||||
"subscribedTask": "Nyní jste přihlášeni k odběru tohoto úkolu a budete dostávat oznámení o změnách.",
|
||||
"notSubscribedTask": "Nejste přihlášeni k odběru tohoto úkolu, takže nebudete dostávat upozornění na změny.",
|
||||
"subscribe": "Odebírat",
|
||||
"unsubscribe": "Odhlásit odběr",
|
||||
"subscribeSuccess": "Nyní jste přihlášeni k odběru {entity}",
|
||||
"unsubscribeSuccess": "Nyní jste odhlášeni od odběru {entity}"
|
||||
"subscribeSuccessNamespace": "Nyní jste přihlášeni k tomuto prostoru",
|
||||
"unsubscribeSuccessNamespace": "Nyní jste odhlášeni od tohoto prostoru",
|
||||
"subscribeSuccessList": "Nyní jste přihlášeni k tomuto seznamu",
|
||||
"unsubscribeSuccessList": "Nyní jste odhlášeni od tohoto seznamu",
|
||||
"subscribeSuccessTask": "Nyní jste přihlášeni k tomuto úkolu",
|
||||
"unsubscribeSuccessTask": "Nyní jste odhlášeni od tohoto úkolu"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Přílohy",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Smazat tuto přílohu",
|
||||
"deleteText1": "Opravdu chcete odstranit přílohu {filename}?",
|
||||
"copyUrl": "Kopírovat URL",
|
||||
"copyUrlTooltip": "Kopírovat URL této přílohy pro použití v textu"
|
||||
"copyUrlTooltip": "Kopírovat URL této přílohy pro použití v textu",
|
||||
"setAsCover": "Použij titulní obrázek",
|
||||
"unsetAsCover": "Odstraň titulní obrázek",
|
||||
"successfullyChangedCoverImage": "Titulní obrázek byl úspěšně změněn.",
|
||||
"usedAsCover": "Titulní obrázek"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Komentáře",
|
||||
|
@ -701,7 +715,7 @@
|
|||
"comment": "Komentář",
|
||||
"delete": "Smazat tento komentář",
|
||||
"deleteText1": "Opravdu chcete smazat tento komentář?",
|
||||
"deleteSuccess": "The comment was deleted successfully.",
|
||||
"deleteSuccess": "Komentář byl úspěšně odstraněn.",
|
||||
"addedSuccess": "Komentář byl úspěšně přidán."
|
||||
},
|
||||
"deferDueDate": {
|
||||
|
@ -728,16 +742,16 @@
|
|||
"removeSuccess": "Štítek byl úspěšně odstraněn.",
|
||||
"addCreateSuccess": "Štítek byl úspěšně vytvořen a přidán.",
|
||||
"delete": {
|
||||
"header": "Delete this label",
|
||||
"text1": "Are you sure you want to delete this label?",
|
||||
"text2": "This will remove it from all tasks and cannot be restored."
|
||||
"header": "Smazat tento štítek",
|
||||
"text1": "Opravdu chcete smazat tento štítek?",
|
||||
"text2": "Odebere štítek ze všech úkolů a nelze to vrátit zpět."
|
||||
}
|
||||
},
|
||||
"priority": {
|
||||
"unset": "Zrušit nastavení",
|
||||
"low": "Nízká",
|
||||
"medium": "Střední",
|
||||
"high": "High",
|
||||
"high": "Vysoká",
|
||||
"urgent": "Naléhavé",
|
||||
"doNow": "UDĚLEJ TO HNED"
|
||||
},
|
||||
|
@ -781,7 +795,7 @@
|
|||
"weeks": "Týdny",
|
||||
"months": "Měsíce",
|
||||
"years": "Roky",
|
||||
"invalidAmount": "Please enter more than 0."
|
||||
"invalidAmount": "Zadejte prosím více než 0."
|
||||
},
|
||||
"quickAddMagic": {
|
||||
"hint": "Můžeš použít Kouzelné rychlé přidání",
|
||||
|
@ -791,15 +805,15 @@
|
|||
"multiple": "Toto můžete použít několikrát.",
|
||||
"label1": "Chcete-li přidat štítek, jednoduše napište před název štítku {prefix}.",
|
||||
"label2": "Vikunja nejprve zkontroluje, zda štítek již existuje a vytvoří jej, pokud ne.",
|
||||
"label3": "To use spaces, simply add a \" or ' around the label name.",
|
||||
"label3": "Chcete-li použít mezery, uzavřete název štítku do \" nebo '.",
|
||||
"label4": "Například: {prefix}\"Štítek s mezerami\".",
|
||||
"priority1": "Chcete-li nastavit prioritu úkolu, přidejte číslo 1-5, s prefixem {prefix}.",
|
||||
"priority2": "Čím vyšší číslo, tím vyšší priorita.",
|
||||
"assignees": "Chcete-li přímo přiřadit úkol k uživateli, přidejte k úkolu jejich uživatelské jméno s prefixem {prefix}.",
|
||||
"list1": "Chcete-li nastavit seznam, ve kterém se má úkol zobrazit, zadejte jeho název s předponou {prefix}.",
|
||||
"list2": "Toto vrátí chybu, pokud seznam neexistuje.",
|
||||
"list3": "To use spaces, simply add a \" or ' around the list name.",
|
||||
"list4": "For example: {prefix}\"List with spaces\".",
|
||||
"list3": "Chcete-li použít mezery, uzavřete název seznamu do \" nebo '.",
|
||||
"list4": "Například: {prefix}\"Štítek s mezerami\".",
|
||||
"dateAndTime": "Datum a čas",
|
||||
"date": "Jakékoliv datum bude použito jako datum dokončení nového úkolu. Můžete použít data v kterémkoli z těchto formátů:",
|
||||
"dateWeekday": "každý pracovní den použije další datum s tímto datem",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Opravdu chcete odebrat tohoto uživatele z týmu?",
|
||||
"text2": "Ztratí přístup ke všem seznamům a prostorům, k nimž má tento tým přístup. To NEMŮŽE BÝT VZATO ZPĚT!",
|
||||
"success": "Uživatel byl úspěšně odstraněn z týmu."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Opustit tým",
|
||||
"text1": "Opravdu chcete opustit tento tým?",
|
||||
"text2": "Ztratíte přístup ke všem seznamům a prostorům, k nimž má tento tým přístup. Pokud změníte názor, budete potřebovat správce týmu, aby vás znovu přidal.",
|
||||
"success": "Úspěšně jste opustili tým."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -870,8 +890,8 @@
|
|||
"related": "Upravit související úkoly tohoto úkolu",
|
||||
"color": "Změnit barvu tohoto úkolu",
|
||||
"move": "Přesunout tento úkol do jiného seznamu",
|
||||
"reminder": "Manage reminders of this task",
|
||||
"description": "Toggle editing of the task description"
|
||||
"reminder": "Spravovat připomenutí této úlohy",
|
||||
"description": "Přepnout úpravy popisu úkolu"
|
||||
},
|
||||
"list": {
|
||||
"title": "Zobrazení seznamů",
|
||||
|
@ -943,7 +963,7 @@
|
|||
}
|
||||
},
|
||||
"date": {
|
||||
"locale": "cs-CZ",
|
||||
"locale": "cs",
|
||||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
|
@ -1013,11 +1033,11 @@
|
|||
"8002": "Štítek neexistuje.",
|
||||
"8003": "K tomuto štítku nemáte přístup.",
|
||||
"9001": "Právo je neplatné.",
|
||||
"10001": "Kbelík neexistuje.",
|
||||
"10002": "Kbelík nepatří do tohoto seznamu.",
|
||||
"10003": "Poslední kbelík v seznamu nelze odstranit.",
|
||||
"10004": "Nemůžete přidat úkol do tohoto kbelíku, protože již překročil limit úkolů, které do něj můžete uložit.",
|
||||
"10005": "Na seznam může být pouze jeden kbelík \"Hotovo\".",
|
||||
"10001": "Sloupec neexistuje.",
|
||||
"10002": "Sloupec nepatří do tohoto seznamu.",
|
||||
"10003": "Poslední sloupec v seznamu nelze odstranit.",
|
||||
"10004": "Nemůžete přidat úkol do tohoto sloupce, protože již překročil limit úkolů, které do něj můžete uložit.",
|
||||
"10005": "Pro seznam může být pouze jeden sloupec \"Hotovo\".",
|
||||
"11001": "Uložený filtr neexistuje.",
|
||||
"11002": "Uložené filtry nejsou k dispozici pro sdílení odkazů.",
|
||||
"12001": "Typ předplatného je neplatný.",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Gute Nacht, {username}",
|
||||
"welcomeMorning": "Guten Morgen, {username}",
|
||||
"welcomeDay": "Hallo, {username}",
|
||||
"welcomeEvening": "Guten Abend, {username}",
|
||||
"welcomeNight": "Gute Nacht, {username}!",
|
||||
"welcomeMorning": "Guten Morgen, {username}!",
|
||||
"welcomeDay": "Hallo {username}!",
|
||||
"welcomeEvening": "Guten Abend, {username}!",
|
||||
"lastViewed": "Zuletzt angesehen",
|
||||
"list": {
|
||||
"newText": "Du kannst eine neue Liste für deine neuen Aufgaben erstellen:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Listentitel",
|
||||
"color": "Farbe",
|
||||
"lists": "Listen",
|
||||
"list": {
|
||||
"title": "Liste",
|
||||
"add": "Hinzufügen",
|
||||
"addPlaceholder": "Eine neue Aufgabe hinzufügen …",
|
||||
"empty": "Diese Liste ist derzeit leer.",
|
||||
"newTaskCta": "Eine neue Aufgabe erstellen.",
|
||||
"editTask": "Aufgabe bearbeiten"
|
||||
},
|
||||
"search": "Tippe, um nach einer Liste zu suchen…",
|
||||
"searchSelect": "Klicke auf oder drücke die Eingabetaste, um diese Liste auszuwählen",
|
||||
"shared": "Geteilte Listen",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Löschen"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Liste",
|
||||
"add": "Hinzufügen",
|
||||
"addPlaceholder": "Eine neue Aufgabe hinzufügen …",
|
||||
"empty": "Diese Liste ist derzeit leer.",
|
||||
"newTaskCta": "Eine neue Aufgabe erstellen.",
|
||||
"editTask": "Aufgabe bearbeiten"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Aufgaben anzeigen, für die keine Daten festgelegt sind",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"create": {
|
||||
"title": "Neuer Namespace",
|
||||
"titleRequired": "Bitte gebe einen Titel an.",
|
||||
"explanation": "Ein Namespace ist eine Sammlung von Listen, die du teilen und zur Organisation verwenden kannst. Jede Liste zu einem Namespace.",
|
||||
"explanation": "Ein Namespace ist eine Sammlung von Listen, die du teilen und zur Organisation verwenden kannst. Jede Liste gehört zu einem Namespace.",
|
||||
"tooltip": "Was ist ein Namespace?",
|
||||
"success": "Der Namespace wurde erfolgreich erstellt."
|
||||
},
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Aktualisiert"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Du kannst hier nicht de-abonnieren, da du diese {entity} durch {parent} abonniert hast.",
|
||||
"subscribed": "Du erhältst Benachrichtigungen zu dieser {entity}.",
|
||||
"notSubscribed": "Du hast diese {entity} nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribedListThroughParentNamespace": "Du kannst hier nicht de-abonnieren, da du diese Liste über ihren Namespace abonniert hast.",
|
||||
"subscribedTaskThroughParentNamespace": "Du kannst hier nicht de-abonnieren, da du diese Aufgabe über ihren Namespace abonniert hast.",
|
||||
"subscribedTaskThroughParentList": "Du kannst hier nicht de-abonnieren, da du diese Aufgabe über ihre Liste abonniert hast.",
|
||||
"subscribedNamespace": "Du hast diesen Namespace abonniert und erhältst Benachrichtigungen über Änderungen.",
|
||||
"notSubscribedNamespace": "Du hast diesen Namespace nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribedList": "Du hast diese Liste abonniert und erhältst Benachrichtigungen über Änderungen.",
|
||||
"notSubscribedList": "Du hast diese Liste nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribedTask": "Du hast diese Aufgabe abonniert und erhältst Benachrichtigungen über Änderungen.",
|
||||
"notSubscribedTask": "Du hast diese Aufgabe nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribe": "Abonnieren",
|
||||
"unsubscribe": "Abbestellen",
|
||||
"subscribeSuccess": "Du hast jetzt diese {entity} abonniert",
|
||||
"unsubscribeSuccess": "Du hast diese {entity} jetzt abbestellt"
|
||||
"subscribeSuccessNamespace": "Du hast diesen Namespace jetzt abonniert",
|
||||
"unsubscribeSuccessNamespace": "Du hast diesen Namespace jetzt nicht mehr abonniert",
|
||||
"subscribeSuccessList": "Du hast diese Liste jetzt abonniert",
|
||||
"unsubscribeSuccessList": "Du hast diese Liste jetzt nicht mehr abonniert",
|
||||
"subscribeSuccessTask": "Du hast diese Aufgabe jetzt abonniert",
|
||||
"unsubscribeSuccessTask": "Du hast diese Aufgabe jetzt nicht mehr abonniert"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Anhänge",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Diesen Anhang löschen",
|
||||
"deleteText1": "Soll der Anhang {filename} gelöscht werden?",
|
||||
"copyUrl": "URL kopieren",
|
||||
"copyUrlTooltip": "Die URL dieses Anhangs zur Verwendung im Text kopieren"
|
||||
"copyUrlTooltip": "Die URL dieses Anhangs zur Verwendung im Text kopieren",
|
||||
"setAsCover": "Als Titelbild setzen",
|
||||
"unsetAsCover": "Titelbild entfernen",
|
||||
"successfullyChangedCoverImage": "Das Titelbild wurde erfolgreich geändert.",
|
||||
"usedAsCover": "Titelbild"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Kommentare",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Bist du sicher, dass du diese:n Benutzer:in aus dem Team entfernen willst?",
|
||||
"text2": "Diese:r Benutzer:in verliert den Zugriff auf alle Listen und Namespaces auf die dieses Team Zugriff hat. Dies kann nicht rückgängig gemacht werden!",
|
||||
"success": "Der:die Benutzer:in wurde erfolgreich aus dem Team gelöscht."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Team verlassen",
|
||||
"text1": "Bist du sicher, dass du dieses Team verlassen willst?",
|
||||
"text2": "Du wirst Zugriff auf alle Listen und Namespaces verlieren, auf die dieses Team Zugriff hat. Wenn du deine Meinung änderst, musst du durch einen Team-Admin wieder hinzugefügt werden.",
|
||||
"success": "Du hast das Team erfolgreich verlassen."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Guet Nacht, {username}",
|
||||
"welcomeMorning": "Guete Morgä, {username}",
|
||||
"welcomeDay": "Hoi {username}",
|
||||
"welcomeEvening": "Guete Abig, {username}",
|
||||
"welcomeNight": "Gute Nacht, {username}!",
|
||||
"welcomeMorning": "Guten Morgen, {username}!",
|
||||
"welcomeDay": "Hallo {username}!",
|
||||
"welcomeEvening": "Guten Abend, {username}!",
|
||||
"lastViewed": "Zletscht ahglueget",
|
||||
"list": {
|
||||
"newText": "Du chasch e Liste für dini neue Uufgabe erstelle:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Liste Titl",
|
||||
"color": "Farb",
|
||||
"lists": "Listene",
|
||||
"list": {
|
||||
"title": "Liste",
|
||||
"add": "Hinzuefüege",
|
||||
"addPlaceholder": "E neui Uufgab erstelle…",
|
||||
"empty": "D'Liste isch momentan leer.",
|
||||
"newTaskCta": "Neui Uufgab erstelle.",
|
||||
"editTask": "Uufgab bearbeite"
|
||||
},
|
||||
"search": "Schriib, um nachere Liste z'sueche…",
|
||||
"searchSelect": "Druck uf Enter um die Liste uuszwähle",
|
||||
"shared": "Teilti Liste",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Chüble"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Liste",
|
||||
"add": "Hinzuefüege",
|
||||
"addPlaceholder": "E neui Uufgab erstelle…",
|
||||
"empty": "D'Liste isch momentan leer.",
|
||||
"newTaskCta": "Neui Uufgab erstelle.",
|
||||
"editTask": "Uufgab bearbeite"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Zeig Uufgabe, wo kei Date hend",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"create": {
|
||||
"title": "Neuer Namespace",
|
||||
"titleRequired": "Bitte gib en Titl ah.",
|
||||
"explanation": "Ein Namespace ist eine Sammlung von Listen, die du teilen und zur Organisation verwenden kannst. Jede Liste zu einem Namespace.",
|
||||
"explanation": "Ein Namespace ist eine Sammlung von Listen, die du teilen und zur Organisation verwenden kannst. Jede Liste gehört zu einem Namespace.",
|
||||
"tooltip": "Was isch en Namensruum?",
|
||||
"success": "Namensruum erstellt."
|
||||
},
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Aktualisiert"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Du chasch da nid deabonnierä, weil du zu dere {entity} durch {parent} dezue abonniert bisch.",
|
||||
"subscribed": "Du bisch momentan zu dere {entity} abonniert und bechunsch Benachrichtigunge für Änderige.",
|
||||
"notSubscribed": "Du bisch momentan nid zu dere {entity} abonniert und bechunsch kei Benachrichtigunge für Änderige.",
|
||||
"subscribedListThroughParentNamespace": "Du kannst hier nicht de-abonnieren, da du diese Liste über ihren Namespace abonniert hast.",
|
||||
"subscribedTaskThroughParentNamespace": "Du kannst hier nicht de-abonnieren, da du diese Aufgabe über ihren Namespace abonniert hast.",
|
||||
"subscribedTaskThroughParentList": "Du kannst hier nicht de-abonnieren, da du diese Aufgabe über ihre Liste abonniert hast.",
|
||||
"subscribedNamespace": "Du hast diesen Namespace abonniert und erhältst Benachrichtigungen über Änderungen.",
|
||||
"notSubscribedNamespace": "Du hast diesen Namespace nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribedList": "Du hast diese Liste abonniert und erhältst Benachrichtigungen über Änderungen.",
|
||||
"notSubscribedList": "Du hast diese Liste nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribedTask": "Du hast diese Aufgabe abonniert und erhältst Benachrichtigungen über Änderungen.",
|
||||
"notSubscribedTask": "Du hast diese Aufgabe nicht abonniert und erhältst keine Benachrichtigungen über Änderungen.",
|
||||
"subscribe": "Abooniere",
|
||||
"unsubscribe": "Deabonniere",
|
||||
"subscribeSuccess": "Du hesch die {entity} abonniert",
|
||||
"unsubscribeSuccess": "Du hesch die {entity} deabonniert"
|
||||
"subscribeSuccessNamespace": "Du hast diesen Namespace jetzt abonniert",
|
||||
"unsubscribeSuccessNamespace": "Du hast diesen Namespace jetzt nicht mehr abonniert",
|
||||
"subscribeSuccessList": "Du hast diese Liste jetzt abonniert",
|
||||
"unsubscribeSuccessList": "Du hast diese Liste jetzt nicht mehr abonniert",
|
||||
"subscribeSuccessTask": "Du hast diese Aufgabe jetzt abonniert",
|
||||
"unsubscribeSuccessTask": "Du hast diese Aufgabe jetzt nicht mehr abonniert"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Aahhäng",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "De Aahhang lösche",
|
||||
"deleteText1": "Bisch du dir sicher, dass du de Aahang {filename} lösche wetsch?",
|
||||
"copyUrl": "URL Kopierä",
|
||||
"copyUrlTooltip": "D'Url vo dem Aahang kopiere, um sie im Text zbruuche"
|
||||
"copyUrlTooltip": "D'Url vo dem Aahang kopiere, um sie im Text zbruuche",
|
||||
"setAsCover": "Als Titelbild setzen",
|
||||
"unsetAsCover": "Titelbild entfernen",
|
||||
"successfullyChangedCoverImage": "Das Titelbild wurde erfolgreich geändert.",
|
||||
"usedAsCover": "Titelbild"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Kommentär",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Bisch du dir sicher, dass du de Benutzer usm Team werfe wetsch?",
|
||||
"text2": "Diese:r Benutzer:in verliert den Zugriff auf alle Listen und Namespaces auf die dieses Team Zugriff hat. Dies kann nicht rückgängig gemacht werden!",
|
||||
"success": "Benutzer erfolgriich usegworfe."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Team verlassen",
|
||||
"text1": "Bist du sicher, dass du dieses Team verlassen willst?",
|
||||
"text2": "Du wirst Zugriff auf alle Listen und Namespaces verlieren, auf die dieses Team Zugriff hat. Wenn du deine Meinung änderst, musst du durch einen Team-Admin wieder hinzugefügt werden.",
|
||||
"success": "Du hast das Team erfolgreich verlassen."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,7 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": "List",
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -675,13 +676,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -693,7 +704,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -842,6 +857,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
|
||||
"totpTitle": "Two Factor Authentication Code",
|
||||
"totpPlaceholder": "e.g. 123456",
|
||||
"login": "Login",
|
||||
"login": "Iniciar sesión",
|
||||
"createAccount": "Create account",
|
||||
"loginWith": "Log in with {provider}",
|
||||
"authenticating": "Authenticating…",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Bonne nuit {username}",
|
||||
"welcomeMorning": "Bonjour {username}",
|
||||
"welcomeDay": "Salut {username}",
|
||||
"welcomeEvening": "Bonsoir {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Dernière consultation",
|
||||
"list": {
|
||||
"newText": "Tu peux créer une nouvelle liste pour tes nouvelles tâches :",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Nom de la liste",
|
||||
"color": "Couleur",
|
||||
"lists": "Listes",
|
||||
"list": {
|
||||
"title": "Liste",
|
||||
"add": "Ajouter",
|
||||
"addPlaceholder": "Ajouter une nouvelle tâche…",
|
||||
"empty": "Cette liste est actuellement vide.",
|
||||
"newTaskCta": "Créer une nouvelle tâche.",
|
||||
"editTask": "Modifier la tâche"
|
||||
},
|
||||
"search": "Écris pour rechercher une liste…",
|
||||
"searchSelect": "Clique ou appuie sur la touche Entrée pour sélectionner cette liste",
|
||||
"shared": "Listes partagées",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Supprimer"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Liste",
|
||||
"add": "Ajouter",
|
||||
"addPlaceholder": "Ajouter une nouvelle tâche…",
|
||||
"empty": "Cette liste est actuellement vide.",
|
||||
"newTaskCta": "Créer une nouvelle tâche.",
|
||||
"editTask": "Modifier la tâche"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Afficher les tâches pour lesquelles aucune date n’a été fixée",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Mis à jour"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Tu ne peux pas te désabonner ici car tu es abonné·e à cette {entity} par le biais de son {parent}.",
|
||||
"subscribed": "Tu es actuellement abonné·e à cette {entity} et recevras des notifications pour les changements.",
|
||||
"notSubscribed": "Tu n’es pas abonné·e à cette {entity} et ne recevras pas de notifications pour les changements.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "S’abonner",
|
||||
"unsubscribe": "Se désabonner",
|
||||
"subscribeSuccess": "Tu es maintenant abonné·e à cette {entity}",
|
||||
"unsubscribeSuccess": "Tu es maintenant désabonné·e de cette {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Pièces jointes",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Supprimer cette pièce jointe",
|
||||
"deleteText1": "Supprimer la pièce jointe {filename} ?",
|
||||
"copyUrl": "Copier l’URL",
|
||||
"copyUrlTooltip": "Copier l’URL de cette pièce jointe pour l’utiliser dans le texte"
|
||||
"copyUrlTooltip": "Copier l’URL de cette pièce jointe pour l’utiliser dans le texte",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Commentaires",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Retirer cette personne de l’équipe ?",
|
||||
"text2": "Ils perdront l'accès à toutes les listes et espaces de noms auxquels cette équipe a accès. Ceci NE PEUT PAS ÊTRE ANNULÉ !",
|
||||
"success": "Utilisateur·rice retiré·e de l’équipe."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Buonanotte {username}",
|
||||
"welcomeMorning": "Buongiorno {username}",
|
||||
"welcomeDay": "Ciao {username}",
|
||||
"welcomeEvening": "Buonasera {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Ultima visualizzazione",
|
||||
"list": {
|
||||
"newText": "È possibile creare una nuova lista per le nuove attività:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Titolo della Lista",
|
||||
"color": "Colore",
|
||||
"lists": "Liste",
|
||||
"list": {
|
||||
"title": "Lista",
|
||||
"add": "Aggiungi",
|
||||
"addPlaceholder": "Aggiungi una nuova attività…",
|
||||
"empty": "Questa lista è attualmente vuota.",
|
||||
"newTaskCta": "Crea una nuova attività.",
|
||||
"editTask": "Modifica Attività"
|
||||
},
|
||||
"search": "Digita per cercare una lista…",
|
||||
"searchSelect": "Fare clic o premere invio per selezionare questa lista",
|
||||
"shared": "Liste Condivise",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Elimina"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Lista",
|
||||
"add": "Aggiungi",
|
||||
"addPlaceholder": "Aggiungi una nuova attività…",
|
||||
"empty": "Questa lista è attualmente vuota.",
|
||||
"newTaskCta": "Crea una nuova attività.",
|
||||
"editTask": "Modifica Attività"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Mostra attività che non hanno date impostate",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Aggiornato"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Non puoi annullare l'iscrizione qui perché sei iscritto a questo {entity} attraverso il suo {parent}.",
|
||||
"subscribed": "Sei attualmente iscritto a questo {entity} e riceverai notifiche per le modifiche.",
|
||||
"notSubscribed": "Non sei iscritto a questo {entity} e non riceverai notifiche per le modifiche.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Iscriviti",
|
||||
"unsubscribe": "Disiscriviti",
|
||||
"subscribeSuccess": "Ti sei iscritto a questo {entity}",
|
||||
"unsubscribeSuccess": "Ti sei disiscritto a questo {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Allegati",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Elimina questo allegato",
|
||||
"deleteText1": "Sei sicuro di voler eliminare l'allegato {filename}?",
|
||||
"copyUrl": "Copia URL",
|
||||
"copyUrlTooltip": "Copia l'URL di questo allegato per usarlo nel testo"
|
||||
"copyUrlTooltip": "Copia l'URL di questo allegato per usarlo nel testo",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Commenti",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Confermi di voler rimuovere questo utente dal gruppo?",
|
||||
"text2": "Perderanno l'accesso a tutte le liste e i namespace a cui questo gruppo ha accesso. NON PUÒ ESSERE RIPRISTINATO!",
|
||||
"success": "Utente rimosso dal gruppo."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Goede nacht {username}",
|
||||
"welcomeMorning": "Goedemorgen {username}",
|
||||
"welcomeDay": "Hallo {username}",
|
||||
"welcomeEvening": "Goedenavond {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Laatst bekeken",
|
||||
"list": {
|
||||
"newText": "Je kan een nieuwe lijst maken voor je nieuwe taken:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Lijst titel",
|
||||
"color": "Kleur",
|
||||
"lists": "Lijsten",
|
||||
"list": {
|
||||
"title": "Lijst",
|
||||
"add": "Toevoegen",
|
||||
"addPlaceholder": "Voeg een nieuwe taak toe…",
|
||||
"empty": "Deze lijst is momenteel leeg.",
|
||||
"newTaskCta": "Creëer een nieuwe taak.",
|
||||
"editTask": "Taak bewerken"
|
||||
},
|
||||
"search": "Typ om naar een lijst te zoeken…",
|
||||
"searchSelect": "Klik of druk op enter om deze lijst te selecteren",
|
||||
"shared": "Gedeelde lijsten",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Verwijderen"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Lijst",
|
||||
"add": "Toevoegen",
|
||||
"addPlaceholder": "Voeg een nieuwe taak toe…",
|
||||
"empty": "Deze lijst is momenteel leeg.",
|
||||
"newTaskCta": "Creëer een nieuwe taak.",
|
||||
"editTask": "Taak bewerken"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Toon taken waarvoor geen datums zijn ingesteld",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Bijgewerkt"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Bijlagen",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Verwijder deze bijlage",
|
||||
"deleteText1": "Weet je zeker dat je de bijlage {filename} wilt verwijderen?",
|
||||
"copyUrl": "URL kopiëren",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Reacties",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Weet je zeker dat je deze gebruiker wilt verwijderen uit het team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Dobrej nocy {username}",
|
||||
"welcomeMorning": "Dzień dobry {username}",
|
||||
"welcomeDay": "Cześć {username}",
|
||||
"welcomeEvening": "Dobry wieczór {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Ostatnio oglądane",
|
||||
"list": {
|
||||
"newText": "Możesz stworzyć nową listę dla swoich nowych zadań:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Tytuł listy",
|
||||
"color": "Kolor",
|
||||
"lists": "Listy",
|
||||
"list": {
|
||||
"title": "Lista",
|
||||
"add": "Dodaj",
|
||||
"addPlaceholder": "Dodaj nowe zadanie…",
|
||||
"empty": "Ta lista jest obecnie pusta.",
|
||||
"newTaskCta": "Utwórz nowe zadanie.",
|
||||
"editTask": "Edytuj zadanie"
|
||||
},
|
||||
"search": "Wpisz, aby wyszukać listę…",
|
||||
"searchSelect": "Kliknij lub naciśnij Enter, aby wybrać tę listę",
|
||||
"shared": "Współdzielone listy",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Usuń"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Lista",
|
||||
"add": "Dodaj",
|
||||
"addPlaceholder": "Dodaj nowe zadanie…",
|
||||
"empty": "Ta lista jest obecnie pusta.",
|
||||
"newTaskCta": "Utwórz nowe zadanie.",
|
||||
"editTask": "Edytuj zadanie"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Pokaż zadania, które nie mają ustawionych dat",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Zaktualizowano"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Nie możesz zrezygnować z subskrypcji, ponieważ subskrybujesz {entity} za pośrednictwem {parent}.",
|
||||
"subscribed": "Obecnie subskrybujesz {entity} i będziesz otrzymywać powiadomienia o zmianach.",
|
||||
"notSubscribed": "Nie subskrybujesz {entity} i nie będziesz otrzymywać powiadomień o zmianach.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subskrybuj",
|
||||
"unsubscribe": "Anuluj subskrypcję",
|
||||
"subscribeSuccess": "Od teraz subskrybujesz {entity}",
|
||||
"unsubscribeSuccess": "Już nie subskrybujesz {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Załączniki",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Usuń ten załącznik",
|
||||
"deleteText1": "Czy na pewno chcesz usunąć załącznik {filename}?",
|
||||
"copyUrl": "Kopiuj URL",
|
||||
"copyUrlTooltip": "Skopiuj adres URL tego załącznika do użycia w tekście"
|
||||
"copyUrlTooltip": "Skopiuj adres URL tego załącznika do użycia w tekście",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Komentarze",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Czy na pewno chcesz usunąć tego użytkownika z zespołu?",
|
||||
"text2": "Utraci on dostęp do wszystkich list i sekcji, do których ma dostęp ten zespół. Tego NIE DA SIĘ COFNĄĆ!",
|
||||
"success": "Użytkownik został pomyślnie usunięty z zespołu."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Boa Noite {username}",
|
||||
"welcomeMorning": "Bom Dia {username}",
|
||||
"welcomeDay": "Olá {username}",
|
||||
"welcomeEvening": "Boa Tarde {username}",
|
||||
"welcomeNight": "Boa Noite {username}!",
|
||||
"welcomeMorning": "Bom Dia {username}!",
|
||||
"welcomeDay": "Olá {username}!",
|
||||
"welcomeEvening": "Boa Tarde {username}!",
|
||||
"lastViewed": "Visto recentemente",
|
||||
"list": {
|
||||
"newText": "Podes criar uma nova lista para as tuas novas tarefas:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Título da Lista",
|
||||
"color": "Cor",
|
||||
"lists": "Listas",
|
||||
"list": {
|
||||
"title": "Lista",
|
||||
"add": "Adicionar",
|
||||
"addPlaceholder": "Adicionar uma nova tarefa…",
|
||||
"empty": "Esta lista está atualmente vazia.",
|
||||
"newTaskCta": "Cria uma nova tarefa.",
|
||||
"editTask": "Editar Tarefa"
|
||||
},
|
||||
"search": "Escreve para pesquisar por uma lista…",
|
||||
"searchSelect": "Clica ou pressiona Enter para selecionar esta lista",
|
||||
"shared": "Listas Partilhadas",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Eliminar"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Lista",
|
||||
"add": "Adicionar",
|
||||
"addPlaceholder": "Adicionar uma nova tarefa…",
|
||||
"empty": "Esta lista está atualmente vazia.",
|
||||
"newTaskCta": "Cria uma nova tarefa.",
|
||||
"editTask": "Editar Tarefa"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Mostrar tarefas que não têm datas atríbuidas",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Atualizado"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Não podes cancelar a tua subscrição aqui porque estás subscrito nesta {entity} através de {parent}.",
|
||||
"subscribed": "Estás atualmente subscrito a esta {entity} e serás notificado de alterações.",
|
||||
"notSubscribed": "Não estás subscrito a esta {entity} e não serás notificado de alterações.",
|
||||
"subscribedListThroughParentNamespace": "Não podes cancelar a tua subscrição aqui porque estás subscrito nesta lista através do seu espaço.",
|
||||
"subscribedTaskThroughParentNamespace": "Não podes cancelar a tua subscrição aqui porque estás subscrito nesta tarefa através do seu espaço.",
|
||||
"subscribedTaskThroughParentList": "Não podes cancelar a tua subscrição aqui porque estás subscrito nesta tarefa através da sua lista.",
|
||||
"subscribedNamespace": "Estás atualmente subscrito a este espaço e serás notificado de alterações.",
|
||||
"notSubscribedNamespace": "Não estás subscrito a este espaço e não serás notificado de alterações.",
|
||||
"subscribedList": "Estás atualmente subscrito a esta lista e serás notificado de alterações.",
|
||||
"notSubscribedList": "Não estás subscrito a esta lista e não serás notificado de alterações.",
|
||||
"subscribedTask": "Estás atualmente subscrito a esta tarefa e serás notificado de alterações.",
|
||||
"notSubscribedTask": "Não estás subscrito a esta tarefa e não serás notificado de alterações.",
|
||||
"subscribe": "Subscrever",
|
||||
"unsubscribe": "Remover Subscrição",
|
||||
"subscribeSuccess": "Estás agora subscrito a esta {entity}",
|
||||
"unsubscribeSuccess": "Não estás mais subcrito a esta {entity}"
|
||||
"subscribeSuccessNamespace": "Estás agora subscrito a este espaço",
|
||||
"unsubscribeSuccessNamespace": "Não estás mais subcrito a este espaço",
|
||||
"subscribeSuccessList": "Estás agora subscrito a esta lista",
|
||||
"unsubscribeSuccessList": "Não estás mais subcrito a esta lista",
|
||||
"subscribeSuccessTask": "Estás agora subscrito a esta tarefa",
|
||||
"unsubscribeSuccessTask": "Não estás mais subcrito a esta tarefa"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Anexos",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Eliminar este anexo",
|
||||
"deleteText1": "Tens a certeza que pretendes eliminar o anexo {filename}?",
|
||||
"copyUrl": "Copiar URL",
|
||||
"copyUrlTooltip": "Copia o url deste anexo para o utilizar no texto"
|
||||
"copyUrlTooltip": "Copia o url deste anexo para o utilizar no texto",
|
||||
"setAsCover": "Criar capa",
|
||||
"unsetAsCover": "Remover capa",
|
||||
"successfullyChangedCoverImage": "A imagem de capa foi alterada com sucesso.",
|
||||
"usedAsCover": "Imagem de capa"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comentários",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Tens a certeza que pretendes remover este utilizador da equipa?",
|
||||
"text2": "Eles perderão o acesso a todas as listas e espaços a que esta equipa tem acesso. Isto NÃO PODER SER REVERTIDO!",
|
||||
"success": "O utilizador foi removido da equipa com sucesso."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Sair da equipa",
|
||||
"text1": "Tens a certeza de que queres sair desta equipa?",
|
||||
"text2": "Vais perder acesso a todas as listas e espaços a que esta equipa tem acesso. Se mudares de ideias, vais necessitar que um administrador da equipa te adicione novamente.",
|
||||
"success": "Saíste da equipa com sucesso."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Доброй ночи, {username}",
|
||||
"welcomeMorning": "Доброе утро, {username}",
|
||||
"welcomeDay": "Привет, {username}",
|
||||
"welcomeEvening": "Добрый вечер, {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Последние просмотренные",
|
||||
"list": {
|
||||
"newText": "Ты можешь создать новый список для своих задач:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Название списка",
|
||||
"color": "Цвет",
|
||||
"lists": "Списки",
|
||||
"list": {
|
||||
"title": "Список",
|
||||
"add": "Добавить",
|
||||
"addPlaceholder": "Добавить новую задачу…",
|
||||
"empty": "Список сейчас пуст.",
|
||||
"newTaskCta": "Создать новую задачу.",
|
||||
"editTask": "Изменить задачу"
|
||||
},
|
||||
"search": "Введи запрос для поиска списка…",
|
||||
"searchSelect": "Кликни или нажми Enter для выбора этого списка",
|
||||
"shared": "Общие списки",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Удалить"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Список",
|
||||
"add": "Добавить",
|
||||
"addPlaceholder": "Добавить новую задачу…",
|
||||
"empty": "Список сейчас пуст.",
|
||||
"newTaskCta": "Создать новую задачу.",
|
||||
"editTask": "Изменить задачу"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Гант",
|
||||
"showTasksWithoutDates": "Показать задачи без установленной даты",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Дата изменения"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Ты не можешь отписаться здесь, потому что ты подписан на {entity} через {parent}.",
|
||||
"subscribed": "Ты подписан на {entity} и будешь получать уведомления об изменениях.",
|
||||
"notSubscribed": "Ты не подписан на {entity} и не будешь получать уведомления об изменениях.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Подписаться",
|
||||
"unsubscribe": "Отписаться",
|
||||
"subscribeSuccess": "Ты подписался на {entity}",
|
||||
"unsubscribeSuccess": "Ты отписался от {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Вложения",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Удалить это вложение",
|
||||
"deleteText1": "Удалить вложение {filename}?",
|
||||
"copyUrl": "Скопировать URL",
|
||||
"copyUrlTooltip": "Скопировать ссылку на это вложение для использования в тексте"
|
||||
"copyUrlTooltip": "Скопировать ссылку на это вложение для использования в тексте",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Комментарии",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Удалить этого пользователя из команды?",
|
||||
"text2": "Пользователь потеряет доступ ко всем спискам и пространствам имён, к котором есть доступ у команды. Это действие отменить нельзя!",
|
||||
"success": "Пользователь удалён из команды."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Ngủ ngon nhé, {username}",
|
||||
"welcomeMorning": "Chào buổi sáng, {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Chào buổi tối, {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Xem gần đây",
|
||||
"list": {
|
||||
"newText": "Bạn có thể tạo một danh sách công việc mới cho mình:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "Tên Danh sách",
|
||||
"color": "Màu sắc",
|
||||
"lists": "Danh sách",
|
||||
"list": {
|
||||
"title": "Danh sách",
|
||||
"add": "Thêm",
|
||||
"addPlaceholder": "Thêm việc cần làm…",
|
||||
"empty": "Danh sách này đang trống trơn.",
|
||||
"newTaskCta": "Thêm một công việc mới.",
|
||||
"editTask": "Chỉnh sửa Công việc"
|
||||
},
|
||||
"search": "Gõ để tìm kiếm danh sách…",
|
||||
"searchSelect": "Nhấp hoặc nhấn enter để chọn danh sách này",
|
||||
"shared": "Đang tham gia",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Xóa"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "Danh sách",
|
||||
"add": "Thêm",
|
||||
"addPlaceholder": "Thêm việc cần làm…",
|
||||
"empty": "Danh sách này đang trống trơn.",
|
||||
"newTaskCta": "Thêm một công việc mới.",
|
||||
"editTask": "Chỉnh sửa Công việc"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Biểu đồ Gantt",
|
||||
"showTasksWithoutDates": "Hiển thị các nhiệm vụ không cài đặt ngày",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Đã cập nhật"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "Bạn không thể hủy theo dõi ở đây vì bạn đã theo dõi {entity} này thông qua {parent} của nó.",
|
||||
"subscribed": "Bạn đang theo dõi {entity} này và sẽ nhận được thông báo về các thay đổi.",
|
||||
"notSubscribed": "Bạn chưa theo dõi {entity} này và sẽ không nhận được thông báo về các thay đổi.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Theo dõi",
|
||||
"unsubscribe": "Bỏ theo dõi",
|
||||
"subscribeSuccess": "Bạn hiện đã theo dõi {entity} này",
|
||||
"unsubscribeSuccess": "Bạn đã bỏ theo dõi {entity} này"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Tệp đính kèm",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Xóa tệp đính kèm này",
|
||||
"deleteText1": "Bạn có chắc chắn muốn xóa tệp đính kèm {filename} không?",
|
||||
"copyUrl": "Sao chép URL",
|
||||
"copyUrlTooltip": "Sao chép url của tệp đính kèm này để sử dụng trong văn bản"
|
||||
"copyUrlTooltip": "Sao chép url của tệp đính kèm này để sử dụng trong văn bản",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Bình luận",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Bạn có chắc muốn đưa thành viên này ra khỏi Team không?",
|
||||
"text2": "Họ sẽ mất quyền truy cập vào tất cả danh sách và góc làm việc mà Team này có quyền truy cập. Điều đó KHÔNG THỂ HOÀN TÁC!",
|
||||
"success": "Thành viên đã rời khỏi Team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"welcomeNight": "Good Night {username}!",
|
||||
"welcomeMorning": "Good Morning {username}!",
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"list": {
|
||||
"newText": "You can create a new list for your new tasks:",
|
||||
|
@ -169,6 +169,14 @@
|
|||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
|
@ -270,14 +278,6 @@
|
|||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
|
@ -672,13 +672,23 @@
|
|||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribedListThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this list through its namespace.",
|
||||
"subscribedTaskThroughParentNamespace": "You can't unsubscribe here because you are subscribed to this task through its namespace.",
|
||||
"subscribedTaskThroughParentList": "You can't unsubscribe here because you are subscribed to this task through its list.",
|
||||
"subscribedNamespace": "You are currently subscribed to this namespace and will receive notifications for changes.",
|
||||
"notSubscribedNamespace": "You are not subscribed to this namespace and won't receive notifications for changes.",
|
||||
"subscribedList": "You are currently subscribed to this list and will receive notifications for changes.",
|
||||
"notSubscribedList": "You are not subscribed to this list and won't receive notifications for changes.",
|
||||
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
|
||||
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
"subscribeSuccessNamespace": "You are now subscribed to this namespace",
|
||||
"unsubscribeSuccessNamespace": "You are now unsubscribed to this namespace",
|
||||
"subscribeSuccessList": "You are now subscribed to this list",
|
||||
"unsubscribeSuccessList": "You are now unsubscribed to this list",
|
||||
"subscribeSuccessTask": "You are now subscribed to this task",
|
||||
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
|
@ -690,7 +700,11 @@
|
|||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
|
||||
"setAsCover": "Make cover",
|
||||
"unsetAsCover": "Remove cover",
|
||||
"successfullyChangedCoverImage": "The cover image was successfully changed.",
|
||||
"usedAsCover": "Cover image"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
|
@ -839,6 +853,12 @@
|
|||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
},
|
||||
"leave": {
|
||||
"title": "Leave team",
|
||||
"text1": "Are you sure you want to leave this team?",
|
||||
"text2": "You will loose access to all lists and namespaces this team has access to. If you change your mind you'll need a team admin to add you again.",
|
||||
"success": "You have successfully left the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,6 +13,7 @@ import type {IRepeatAfter} from '@/types/IRepeatAfter'
|
|||
import type {IRepeatMode} from '@/types/IRepeatMode'
|
||||
|
||||
import type {PartialWithId} from '@/types/PartialWithId'
|
||||
|
||||
export interface ITask extends IAbstract {
|
||||
id: number
|
||||
title: string
|
||||
|
@ -33,8 +34,9 @@ export interface ITask extends IAbstract {
|
|||
parentTaskId: ITask['id']
|
||||
hexColor: string
|
||||
percentDone: number
|
||||
relatedTasks: Partial<Record<IRelationKind, ITask[]>>,
|
||||
relatedTasks: Partial<Record<IRelationKind, ITask[]>>
|
||||
attachments: IAttachment[]
|
||||
coverImageAttachmentId: IAttachment['id']
|
||||
identifier: string
|
||||
index: number
|
||||
isFavorite: boolean
|
||||
|
|
|
@ -7,7 +7,7 @@ export const AUTH_TYPES = {
|
|||
'LINK_SHARE': 2,
|
||||
} as const
|
||||
|
||||
type AuthType = typeof AUTH_TYPES[keyof typeof AUTH_TYPES]
|
||||
export type AuthType = typeof AUTH_TYPES[keyof typeof AUTH_TYPES]
|
||||
|
||||
export interface IUser extends IAbstract {
|
||||
id: number
|
||||
|
|
|
@ -5,6 +5,8 @@ import type { IUser } from '@/modelTypes/IUser'
|
|||
import type { IFile } from '@/modelTypes/IFile'
|
||||
import type { IAttachment } from '@/modelTypes/IAttachment'
|
||||
|
||||
export const SUPPORTED_IMAGE_SUFFIX = ['.jpg', '.png', '.bmp', '.gif']
|
||||
|
||||
export default class AttachmentModel extends AbstractModel<IAttachment> implements IAttachment {
|
||||
id = 0
|
||||
taskId = 0
|
||||
|
|
|
@ -9,6 +9,7 @@ import {setTitle} from '@/helpers/setTitle'
|
|||
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
||||
import HomeComponent from '../views/Home.vue'
|
||||
import NotFoundComponent from '../views/404.vue'
|
||||
|
@ -465,11 +466,18 @@ const router = createRouter({
|
|||
],
|
||||
})
|
||||
|
||||
export function getAuthForRoute(route: RouteLocation) {
|
||||
export async function getAuthForRoute(route: RouteLocation) {
|
||||
const authStore = useAuthStore()
|
||||
if (authStore.authUser || authStore.authLinkShare) {
|
||||
return
|
||||
}
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
// When trying this before the current user was fully loaded we might get a flash of the login screen
|
||||
// in the user shell. To make shure this does not happen we check if everything is ready before trying.
|
||||
if (!baseStore.ready) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user is already logged in and redirect them to the home page if not
|
||||
if (
|
||||
|
@ -482,11 +490,24 @@ export function getAuthForRoute(route: RouteLocation) {
|
|||
'openid.auth',
|
||||
].includes(route.name as string) &&
|
||||
localStorage.getItem('passwordResetToken') === null &&
|
||||
localStorage.getItem('emailConfirmToken') === null
|
||||
localStorage.getItem('emailConfirmToken') === null &&
|
||||
!(route.name === 'home' && (typeof route.query.userPasswordReset !== 'undefined' || typeof route.query.userEmailConfirm !== 'undefined'))
|
||||
) {
|
||||
saveLastVisited(route.name as string, route.params)
|
||||
saveLastVisited(route.name as string, route.params, route.query)
|
||||
return {name: 'user.login'}
|
||||
}
|
||||
|
||||
if(localStorage.getItem('passwordResetToken') !== null && route.name !== 'user.password-reset.reset') {
|
||||
return {name: 'user.password-reset.reset'}
|
||||
}
|
||||
|
||||
if(localStorage.getItem('emailConfirmToken') !== null && route.name !== 'user.login') {
|
||||
return {name: 'user.login'}
|
||||
}
|
||||
}
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
return getAuthForRoute(to)
|
||||
})
|
||||
|
||||
export default router
|
|
@ -183,11 +183,11 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
|||
////////////////
|
||||
|
||||
/**
|
||||
* The modelFactory returns an model from an object.
|
||||
* The modelFactory returns a model from an object.
|
||||
* This one here is the default one, usually the service definitions for a model will override this.
|
||||
*/
|
||||
modelFactory(data : Partial<Model>) {
|
||||
return new AbstractModel(data)
|
||||
return data as Model
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,7 +22,6 @@ export default class AbstractMigrationFileService extends AbstractService {
|
|||
}
|
||||
|
||||
migrate(file: IFile) {
|
||||
console.log(file)
|
||||
return this.uploadFile(
|
||||
this.paths.create,
|
||||
file,
|
||||
|
|
|
@ -3,7 +3,7 @@ import {defineStore, acceptHMRUpdate} from 'pinia'
|
|||
import {HTTPFactory, AuthenticatedHTTPFactory} from '@/http-common'
|
||||
import {i18n, getCurrentLanguage, saveLanguage} from '@/i18n'
|
||||
import {objectToSnakeCase} from '@/helpers/case'
|
||||
import UserModel, { getAvatarUrl } from '@/models/user'
|
||||
import UserModel, { getAvatarUrl, getDisplayName } from '@/models/user'
|
||||
import UserSettingsService from '@/services/userSettings'
|
||||
import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
|
||||
import {setModuleLoading} from '@/stores/helper'
|
||||
|
@ -55,6 +55,9 @@ export const useAuthStore = defineStore('auth', {
|
|||
state.info.type === AUTH_TYPES.LINK_SHARE
|
||||
)
|
||||
},
|
||||
userDisplayName(state) {
|
||||
return state.info ? getDisplayName(state.info) : undefined
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setIsLoading(isLoading: boolean) {
|
||||
|
@ -115,7 +118,7 @@ export const useAuthStore = defineStore('auth', {
|
|||
saveToken(response.data.token, true)
|
||||
|
||||
// Tell others the user is autheticated
|
||||
this.checkAuth()
|
||||
await this.checkAuth()
|
||||
} catch (e) {
|
||||
if (
|
||||
e.response &&
|
||||
|
@ -168,7 +171,7 @@ export const useAuthStore = defineStore('auth', {
|
|||
saveToken(response.data.token, true)
|
||||
|
||||
// Tell others the user is autheticated
|
||||
this.checkAuth()
|
||||
await this.checkAuth()
|
||||
} finally {
|
||||
this.setIsLoading(false)
|
||||
}
|
||||
|
@ -180,14 +183,14 @@ export const useAuthStore = defineStore('auth', {
|
|||
password: password,
|
||||
})
|
||||
saveToken(response.data.token, false)
|
||||
this.checkAuth()
|
||||
await this.checkAuth()
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates user information from jwt token saved in local storage in store
|
||||
*/
|
||||
checkAuth() {
|
||||
async checkAuth() {
|
||||
const now = new Date()
|
||||
const inOneMinute = new Date(new Date().setMinutes(now.getMinutes() + 1))
|
||||
// This function can be called from multiple places at the same time and shortly after one another.
|
||||
|
@ -212,7 +215,7 @@ export const useAuthStore = defineStore('auth', {
|
|||
this.setUser(info)
|
||||
|
||||
if (authenticated) {
|
||||
this.refreshUserInfo()
|
||||
await this.refreshUserInfo()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,6 +224,8 @@ export const useAuthStore = defineStore('auth', {
|
|||
this.setUser(null)
|
||||
this.redirectToProviderIfNothingElseIsEnabled()
|
||||
}
|
||||
|
||||
return Promise.resolve(authenticated)
|
||||
},
|
||||
|
||||
redirectToProviderIfNothingElseIsEnabled() {
|
||||
|
@ -290,11 +295,11 @@ export const useAuthStore = defineStore('auth', {
|
|||
const stopLoading = setModuleLoading(this)
|
||||
try {
|
||||
await HTTPFactory().post('user/confirm', {token: emailVerifyToken})
|
||||
localStorage.removeItem('emailConfirmToken')
|
||||
return true
|
||||
} catch(e) {
|
||||
throw new Error(e.response.data.message)
|
||||
} finally {
|
||||
localStorage.removeItem('emailConfirmToken')
|
||||
stopLoading()
|
||||
}
|
||||
}
|
||||
|
@ -339,22 +344,22 @@ export const useAuthStore = defineStore('auth', {
|
|||
|
||||
try {
|
||||
await refreshToken(!this.isLinkShareAuth)
|
||||
this.checkAuth()
|
||||
await this.checkAuth()
|
||||
} catch (e) {
|
||||
// Don't logout on network errors as the user would then get logged out if they don't have
|
||||
// internet for a short period of time - such as when the laptop is still reconnecting
|
||||
if (e?.request?.status) {
|
||||
this.logout()
|
||||
await this.logout()
|
||||
}
|
||||
}
|
||||
}, 5000)
|
||||
},
|
||||
|
||||
logout() {
|
||||
async logout() {
|
||||
removeToken()
|
||||
window.localStorage.clear() // Clear all settings and history we might have saved in local storage.
|
||||
router.push({name: 'user.login'})
|
||||
this.checkAuth()
|
||||
await router.push({name: 'user.login'})
|
||||
await this.checkAuth()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -11,6 +11,7 @@ import type {IList} from '@/modelTypes/IList'
|
|||
|
||||
export interface RootStoreState {
|
||||
loading: boolean,
|
||||
ready: boolean,
|
||||
|
||||
currentList: IList | null,
|
||||
background: string,
|
||||
|
@ -26,6 +27,7 @@ export interface RootStoreState {
|
|||
export const useBaseStore = defineStore('base', {
|
||||
state: () : RootStoreState => ({
|
||||
loading: false,
|
||||
ready: false,
|
||||
|
||||
// This is used to highlight the current list in menu for all list related views
|
||||
currentList: new ListModel({
|
||||
|
@ -95,6 +97,10 @@ export const useBaseStore = defineStore('base', {
|
|||
setLogoVisible(visible: boolean) {
|
||||
this.logoVisible = visible
|
||||
},
|
||||
|
||||
setReady(ready: boolean) {
|
||||
this.ready = ready
|
||||
},
|
||||
|
||||
async handleSetCurrentList({list, forceUpdate = false} : {list: IList | null, forceUpdate: boolean}) {
|
||||
if (list === null) {
|
||||
|
@ -133,7 +139,8 @@ export const useBaseStore = defineStore('base', {
|
|||
|
||||
async loadApp() {
|
||||
await checkAndSetApiUrl(window.API_URL)
|
||||
useAuthStore().checkAuth()
|
||||
await useAuthStore().checkAuth()
|
||||
this.ready = true
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -75,12 +75,11 @@ export const useKanbanStore = defineStore('kanban', {
|
|||
getTaskById(state) {
|
||||
return (id: ITask['id']) => {
|
||||
const { bucketIndex, taskIndex } = getTaskIndicesById(state, id)
|
||||
|
||||
|
||||
return {
|
||||
bucketIndex,
|
||||
taskIndex,
|
||||
task: bucketIndex && taskIndex && state.buckets[bucketIndex]?.tasks?.[taskIndex] || null,
|
||||
task: bucketIndex !== null && taskIndex !== null && state.buckets[bucketIndex]?.tasks?.[taskIndex] || null,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -43,7 +43,7 @@ export const useNamespaceStore = defineStore('namespace', {
|
|||
getNamespaceById: state => (namespaceId: INamespace['id']) => {
|
||||
return state.namespaces.find(({id}) => id == namespaceId) || null
|
||||
},
|
||||
|
||||
|
||||
searchNamespace() {
|
||||
return (query: string) => (
|
||||
search(query)
|
||||
|
@ -64,6 +64,13 @@ export const useNamespaceStore = defineStore('namespace', {
|
|||
this.namespaces = namespaces
|
||||
namespaces.forEach(n => {
|
||||
add(n)
|
||||
|
||||
// Check for each list in that namespace if it has a subscription and set it if not
|
||||
n.lists.forEach(l => {
|
||||
if (l.subscription === null || l.subscription.entity !== 'list') {
|
||||
l.subscription = n.subscription
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -203,5 +210,5 @@ export const useNamespaceStore = defineStore('namespace', {
|
|||
|
||||
// support hot reloading
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useNamespaceStore, import.meta.hot))
|
||||
import.meta.hot.accept(acceptHMRUpdate(useNamespaceStore, import.meta.hot))
|
||||
}
|
|
@ -28,6 +28,7 @@ import {useLabelStore} from '@/stores/labels'
|
|||
import {useListStore} from '@/stores/lists'
|
||||
import {useAttachmentStore} from '@/stores/attachments'
|
||||
import {useKanbanStore} from '@/stores/kanban'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
||||
// IDEA: maybe use a small fuzzy search here to prevent errors
|
||||
function findPropertyByValue(object, key, value) {
|
||||
|
@ -105,6 +106,7 @@ export const useTaskStore = defineStore('task', {
|
|||
const cancel = setModuleLoading(this)
|
||||
try {
|
||||
this.tasks = await taskService.getAll({}, params)
|
||||
useBaseStore().setHasTasks(this.tasks.length > 0)
|
||||
return this.tasks
|
||||
} finally {
|
||||
cancel()
|
||||
|
@ -171,32 +173,39 @@ export const useTaskStore = defineStore('task', {
|
|||
user: IUser,
|
||||
taskId: ITask['id']
|
||||
}) {
|
||||
const kanbanStore = useKanbanStore()
|
||||
const taskAssigneeService = new TaskAssigneeService()
|
||||
const r = await taskAssigneeService.create(new TaskAssigneeModel({
|
||||
userId: user.id,
|
||||
taskId: taskId,
|
||||
}))
|
||||
const t = kanbanStore.getTaskById(taskId)
|
||||
if (t.task === null) {
|
||||
// Don't try further adding a label if the task is not in kanban
|
||||
// Usually this means the kanban board hasn't been accessed until now.
|
||||
// Vuex seems to have its difficulties with that, so we just log the error and fail silently.
|
||||
console.debug('Could not add assignee to task in kanban, task not found', t)
|
||||
return r
|
||||
}
|
||||
const cancel = setModuleLoading(this)
|
||||
|
||||
try {
|
||||
const kanbanStore = useKanbanStore()
|
||||
const taskAssigneeService = new TaskAssigneeService()
|
||||
const r = await taskAssigneeService.create(new TaskAssigneeModel({
|
||||
userId: user.id,
|
||||
taskId: taskId,
|
||||
}))
|
||||
const t = kanbanStore.getTaskById(taskId)
|
||||
if (t.task === null) {
|
||||
// Don't try further adding a label if the task is not in kanban
|
||||
// Usually this means the kanban board hasn't been accessed until now.
|
||||
// Vuex seems to have its difficulties with that, so we just log the error and fail silently.
|
||||
console.debug('Could not add assignee to task in kanban, task not found', t)
|
||||
return r
|
||||
}
|
||||
|
||||
kanbanStore.setTaskInBucketByIndex({
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
assignees: [
|
||||
...t.task.assignees,
|
||||
user,
|
||||
],
|
||||
},
|
||||
})
|
||||
return r
|
||||
kanbanStore.setTaskInBucketByIndex({
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
assignees: [
|
||||
...t.task.assignees,
|
||||
user,
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
return r
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async removeAssignee({
|
||||
|
@ -252,7 +261,7 @@ export const useTaskStore = defineStore('task', {
|
|||
// Don't try further adding a label if the task is not in kanban
|
||||
// Usually this means the kanban board hasn't been accessed until now.
|
||||
// Vuex seems to have its difficulties with that, so we just log the error and fail silently.
|
||||
console.debug('Could not add label to task in kanban, task not found', t)
|
||||
console.debug('Could not add label to task in kanban, task not found', {taskId, t})
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -378,6 +387,7 @@ export const useTaskStore = defineStore('task', {
|
|||
})
|
||||
|
||||
if(foundListId === null || foundListId === 0) {
|
||||
cancel()
|
||||
throw new Error('NO_LIST')
|
||||
}
|
||||
|
||||
|
@ -409,6 +419,13 @@ export const useTaskStore = defineStore('task', {
|
|||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async setCoverImage(task: ITask, attachment: IAttachment | null) {
|
||||
return this.update({
|
||||
...task,
|
||||
coverImageAttachmentId: attachment ? attachment.id : 0,
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
declare module 'vue-flatpickr-component' {
|
||||
import type {DefineComponent} from 'vue'
|
||||
export default DefineComponent<>
|
||||
}
|
|
@ -34,9 +34,10 @@
|
|||
<script setup lang="ts">
|
||||
import {computed} from 'vue'
|
||||
|
||||
import {VERSION as frontendVersion} from '@/version.json'
|
||||
import {VERSION} from '@/version.json'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
const configStore = useConfigStore()
|
||||
const apiVersion = computed(() => configStore.version)
|
||||
const frontendVersion = VERSION
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<div class="content has-text-centered">
|
||||
<h2 v-if="userInfo">
|
||||
{{ $t(welcome, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
||||
</h2>
|
||||
<h2 v-if="salutation">{{ salutation }}</h2>
|
||||
|
||||
<message variant="danger" v-if="deletionScheduledAt !== null" class="mb-4">
|
||||
{{
|
||||
$t('user.deletion.scheduled', {
|
||||
|
@ -69,7 +68,7 @@ import AddTask from '@/components/tasks/add-task.vue'
|
|||
import {getHistory} from '@/modules/listHistory'
|
||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||
import {formatDateShort, formatDateSince} from '@/helpers/time/formatDate'
|
||||
import {useDateTimeSalutation} from '@/composables/useDateTimeSalutation'
|
||||
import {useDaytimeSalutation} from '@/composables/useDaytimeSalutation'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
|
@ -78,7 +77,7 @@ import {useNamespaceStore} from '@/stores/namespaces'
|
|||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const welcome = useDateTimeSalutation()
|
||||
const salutation = useDaytimeSalutation()
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const authStore = useAuthStore()
|
||||
|
@ -99,7 +98,6 @@ const listHistory = computed(() => {
|
|||
})
|
||||
|
||||
const migratorsEnabled = computed(() => configStore.availableMigrators?.length > 0)
|
||||
const userInfo = computed(() => authStore.info)
|
||||
const hasTasks = computed(() => baseStore.hasTasks)
|
||||
const defaultListId = computed(() => authStore.settings.defaultListId)
|
||||
const defaultNamespaceId = computed(() => namespaceStore.namespaces?.[0]?.id || 0)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<ListWrapper class="list-kanban" :list-id="listId" viewName="kanban">
|
||||
<template #header>
|
||||
<div class="filter-container" v-if="isSavedFilter(list)">
|
||||
<div class="filter-container" v-if="!isSavedFilter(listId)">
|
||||
<div class="items">
|
||||
<filter-popup
|
||||
v-model="params"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<create-edit
|
||||
:title="$t('list.share.header')"
|
||||
primary-label=""
|
||||
:has-primary-action="false"
|
||||
>
|
||||
<template v-if="list">
|
||||
<userTeam
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg viewBox="0 0 88 88" xmlns="http://www.w3.org/2000/svg" class="logo_1OKcB"><g fill="none" fill-rule="evenodd"><rect></rect><path d="M30.755 33.292l-7.34 8.935L40.798 56.48a5.782 5.782 0 008.182-.854l31.179-38.93-9.026-7.228L43.614 43.83l-12.86-10.538z" fill="#FFB000"></path><path d="M44 78.1C25.197 78.1 9.9 62.803 9.9 44S25.197 9.9 44 9.9V0C19.738 0 0 19.738 0 44s19.738 44 44 44 44-19.738 44-44h-9.9c0 18.803-15.297 34.1-34.1 34.1" fill="#4772FA"></path></g></svg>
|
After Width: | Height: | Size: 471 B |
|
@ -3,6 +3,7 @@ import todoistIcon from './icons/todoist.svg?url'
|
|||
import trelloIcon from './icons/trello.svg?url'
|
||||
import microsoftTodoIcon from './icons/microsoft-todo.svg?url'
|
||||
import vikunjaFileIcon from './icons/vikunja-file.png?url'
|
||||
import tickTickIcon from './icons/ticktick.svg?url'
|
||||
|
||||
export interface Migrator {
|
||||
id: string
|
||||
|
@ -42,4 +43,10 @@ export const MIGRATORS: IMigratorRecord = {
|
|||
icon: vikunjaFileIcon,
|
||||
isFileMigrator: true,
|
||||
},
|
||||
ticktick: {
|
||||
id: 'ticktick',
|
||||
name: 'TickTick',
|
||||
icon: tickTickIcon as string,
|
||||
isFileMigrator: true,
|
||||
},
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<create-edit
|
||||
:title="title"
|
||||
primary-label=""
|
||||
:has-primary-action="false"
|
||||
>
|
||||
<template v-if="namespace">
|
||||
<manageSharing
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
:disabled="!canWrite"
|
||||
:list-id="task.listId"
|
||||
:task-id="task.id"
|
||||
ref="assignees"
|
||||
:ref="e => setFieldRef('assignees', e)"
|
||||
v-model="task.assignees"
|
||||
/>
|
||||
</div>
|
||||
|
@ -39,8 +39,8 @@
|
|||
</div>
|
||||
<priority-select
|
||||
:disabled="!canWrite"
|
||||
@update:model-value="saveTask"
|
||||
ref="priority"
|
||||
@update:model-value="setPriority"
|
||||
:ref="e => setFieldRef('priority', e)"
|
||||
v-model="task.priority"/>
|
||||
</div>
|
||||
</transition>
|
||||
|
@ -57,7 +57,7 @@
|
|||
@close-on-change="() => saveTask()"
|
||||
:choose-date-label="$t('task.detail.chooseDueDate')"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
ref="dueDate"
|
||||
:ref="e => setFieldRef('dueDate', e)"
|
||||
/>
|
||||
<BaseButton
|
||||
@click="() => {task.dueDate = null;saveTask()}"
|
||||
|
@ -79,8 +79,8 @@
|
|||
</div>
|
||||
<percent-done-select
|
||||
:disabled="!canWrite"
|
||||
@update:model-value="saveTask"
|
||||
ref="percentDone"
|
||||
@update:model-value="setPercentDone"
|
||||
:ref="e => setFieldRef('percentDone', e)"
|
||||
v-model="task.percentDone"/>
|
||||
</div>
|
||||
</transition>
|
||||
|
@ -97,7 +97,7 @@
|
|||
@close-on-change="() => saveTask()"
|
||||
:choose-date-label="$t('task.detail.chooseStartDate')"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
ref="startDate"
|
||||
:ref="e => setFieldRef('startDate', e)"
|
||||
/>
|
||||
<BaseButton
|
||||
@click="() => {task.startDate = null;saveTask()}"
|
||||
|
@ -124,7 +124,7 @@
|
|||
@close-on-change="() => saveTask()"
|
||||
:choose-date-label="$t('task.detail.chooseEndDate')"
|
||||
:disabled="taskService.loading || !canWrite"
|
||||
ref="endDate"
|
||||
:ref="e => setFieldRef('endDate', e)"
|
||||
/>
|
||||
<BaseButton
|
||||
@click="() => {task.endDate = null;saveTask()}"
|
||||
|
@ -146,7 +146,7 @@
|
|||
</div>
|
||||
<reminders
|
||||
:disabled="!canWrite"
|
||||
ref="reminders"
|
||||
:ref="e => setFieldRef('reminders', e)"
|
||||
v-model="task.reminderDates"
|
||||
@update:model-value="saveTask"
|
||||
/>
|
||||
|
@ -171,7 +171,7 @@
|
|||
</div>
|
||||
<repeat-after
|
||||
:disabled="!canWrite"
|
||||
ref="repeatAfter"
|
||||
:ref="e => setFieldRef('repeatAfter', e)"
|
||||
v-model="task"
|
||||
@update:model-value="saveTask"
|
||||
/>
|
||||
|
@ -186,7 +186,7 @@
|
|||
</div>
|
||||
<color-picker
|
||||
menu-position="bottom"
|
||||
ref="color"
|
||||
:ref="e => setFieldRef('color', e)"
|
||||
v-model="taskColor"
|
||||
@update:model-value="saveTask"
|
||||
/>
|
||||
|
@ -202,7 +202,11 @@
|
|||
</span>
|
||||
{{ $t('task.attributes.labels') }}
|
||||
</div>
|
||||
<edit-labels :disabled="!canWrite" :task-id="taskId" ref="labels" v-model="task.labels"/>
|
||||
<edit-labels
|
||||
:disabled="!canWrite"
|
||||
:task-id="taskId"
|
||||
:ref="e => setFieldRef('labels', e)"
|
||||
v-model="task.labels"/>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
|
@ -218,8 +222,9 @@
|
|||
<div class="content attachments" v-if="activeFields.attachments || hasAttachments">
|
||||
<attachments
|
||||
:edit-enabled="canWrite"
|
||||
:task-id="taskId"
|
||||
ref="attachments"
|
||||
:task="task"
|
||||
@task-changed="({coverImageAttachmentId}) => task.coverImageAttachmentId = coverImageAttachmentId"
|
||||
:ref="e => setFieldRef('attachments', e)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -237,7 +242,7 @@
|
|||
:list-id="task.listId"
|
||||
:show-no-relations-notice="true"
|
||||
:task-id="taskId"
|
||||
ref="relatedTasks"
|
||||
:ref="e => setFieldRef('relatedTasks', e)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -251,7 +256,10 @@
|
|||
</h3>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<list-search @update:modelValue="changeList" ref="moveList"/>
|
||||
<list-search
|
||||
@update:modelValue="changeList"
|
||||
:ref="e => setFieldRef('moveList', e)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -442,7 +450,7 @@ import TaskModel, {TASK_DEFAULT_COLOR} from '@/models/task'
|
|||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
|
||||
import {PRIORITIES} from '@/constants/priorities'
|
||||
import {PRIORITIES, type Priority} from '@/constants/priorities'
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
@ -500,7 +508,7 @@ const attachmentStore = useAttachmentStore()
|
|||
const taskStore = useTaskStore()
|
||||
const kanbanStore = useKanbanStore()
|
||||
|
||||
const task = reactive(new TaskModel())
|
||||
const task = reactive<ITask>(new TaskModel())
|
||||
useTitle(toRef(task, 'title'))
|
||||
|
||||
// We doubled the task color property here because verte does not have a real change property, leading
|
||||
|
@ -658,10 +666,14 @@ const activeFieldElements : {[id in FieldType]: HTMLElement | null} = reactive({
|
|||
startDate: null,
|
||||
})
|
||||
|
||||
function setFieldRef(name, e) {
|
||||
activeFieldElements[name] = unrefElement(e)
|
||||
}
|
||||
|
||||
function setFieldActive(fieldName: keyof typeof activeFields) {
|
||||
activeFields[fieldName] = true
|
||||
nextTick(() => {
|
||||
const el = unrefElement(activeFieldElements[fieldName])
|
||||
const el = activeFieldElements[fieldName]
|
||||
|
||||
if (!el) {
|
||||
return
|
||||
|
@ -675,21 +687,19 @@ function setFieldActive(fieldName: keyof typeof activeFields) {
|
|||
}
|
||||
|
||||
async function saveTask(args?: {
|
||||
task: ITask,
|
||||
showNotification?: boolean,
|
||||
undoCallback?: () => void,
|
||||
}) {
|
||||
const {
|
||||
task: currentTask,
|
||||
showNotification,
|
||||
undoCallback,
|
||||
} = {
|
||||
...{
|
||||
task: cloneDeep(task),
|
||||
showNotification: true,
|
||||
},
|
||||
...args,
|
||||
}
|
||||
task: ITask,
|
||||
undoCallback?: () => void,
|
||||
}) {
|
||||
const {
|
||||
task: currentTask,
|
||||
undoCallback,
|
||||
} = {
|
||||
...{
|
||||
task: cloneDeep(task),
|
||||
},
|
||||
...args,
|
||||
}
|
||||
|
||||
if (!canWrite.value) {
|
||||
return
|
||||
}
|
||||
|
@ -710,10 +720,6 @@ async function saveTask(args?: {
|
|||
Object.assign(task, newTask)
|
||||
setActiveFields()
|
||||
|
||||
if (!showNotification) {
|
||||
return
|
||||
}
|
||||
|
||||
let actions = []
|
||||
if (undoCallback !== null) {
|
||||
actions = [{
|
||||
|
@ -759,14 +765,34 @@ async function toggleFavorite() {
|
|||
Object.assign(task, newTask)
|
||||
await namespaceStore.loadNamespacesIfFavoritesDontExist()
|
||||
}
|
||||
|
||||
async function setPriority(priority: Priority) {
|
||||
const newTask: ITask = {
|
||||
...task,
|
||||
priority,
|
||||
}
|
||||
|
||||
return saveTask({
|
||||
task: newTask,
|
||||
})
|
||||
}
|
||||
|
||||
async function setPercentDone(percentDone: number) {
|
||||
const newTask: ITask = {
|
||||
...task,
|
||||
percentDone,
|
||||
}
|
||||
|
||||
return saveTask({
|
||||
task: newTask,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$flash-background-duration: 750ms;
|
||||
|
||||
.task-view {
|
||||
// This is a workaround to hide the llama background from the top on the task detail page
|
||||
margin-top: -1.5rem;
|
||||
padding: 1rem;
|
||||
background-color: var(--site-background);
|
||||
|
||||
|
|
|
@ -127,6 +127,24 @@
|
|||
</table>
|
||||
</card>
|
||||
|
||||
<x-button class="is-fullwidth is-danger" @click="showLeaveModal = true">
|
||||
{{ $t('team.edit.leave.title') }}
|
||||
</x-button>
|
||||
|
||||
<!-- Leave team modal -->
|
||||
<modal
|
||||
v-if="showLeaveModal"
|
||||
@close="showLeaveModal = false"
|
||||
@submit="leave()"
|
||||
>
|
||||
<template #header><span>{{ $t('team.edit.leave.title') }}</span></template>
|
||||
|
||||
<template #text>
|
||||
<p>{{ $t('team.edit.leave.text1') }}<br/>
|
||||
{{ $t('team.edit.leave.text2') }}</p>
|
||||
</template>
|
||||
</modal>
|
||||
|
||||
<!-- Team delete modal -->
|
||||
<transition name="modal">
|
||||
<modal
|
||||
|
@ -202,13 +220,14 @@ const teamMemberService = ref<TeamMemberService>(new TeamMemberService())
|
|||
const userService = ref<UserService>(new UserService())
|
||||
|
||||
const team = ref<ITeam>()
|
||||
const teamId = computed(() => route.params.id)
|
||||
const teamId = computed(() => Number(route.params.id))
|
||||
const memberToDelete = ref<ITeamMember>()
|
||||
const newMember = ref<IUser>()
|
||||
const foundUsers = ref<IUser[]>()
|
||||
|
||||
const showDeleteModal = ref(false)
|
||||
const showUserDeleteModal = ref(false)
|
||||
const showLeaveModal = ref(false)
|
||||
const showError = ref(false)
|
||||
|
||||
const title = ref('')
|
||||
|
@ -287,6 +306,19 @@ async function findUser(query: string) {
|
|||
const users = await userService.value.getAll({}, {s: query})
|
||||
foundUsers.value = users.filter((u: IUser) => u.id !== userInfo.value.id)
|
||||
}
|
||||
|
||||
async function leave() {
|
||||
try {
|
||||
await teamMemberService.value.delete({
|
||||
teamId: teamId.value,
|
||||
username: userInfo.value.username,
|
||||
})
|
||||
success({message: t('team.edit.leave.success')})
|
||||
await router.push({name: 'home'})
|
||||
} finally {
|
||||
showUserDeleteModal.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -104,7 +104,6 @@
|
|||
<script setup lang="ts">
|
||||
import {computed, onBeforeMount, ref} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useRouter} from 'vue-router'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
|
||||
import Message from '@/components/misc/message.vue'
|
||||
|
@ -112,19 +111,19 @@ import Password from '@/components/input/password.vue'
|
|||
|
||||
import {getErrorText} from '@/message'
|
||||
import {redirectToProvider} from '@/helpers/redirectToProvider'
|
||||
import {getLastVisited, clearLastVisited} from '@/helpers/saveLastVisited'
|
||||
import {useRedirectToLastVisited} from '@/composables/useRedirectToLastVisited'
|
||||
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
const router = useRouter()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
useTitle(() => t('user.auth.login'))
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const configStore = useConfigStore()
|
||||
const {redirectIfSaved} = useRedirectToLastVisited()
|
||||
|
||||
const registrationEnabled = computed(() => configStore.registrationEnabled)
|
||||
const localAuthEnabled = computed(() => configStore.auth.local.enabled)
|
||||
|
@ -151,16 +150,7 @@ onBeforeMount(() => {
|
|||
|
||||
// Check if the user is already logged in, if so, redirect them to the homepage
|
||||
if (authenticated.value) {
|
||||
const last = getLastVisited()
|
||||
if (last !== null) {
|
||||
router.push({
|
||||
name: last.name,
|
||||
params: last.params,
|
||||
})
|
||||
clearLastVisited()
|
||||
} else {
|
||||
router.push({name: 'home'})
|
||||
}
|
||||
redirectIfSaved()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
<message variant="danger" v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</message>
|
||||
<message variant="danger" v-if="errorMessageFromQuery" class="mt-2">
|
||||
{{ errorMessageFromQuery }}
|
||||
</message>
|
||||
<message v-if="loading">
|
||||
{{ $t('user.auth.authenticating') }}
|
||||
</message>
|
||||
|
@ -15,24 +18,25 @@ export default { name: 'Auth' }
|
|||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, onMounted} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {useRoute} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {getErrorText} from '@/message'
|
||||
import Message from '@/components/misc/message.vue'
|
||||
import {clearLastVisited, getLastVisited} from '@/helpers/saveLastVisited'
|
||||
import {useRedirectToLastVisited} from '@/composables/useRedirectToLastVisited'
|
||||
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const {redirectIfSaved} = useRedirectToLastVisited()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const loading = computed(() => authStore.isLoading)
|
||||
const errorMessage = ref('')
|
||||
const errorMessageFromQuery = computed(() => route.query.error)
|
||||
|
||||
async function authenticateWithCode() {
|
||||
// This component gets mounted twice: The first time when the actual auth request hits the frontend,
|
||||
|
@ -70,16 +74,7 @@ async function authenticateWithCode() {
|
|||
provider: route.params.provider,
|
||||
code: route.query.code,
|
||||
})
|
||||
const last = getLastVisited()
|
||||
if (last !== null) {
|
||||
router.push({
|
||||
name: last.name,
|
||||
params: last.params,
|
||||
})
|
||||
clearLastVisited()
|
||||
} else {
|
||||
router.push({name: 'home'})
|
||||
}
|
||||
redirectIfSaved()
|
||||
} catch(e) {
|
||||
const err = getErrorText(e)
|
||||
errorMessage.value = typeof err[1] !== 'undefined' ? err[1] : err[0]
|
||||
|
|
|
@ -7,41 +7,14 @@
|
|||
<message variant="success">
|
||||
{{ successMessage }}
|
||||
</message>
|
||||
<x-button :to="{ name: 'user.login' }">
|
||||
<x-button :to="{ name: 'user.login' }" class="mt-4">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<form @submit.prevent="submit" id="form" v-if="!successMessage">
|
||||
<div class="field">
|
||||
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password1"
|
||||
name="password1"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
required
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
v-focus
|
||||
v-model="credentials.password"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
id="password2"
|
||||
name="password2"
|
||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
required
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
v-model="credentials.password2"
|
||||
@keyup.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
|
||||
<Password @submit="submit" @update:modelValue="v => credentials.password = v"/>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
|
@ -60,17 +33,14 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import PasswordResetModel from '@/models/passwordReset'
|
||||
import PasswordResetService from '@/services/passwordReset'
|
||||
import Message from '@/components/misc/message.vue'
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
import Password from '@/components/input/password.vue'
|
||||
|
||||
const credentials = reactive({
|
||||
password: '',
|
||||
password2: '',
|
||||
})
|
||||
|
||||
const passwordResetService = reactive(new PasswordResetService())
|
||||
|
@ -79,15 +49,14 @@ const successMessage = ref('')
|
|||
|
||||
async function submit() {
|
||||
errorMsg.value = ''
|
||||
|
||||
if (credentials.password2 !== credentials.password) {
|
||||
errorMsg.value = t('user.auth.passwordsDontMatch')
|
||||
|
||||
if(credentials.password === '') {
|
||||
return
|
||||
}
|
||||
|
||||
const passwordReset = new PasswordResetModel({newPassword: credentials.password})
|
||||
try {
|
||||
const {message} = passwordResetService.resetPassword(passwordReset)
|
||||
const {message} = await passwordResetService.resetPassword(passwordReset)
|
||||
successMessage.value = message
|
||||
localStorage.removeItem('passwordResetToken')
|
||||
} catch (e) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<message variant="success">
|
||||
{{ $t('user.auth.resetPasswordSuccess') }}
|
||||
</message>
|
||||
<x-button :to="{ name: 'user.login' }">
|
||||
<x-button :to="{ name: 'user.login' }" class="mt-4">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,8 @@ import legacyFn from '@vitejs/plugin-legacy'
|
|||
import {VitePWA} from 'vite-plugin-pwa'
|
||||
import {visualizer} from 'rollup-plugin-visualizer'
|
||||
import svgLoader from 'vite-svg-loader'
|
||||
import postcssPresetEnv from "postcss-preset-env";
|
||||
|
||||
import { fileURLToPath, URL } from 'url'
|
||||
|
||||
const pathSrc = fileURLToPath(new URL('./src', import.meta.url))
|
||||
|
@ -40,6 +42,11 @@ export default defineConfig({
|
|||
charset: false, // fixes "@charset" must be the first rule in the file" warnings
|
||||
},
|
||||
},
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssPresetEnv(),
|
||||
],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
|
|
Reference in New Issue