Compare commits
131 Commits
5b502d3353
...
053f0a4c7a
Author | SHA1 | Date |
---|---|---|
Elscrux | 053f0a4c7a | |
Elscrux | b81f302914 | |
Elscrux | 629afbed21 | |
Frederick [Bot] | 2239d73797 | |
Frederick [Bot] | 98b833f61a | |
Frederick [Bot] | ecd002dca3 | |
renovate | 5e3616bda3 | |
renovate | 3e5ff77b91 | |
kolaente | 403db6adbf | |
kolaente | fd4312382e | |
kolaente | 2dcf6c6861 | |
kolaente | 8f85af07ca | |
kolaente | 9f89fbe5a6 | |
kolaente | 6ad83c0685 | |
kolaente | 97b7592e7c | |
kolaente | 19c9cd9bc2 | |
Frederick [Bot] | e53fcd3367 | |
kolaente | d635fd2dd3 | |
renovate | b8584301a3 | |
renovate | 2bb4c31f20 | |
Frederick [Bot] | d22ebef0b3 | |
Frederick [Bot] | 68d8ed5a7a | |
konrad | 7230db1603 | |
kolaente | 32e8a15f1f | |
renovate | f80ffcd541 | |
kolaente | 89ed71777e | |
kolaente | 5b01710943 | |
kolaente | d7b40f393e | |
kolaente | fee75e55a3 | |
kolaente | fa137b1ffc | |
kolaente | e7d6ee2392 | |
kolaente | 62ff05695f | |
kolaente | 6f51b56589 | |
kolaente | 5e9edef3b3 | |
kolaente | f18fcc5569 | |
kolaente | bc8b5da61d | |
kolaente | b3a96ea251 | |
kolaente | b0ad087a36 | |
kolaente | b65d05ec3d | |
kolaente | 974c9cdd21 | |
kolaente | 8206cc8767 | |
kolaente | 53e57d524a | |
kolaente | 165d291cd5 | |
kolaente | e940db6d32 | |
kolaente | 7c30b00668 | |
renovate | 0ee8150e24 | |
kolaente | cf9b2fa203 | |
renovate | a9622fe03a | |
renovate | c67f0ae3cc | |
renovate | 8e7828f71d | |
renovate | 5e60edd9ae | |
kolaente | 511c9aa824 | |
kolaente | 4b903c4f48 | |
kolaente | 30b41bd143 | |
kolaente | f3cdd7d15f | |
kolaente | 8b90eb4a15 | |
kolaente | 803f58f402 | |
kolaente | b7b3169169 | |
kolaente | 409f9a0cc6 | |
kolaente | 9075a45cb8 | |
kolaente | d4bdd2d4e8 | |
kolaente | 9cc273d9bd | |
kolaente | 0f60a92873 | |
kolaente | bec9e3eb7d | |
kolaente | 3f8c5a5feb | |
kolaente | 24fa3b206f | |
kolaente | 434b1ea0e8 | |
kolaente | 433584813a | |
kolaente | 3ec3bb76af | |
kolaente | 6e53bf4ebe | |
kolaente | b8ff7910b0 | |
kolaente | f6485be9e2 | |
kolaente | 004f1e06bb | |
kolaente | 27cb6e3372 | |
kolaente | 445f1c06fa | |
kolaente | 4c1a53beed | |
kolaente | 7368a51f18 | |
kolaente | e1774cc49a | |
kolaente | 61e27ae3eb | |
kolaente | 7f1788eba9 | |
kolaente | 39c9928421 | |
kolaente | 59ced554cd | |
kolaente | bc34a33922 | |
kolaente | 2dfb3a6379 | |
kolaente | 337d289a39 | |
kolaente | 5451ddf58d | |
kolaente | a3714c74fd | |
kolaente | 4170f5468f | |
kolaente | f364f3bec8 | |
kolaente | 786e67f692 | |
kolaente | c36fdb9f5d | |
kolaente | dee78be579 | |
kolaente | 398c9f1056 | |
kolaente | ca0550acea | |
kolaente | cb111df2b7 | |
kolaente | df415f97a9 | |
kolaente | 86039b1dd2 | |
kolaente | 73e5483e87 | |
kolaente | 6913334b17 | |
kolaente | 7866543198 | |
kolaente | cf15cc6f12 | |
kolaente | ee6ea03506 | |
kolaente | 43f24661d7 | |
kolaente | 5641da27f7 | |
kolaente | 14353b24d7 | |
kolaente | ca4e3e01c5 | |
kolaente | 8ce476491e | |
kolaente | f2a0d69670 | |
kolaente | a13276e28e | |
kolaente | 9cf84646a1 | |
kolaente | 006f932dc4 | |
kolaente | 0a3f45ab11 | |
kolaente | d1d07f462c | |
kolaente | 2502776460 | |
kolaente | 238baf86f7 | |
kolaente | 652bf4b4ed | |
kolaente | a9020e976d | |
kolaente | 38457aaca5 | |
kolaente | 98b7cc9254 | |
kolaente | 4149ebed3a | |
kolaente | 2096fc5274 | |
kolaente | e4b1a5d2db | |
kolaente | 2fa3e2c2f5 | |
kolaente | ee228106fc | |
kolaente | b39c5580c2 | |
kolaente | 6bdb33fb46 | |
renovate | 091e03a39d | |
renovate | 55c9403dda | |
renovate | 3f33f903b5 | |
renovate | 650c6cb339 | |
kolaente | 2fff9f1c59 |
|
@ -5,6 +5,7 @@ WORKDIR /build
|
|||
|
||||
ENV PNPM_CACHE_FOLDER .cache/pnpm/
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD true
|
||||
ENV CYPRESS_INSTALL_BINARY 0
|
||||
|
||||
COPY frontend/ ./
|
||||
|
||||
|
|
|
@ -56,6 +56,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"connect-history-api-fallback": "2.0.0",
|
||||
"express": "4.18.3"
|
||||
"express": "4.19.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -581,10 +581,10 @@ cookie-signature@1.0.6:
|
|||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
cookie@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
||||
core-util-is@1.0.2:
|
||||
version "1.0.2"
|
||||
|
@ -830,17 +830,17 @@ etag@~1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||
|
||||
express@4.18.3:
|
||||
version "4.18.3"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.3.tgz#6870746f3ff904dee1819b82e4b51509afffb0d4"
|
||||
integrity sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==
|
||||
express@4.19.0:
|
||||
version "4.19.0"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.0.tgz#c9f689a62522f3399132d49eacd9af177d8ccb9e"
|
||||
integrity sha512-/ERliX0l7UuHEgAy7HU2FRsiz3ScIKNl/iwnoYzHTJC0Sqj3ctWDD3MQ9CbUEfjshvxXImWaeukD0Xo7a2lWLA==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.2"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.5.0"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
|
|
|
@ -55,19 +55,20 @@ This document describes the different errors Vikunja can return.
|
|||
|
||||
## Project
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 3001 | 404 | The project does not exist. |
|
||||
| 3004 | 403 | The user needs to have read permissions on that project to perform that action. |
|
||||
| 3005 | 400 | The project title cannot be empty. |
|
||||
| 3006 | 404 | The project share does not exist. |
|
||||
| 3007 | 400 | A project with this identifier already exists. |
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 3001 | 404 | The project does not exist. |
|
||||
| 3004 | 403 | The user needs to have read permissions on that project to perform that action. |
|
||||
| 3005 | 400 | The project title cannot be empty. |
|
||||
| 3006 | 404 | The project share does not exist. |
|
||||
| 3007 | 400 | A project with this identifier already exists. |
|
||||
| 3008 | 412 | The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project. |
|
||||
| 3009 | 412 | The project cannot belong to a dynamically generated parent project like "Favorites". |
|
||||
| 3010 | 412 | This project cannot be a child of itself. |
|
||||
| 3011 | 412 | This project cannot have a cyclic relationship to a parent project. |
|
||||
| 3012 | 412 | This project cannot be deleted because a user has set it as their default project. |
|
||||
| 3013 | 412 | This project cannot be archived because a user has set it as their default project. |
|
||||
| 3009 | 412 | The project cannot belong to a dynamically generated parent project like "Favorites". |
|
||||
| 3010 | 412 | This project cannot be a child of itself. |
|
||||
| 3011 | 412 | This project cannot have a cyclic relationship to a parent project. |
|
||||
| 3012 | 412 | This project cannot be deleted because a user has set it as their default project. |
|
||||
| 3013 | 412 | This project cannot be archived because a user has set it as their default project. |
|
||||
| 3014 | 404 | This project view does not exist. |
|
||||
|
||||
## Task
|
||||
|
||||
|
@ -98,6 +99,7 @@ This document describes the different errors Vikunja can return.
|
|||
| 4023 | 409 | Tried to create a task relation which would create a cycle. |
|
||||
| 4024 | 400 | The provided filter expression is invalid. |
|
||||
| 4025 | 400 | The reaction kind is invalid. |
|
||||
| 4026 | 400 | You must provide a project view ID when sorting by position. |
|
||||
|
||||
## Team
|
||||
|
||||
|
|
|
@ -1,15 +1,50 @@
|
|||
import {ProjectFactory} from '../../factories/project'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {ProjectViewFactory} from "../../factories/project_view";
|
||||
|
||||
export function createDefaultViews(projectId) {
|
||||
ProjectViewFactory.truncate()
|
||||
const list = ProjectViewFactory.create(1, {
|
||||
id: 1,
|
||||
project_id: projectId,
|
||||
view_kind: 0,
|
||||
}, false)
|
||||
const gantt = ProjectViewFactory.create(1, {
|
||||
id: 2,
|
||||
project_id: projectId,
|
||||
view_kind: 1,
|
||||
}, false)
|
||||
const table = ProjectViewFactory.create(1, {
|
||||
id: 3,
|
||||
project_id: projectId,
|
||||
view_kind: 2,
|
||||
}, false)
|
||||
const kanban = ProjectViewFactory.create(1, {
|
||||
id: 4,
|
||||
project_id: projectId,
|
||||
view_kind: 3,
|
||||
bucket_configuration_mode: 1,
|
||||
}, false)
|
||||
|
||||
return [
|
||||
list[0],
|
||||
gantt[0],
|
||||
table[0],
|
||||
kanban[0],
|
||||
]
|
||||
}
|
||||
|
||||
export function createProjects() {
|
||||
const projects = ProjectFactory.create(1, {
|
||||
title: 'First Project'
|
||||
})
|
||||
TaskFactory.truncate()
|
||||
projects.views = createDefaultViews(projects[0].id)
|
||||
return projects
|
||||
}
|
||||
|
||||
export function prepareProjects(setProjects = (...args: any[]) => {}) {
|
||||
export function prepareProjects(setProjects = (...args: any[]) => {
|
||||
}) {
|
||||
beforeEach(() => {
|
||||
const projects = createProjects()
|
||||
setProjects(projects)
|
||||
|
|
|
@ -2,6 +2,7 @@ import {createFakeUserAndLogin} from '../../support/authenticateUser'
|
|||
|
||||
import {ProjectFactory} from '../../factories/project'
|
||||
import {prepareProjects} from './prepareProjects'
|
||||
import {ProjectViewFactory} from '../../factories/project_view'
|
||||
|
||||
describe('Project History', () => {
|
||||
createFakeUserAndLogin()
|
||||
|
@ -12,23 +13,28 @@ describe('Project History', () => {
|
|||
cy.intercept(Cypress.env('API_URL') + '/projects/*').as('loadProject')
|
||||
|
||||
const projects = ProjectFactory.create(6)
|
||||
ProjectViewFactory.truncate()
|
||||
projects.forEach(p => ProjectViewFactory.create(1, {
|
||||
id: p.id,
|
||||
project_id: p.id,
|
||||
}, false))
|
||||
|
||||
cy.visit('/')
|
||||
cy.wait('@loadProjectArray')
|
||||
cy.get('body')
|
||||
.should('not.contain', 'Last viewed')
|
||||
|
||||
cy.visit(`/projects/${projects[0].id}`)
|
||||
cy.visit(`/projects/${projects[0].id}/${projects[0].id}`)
|
||||
cy.wait('@loadProject')
|
||||
cy.visit(`/projects/${projects[1].id}`)
|
||||
cy.visit(`/projects/${projects[1].id}/${projects[1].id}`)
|
||||
cy.wait('@loadProject')
|
||||
cy.visit(`/projects/${projects[2].id}`)
|
||||
cy.visit(`/projects/${projects[2].id}/${projects[2].id}`)
|
||||
cy.wait('@loadProject')
|
||||
cy.visit(`/projects/${projects[3].id}`)
|
||||
cy.visit(`/projects/${projects[3].id}/${projects[3].id}`)
|
||||
cy.wait('@loadProject')
|
||||
cy.visit(`/projects/${projects[4].id}`)
|
||||
cy.visit(`/projects/${projects[4].id}/${projects[4].id}`)
|
||||
cy.wait('@loadProject')
|
||||
cy.visit(`/projects/${projects[5].id}`)
|
||||
cy.visit(`/projects/${projects[5].id}/${projects[5].id}`)
|
||||
cy.wait('@loadProject')
|
||||
|
||||
// cy.visit('/')
|
||||
|
|
|
@ -11,7 +11,7 @@ describe('Project View Gantt', () => {
|
|||
|
||||
it('Hides tasks with no dates', () => {
|
||||
const tasks = TaskFactory.create(1)
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.g-gantt-rows-container')
|
||||
.should('not.contain', tasks[0].title)
|
||||
|
@ -25,7 +25,7 @@ describe('Project View Gantt', () => {
|
|||
nextMonth.setDate(1)
|
||||
nextMonth.setMonth(9)
|
||||
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.g-timeunits-container')
|
||||
.should('contain', format(now, 'MMMM'))
|
||||
|
@ -38,7 +38,7 @@ describe('Project View Gantt', () => {
|
|||
start_date: now.toISOString(),
|
||||
end_date: new Date(new Date(now).setDate(now.getDate() + 4)).toISOString(),
|
||||
})
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.g-gantt-rows-container')
|
||||
.should('not.be.empty')
|
||||
|
@ -50,7 +50,7 @@ describe('Project View Gantt', () => {
|
|||
start_date: null,
|
||||
end_date: null,
|
||||
})
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.gantt-options .fancycheckbox')
|
||||
.contains('Show tasks which don\'t have dates set')
|
||||
|
@ -69,7 +69,7 @@ describe('Project View Gantt', () => {
|
|||
start_date: now.toISOString(),
|
||||
end_date: new Date(new Date(now).setDate(now.getDate() + 4)).toISOString(),
|
||||
})
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.g-gantt-rows-container .g-gantt-row .g-gantt-row-bars-container div .g-gantt-bar')
|
||||
.first()
|
||||
|
@ -83,7 +83,7 @@ describe('Project View Gantt', () => {
|
|||
const now = Date.UTC(2022, 10, 9)
|
||||
cy.clock(now, ['Date'])
|
||||
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.project-gantt .gantt-options .field .control input.input.form-control')
|
||||
.click()
|
||||
|
@ -99,7 +99,7 @@ describe('Project View Gantt', () => {
|
|||
})
|
||||
|
||||
it('Should change the date range based on date query parameters', () => {
|
||||
cy.visit('/projects/1/gantt?dateFrom=2022-09-25&dateTo=2022-11-05')
|
||||
cy.visit('/projects/1/2?dateFrom=2022-09-25&dateTo=2022-11-05')
|
||||
|
||||
cy.get('.g-timeunits-container')
|
||||
.should('contain', 'September 2022')
|
||||
|
@ -115,7 +115,7 @@ describe('Project View Gantt', () => {
|
|||
start_date: formatISO(now),
|
||||
end_date: formatISO(now.setDate(now.getDate() + 4)),
|
||||
})
|
||||
cy.visit('/projects/1/gantt')
|
||||
cy.visit('/projects/1/2')
|
||||
|
||||
cy.get('.gantt-container .g-gantt-chart .g-gantt-row-bars-container .g-gantt-bar')
|
||||
.dblclick()
|
||||
|
|
|
@ -4,35 +4,64 @@ import {BucketFactory} from '../../factories/bucket'
|
|||
import {ProjectFactory} from '../../factories/project'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {prepareProjects} from './prepareProjects'
|
||||
import {ProjectViewFactory} from "../../factories/project_view";
|
||||
import {TaskBucketFactory} from "../../factories/task_buckets";
|
||||
|
||||
function createSingleTaskInBucket(count = 1, attrs = {}) {
|
||||
const projects = ProjectFactory.create(1)
|
||||
const buckets = BucketFactory.create(2, {
|
||||
const views = ProjectViewFactory.create(1, {
|
||||
id: 1,
|
||||
project_id: projects[0].id,
|
||||
view_kind: 3,
|
||||
bucket_configuration_mode: 1,
|
||||
})
|
||||
const buckets = BucketFactory.create(2, {
|
||||
project_view_id: views[0].id,
|
||||
})
|
||||
const tasks = TaskFactory.create(count, {
|
||||
project_id: projects[0].id,
|
||||
bucket_id: buckets[0].id,
|
||||
...attrs,
|
||||
})
|
||||
return tasks[0]
|
||||
TaskBucketFactory.create(1, {
|
||||
task_id: tasks[0].id,
|
||||
bucket_id: buckets[0].id,
|
||||
project_view_id: views[0].id,
|
||||
})
|
||||
return {
|
||||
task: tasks[0],
|
||||
view: views[0],
|
||||
project: projects[0],
|
||||
}
|
||||
}
|
||||
|
||||
function createTaskWithBuckets(buckets, count = 1) {
|
||||
const data = TaskFactory.create(10, {
|
||||
project_id: 1,
|
||||
})
|
||||
TaskBucketFactory.truncate()
|
||||
data.forEach(t => TaskBucketFactory.create(count, {
|
||||
task_id: t.id,
|
||||
bucket_id: buckets[0].id,
|
||||
project_view_id: buckets[0].project_view_id,
|
||||
}, false))
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
describe('Project View Kanban', () => {
|
||||
createFakeUserAndLogin()
|
||||
prepareProjects()
|
||||
|
||||
|
||||
let buckets
|
||||
beforeEach(() => {
|
||||
buckets = BucketFactory.create(2)
|
||||
buckets = BucketFactory.create(2, {
|
||||
project_view_id: 4,
|
||||
})
|
||||
})
|
||||
|
||||
it('Shows all buckets with their tasks', () => {
|
||||
const data = TaskFactory.create(10, {
|
||||
project_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/kanban')
|
||||
const data = createTaskWithBuckets(buckets, 10)
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket .title')
|
||||
.contains(buckets[0].title)
|
||||
|
@ -46,11 +75,8 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Can add a new task to a bucket', () => {
|
||||
TaskFactory.create(2, {
|
||||
project_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/kanban')
|
||||
createTaskWithBuckets(buckets, 2)
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket')
|
||||
.contains(buckets[0].title)
|
||||
|
@ -68,7 +94,7 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Can create a new bucket', () => {
|
||||
cy.visit('/projects/1/kanban')
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket.new-bucket .button')
|
||||
.click()
|
||||
|
@ -82,7 +108,7 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Can set a bucket limit', () => {
|
||||
cy.visit('/projects/1/kanban')
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
|
||||
.first()
|
||||
|
@ -103,7 +129,7 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Can rename a bucket', () => {
|
||||
cy.visit('/projects/1/kanban')
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket .bucket-header .title')
|
||||
.first()
|
||||
|
@ -114,7 +140,7 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Can delete a bucket', () => {
|
||||
cy.visit('/projects/1/kanban')
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
|
||||
.first()
|
||||
|
@ -137,17 +163,14 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Can drag tasks around', () => {
|
||||
const tasks = TaskFactory.create(2, {
|
||||
project_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/kanban')
|
||||
const tasks = createTaskWithBuckets(buckets, 2)
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket .tasks .task')
|
||||
.contains(tasks[0].title)
|
||||
.first()
|
||||
.drag('.kanban .bucket:nth-child(2) .tasks')
|
||||
|
||||
|
||||
cy.get('.kanban .bucket:nth-child(2) .tasks')
|
||||
.should('contain', tasks[0].title)
|
||||
cy.get('.kanban .bucket:nth-child(1) .tasks')
|
||||
|
@ -155,12 +178,8 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Should navigate to the task when the task card is clicked', () => {
|
||||
const tasks = TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
project_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/kanban')
|
||||
const tasks = createTaskWithBuckets(buckets, 5)
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.kanban .bucket .tasks .task')
|
||||
.contains(tasks[0].title)
|
||||
|
@ -168,28 +187,33 @@ describe('Project View Kanban', () => {
|
|||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', `/tasks/${tasks[0].id}`, { timeout: 1000 })
|
||||
.should('contain', `/tasks/${tasks[0].id}`, {timeout: 1000})
|
||||
})
|
||||
|
||||
it('Should remove a task from the kanban board when moving it to another project', () => {
|
||||
const projects = ProjectFactory.create(2)
|
||||
BucketFactory.create(2, {
|
||||
const views = ProjectViewFactory.create(2, {
|
||||
project_id: '{increment}',
|
||||
view_kind: 3,
|
||||
bucket_configuration_mode: 1,
|
||||
})
|
||||
BucketFactory.create(2)
|
||||
const tasks = TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
project_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
TaskBucketFactory.create(5, {
|
||||
project_view_id: 1,
|
||||
})
|
||||
const task = tasks[0]
|
||||
cy.visit('/projects/1/kanban')
|
||||
cy.visit('/projects/1/'+views[0].id)
|
||||
|
||||
cy.get('.kanban .bucket .tasks .task')
|
||||
.contains(task.title)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .action-buttons .button', { timeout: 3000 })
|
||||
cy.get('.task-view .action-buttons .button', {timeout: 3000})
|
||||
.contains('Move')
|
||||
.click()
|
||||
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
|
||||
|
@ -201,27 +225,23 @@ describe('Project View Kanban', () => {
|
|||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 1000 })
|
||||
cy.get('.global-notification', {timeout: 1000})
|
||||
.should('contain', 'Success')
|
||||
cy.go('back')
|
||||
cy.get('.kanban .bucket')
|
||||
.should('not.contain', task.title)
|
||||
})
|
||||
|
||||
|
||||
it('Shows a button to filter the kanban board', () => {
|
||||
const data = TaskFactory.create(10, {
|
||||
project_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/kanban')
|
||||
|
||||
cy.visit('/projects/1/4')
|
||||
|
||||
cy.get('.project-kanban .filter-container .base-button')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
|
||||
it('Should remove a task from the board when deleting it', () => {
|
||||
const task = createSingleTaskInBucket(5)
|
||||
cy.visit('/projects/1/kanban')
|
||||
const {task, view} = createSingleTaskInBucket(5)
|
||||
cy.visit(`/projects/1/${view.id}`)
|
||||
|
||||
cy.get('.kanban .bucket .tasks .task')
|
||||
.contains(task.title)
|
||||
|
@ -239,18 +259,18 @@ describe('Project View Kanban', () => {
|
|||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
|
||||
|
||||
cy.get('.kanban .bucket .tasks')
|
||||
.should('not.contain', task.title)
|
||||
})
|
||||
|
||||
it('Should show a task description icon if the task has a description', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/buckets**').as('loadTasks')
|
||||
const task = createSingleTaskInBucket(1, {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/views/*/tasks**').as('loadTasks')
|
||||
const {task, view} = createSingleTaskInBucket(1, {
|
||||
description: 'Lorem Ipsum',
|
||||
})
|
||||
|
||||
cy.visit(`/projects/${task.project_id}/kanban`)
|
||||
cy.visit(`/projects/${task.project_id}/${view.id}`)
|
||||
cy.wait('@loadTasks')
|
||||
|
||||
cy.get('.bucket .tasks .task .footer .icon svg')
|
||||
|
@ -258,12 +278,12 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Should not show a task description icon if the task has an empty description', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/buckets**').as('loadTasks')
|
||||
const task = createSingleTaskInBucket(1, {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/views/*/tasks**').as('loadTasks')
|
||||
const {task, view} = createSingleTaskInBucket(1, {
|
||||
description: '',
|
||||
})
|
||||
|
||||
cy.visit(`/projects/${task.project_id}/kanban`)
|
||||
cy.visit(`/projects/${task.project_id}/${view.id}`)
|
||||
cy.wait('@loadTasks')
|
||||
|
||||
cy.get('.bucket .tasks .task .footer .icon svg')
|
||||
|
@ -271,15 +291,15 @@ describe('Project View Kanban', () => {
|
|||
})
|
||||
|
||||
it('Should not show a task description icon if the task has a description containing only an empty p tag', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/buckets**').as('loadTasks')
|
||||
const task = createSingleTaskInBucket(1, {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/views/*/tasks**').as('loadTasks')
|
||||
const {task, view} = createSingleTaskInBucket(1, {
|
||||
description: '<p></p>',
|
||||
})
|
||||
|
||||
cy.visit(`/projects/${task.project_id}/kanban`)
|
||||
cy.visit(`/projects/${task.project_id}/${view.id}`)
|
||||
cy.wait('@loadTasks')
|
||||
|
||||
cy.get('.bucket .tasks .task .footer .icon svg')
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5,15 +5,16 @@ import {TaskFactory} from '../../factories/task'
|
|||
import {UserFactory} from '../../factories/user'
|
||||
import {ProjectFactory} from '../../factories/project'
|
||||
import {prepareProjects} from './prepareProjects'
|
||||
import {BucketFactory} from '../../factories/bucket'
|
||||
|
||||
describe('Project View Project', () => {
|
||||
describe('Project View List', () => {
|
||||
createFakeUserAndLogin()
|
||||
prepareProjects()
|
||||
|
||||
it('Should be an empty project', () => {
|
||||
cy.visit('/projects/1')
|
||||
cy.url()
|
||||
.should('contain', '/projects/1/list')
|
||||
.should('contain', '/projects/1/1')
|
||||
cy.get('.project-title')
|
||||
.should('contain', 'First Project')
|
||||
cy.get('.project-title-dropdown')
|
||||
|
@ -24,6 +25,10 @@ describe('Project View Project', () => {
|
|||
})
|
||||
|
||||
it('Should create a new task', () => {
|
||||
BucketFactory.create(2, {
|
||||
project_view_id: 4,
|
||||
})
|
||||
|
||||
const newTaskTitle = 'New task'
|
||||
|
||||
cy.visit('/projects/1')
|
||||
|
@ -38,7 +43,7 @@ describe('Project View Project', () => {
|
|||
id: '{increment}',
|
||||
project_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
|
||||
cy.get('.tasks .task .tasktext')
|
||||
.contains(tasks[0].title)
|
||||
|
@ -88,10 +93,10 @@ describe('Project View Project', () => {
|
|||
title: i => `task${i}`,
|
||||
project_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
|
||||
cy.get('.tasks')
|
||||
.should('contain', tasks[1].title)
|
||||
.should('contain', tasks[20].title)
|
||||
cy.get('.tasks')
|
||||
.should('not.contain', tasks[99].title)
|
||||
|
||||
|
@ -104,6 +109,6 @@ describe('Project View Project', () => {
|
|||
cy.get('.tasks')
|
||||
.should('contain', tasks[99].title)
|
||||
cy.get('.tasks')
|
||||
.should('not.contain', tasks[1].title)
|
||||
.should('not.contain', tasks[20].title)
|
||||
})
|
||||
})
|
|
@ -1,13 +1,15 @@
|
|||
import {createFakeUserAndLogin} from '../../support/authenticateUser'
|
||||
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {prepareProjects} from './prepareProjects'
|
||||
|
||||
describe('Project View Table', () => {
|
||||
createFakeUserAndLogin()
|
||||
prepareProjects()
|
||||
|
||||
it('Should show a table with tasks', () => {
|
||||
const tasks = TaskFactory.create(1)
|
||||
cy.visit('/projects/1/table')
|
||||
cy.visit('/projects/1/3')
|
||||
|
||||
cy.get('.project-table table.table')
|
||||
.should('exist')
|
||||
|
@ -17,7 +19,7 @@ describe('Project View Table', () => {
|
|||
|
||||
it('Should have working column switches', () => {
|
||||
TaskFactory.create(1)
|
||||
cy.visit('/projects/1/table')
|
||||
cy.visit('/projects/1/3')
|
||||
|
||||
cy.get('.project-table .filter-container .items .button')
|
||||
.contains('Columns')
|
||||
|
@ -42,7 +44,7 @@ describe('Project View Table', () => {
|
|||
id: '{increment}',
|
||||
project_id: 1,
|
||||
})
|
||||
cy.visit('/projects/1/table')
|
||||
cy.visit('/projects/1/3')
|
||||
|
||||
cy.get('.project-table table.table')
|
||||
.contains(tasks[0].title)
|
||||
|
|
|
@ -33,14 +33,14 @@ describe('Projects', () => {
|
|||
})
|
||||
|
||||
it('Should redirect to a specific project view after visited', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/*/buckets*').as('loadBuckets')
|
||||
cy.visit('/projects/1/kanban')
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/*/views/*/tasks**').as('loadBuckets')
|
||||
cy.visit('/projects/1/4')
|
||||
cy.url()
|
||||
.should('contain', '/projects/1/kanban')
|
||||
.should('contain', '/projects/1/4')
|
||||
cy.wait('@loadBuckets')
|
||||
cy.visit('/projects/1')
|
||||
cy.url()
|
||||
.should('contain', '/projects/1/kanban')
|
||||
.should('contain', '/projects/1/4')
|
||||
})
|
||||
|
||||
it('Should rename the project in all places', () => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {LinkShareFactory} from '../../factories/link_sharing'
|
||||
import {ProjectFactory} from '../../factories/project'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {createProjects} from '../project/prepareProjects'
|
||||
|
||||
function prepareLinkShare() {
|
||||
const projects = ProjectFactory.create(1)
|
||||
const projects = createProjects()
|
||||
const tasks = TaskFactory.create(10, {
|
||||
project_id: projects[0].id
|
||||
})
|
||||
|
@ -32,13 +32,13 @@ describe('Link shares', () => {
|
|||
cy.get('.tasks')
|
||||
.should('contain', tasks[0].title)
|
||||
|
||||
cy.url().should('contain', `/projects/${project.id}/list#share-auth-token=${share.hash}`)
|
||||
cy.url().should('contain', `/projects/${project.id}/1#share-auth-token=${share.hash}`)
|
||||
})
|
||||
|
||||
it('Should work when directly viewing a project with share hash present', () => {
|
||||
const {share, project, tasks} = prepareLinkShare()
|
||||
|
||||
cy.visit(`/projects/${project.id}/list#share-auth-token=${share.hash}`)
|
||||
cy.visit(`/projects/${project.id}/1#share-auth-token=${share.hash}`)
|
||||
|
||||
cy.get('h1.title')
|
||||
.should('contain', project.title)
|
||||
|
|
|
@ -5,11 +5,13 @@ import {seed} from '../../support/seed'
|
|||
import {TaskFactory} from '../../factories/task'
|
||||
import {BucketFactory} from '../../factories/bucket'
|
||||
import {updateUserSettings} from '../../support/updateUserSettings'
|
||||
import {createDefaultViews} from "../project/prepareProjects";
|
||||
|
||||
function seedTasks(numberOfTasks = 50, startDueDate = new Date()) {
|
||||
const project = ProjectFactory.create()[0]
|
||||
const views = createDefaultViews(project.id)
|
||||
BucketFactory.create(1, {
|
||||
project_id: project.id,
|
||||
project_view_id: views[3].id,
|
||||
})
|
||||
const tasks = []
|
||||
let dueDate = startDueDate
|
||||
|
@ -60,7 +62,7 @@ describe('Home Page Task Overview', () => {
|
|||
})
|
||||
|
||||
it('Should show a new task with a very soon due date at the top', () => {
|
||||
const {tasks} = seedTasks()
|
||||
const {tasks} = seedTasks(49)
|
||||
const newTaskTitle = 'New Task'
|
||||
|
||||
cy.visit('/')
|
||||
|
@ -71,9 +73,8 @@ describe('Home Page Task Overview', () => {
|
|||
due_date: new Date().toISOString(),
|
||||
}, false)
|
||||
|
||||
cy.visit(`/projects/${tasks[0].project_id}/list`)
|
||||
cy.visit(`/projects/${tasks[0].project_id}/1`)
|
||||
cy.get('.tasks .task')
|
||||
.first()
|
||||
.should('contain.text', newTaskTitle)
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
|
@ -88,7 +89,7 @@ describe('Home Page Task Overview', () => {
|
|||
|
||||
cy.visit('/')
|
||||
|
||||
cy.visit(`/projects/${tasks[0].project_id}/list`)
|
||||
cy.visit(`/projects/${tasks[0].project_id}/1`)
|
||||
cy.get('.task-add textarea')
|
||||
.type(newTaskTitle+'{enter}')
|
||||
cy.visit('/')
|
||||
|
|
|
@ -12,6 +12,7 @@ import {BucketFactory} from '../../factories/bucket'
|
|||
|
||||
import {TaskAttachmentFactory} from '../../factories/task_attachments'
|
||||
import {TaskReminderFactory} from '../../factories/task_reminders'
|
||||
import {createDefaultViews} from "../project/prepareProjects";
|
||||
|
||||
function addLabelToTaskAndVerify(labelTitle: string) {
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
|
@ -53,15 +54,16 @@ describe('Task', () => {
|
|||
beforeEach(() => {
|
||||
// UserFactory.create(1)
|
||||
projects = ProjectFactory.create(1)
|
||||
const views = createDefaultViews(projects[0].id)
|
||||
buckets = BucketFactory.create(1, {
|
||||
project_id: projects[0].id,
|
||||
project_view_id: views[3].id,
|
||||
})
|
||||
TaskFactory.truncate()
|
||||
UserProjectFactory.truncate()
|
||||
})
|
||||
|
||||
it('Should be created new', () => {
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.get('.input[placeholder="Add a new task…"')
|
||||
.type('New Task')
|
||||
cy.get('.button')
|
||||
|
@ -75,7 +77,7 @@ describe('Task', () => {
|
|||
it('Inserts new tasks at the top of the project', () => {
|
||||
TaskFactory.create(1)
|
||||
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.get('.project-is-empty-notice')
|
||||
.should('not.exist')
|
||||
cy.get('.input[placeholder="Add a new task…"')
|
||||
|
@ -93,7 +95,7 @@ describe('Task', () => {
|
|||
it('Marks a task as done', () => {
|
||||
TaskFactory.create(1)
|
||||
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.get('.tasks .task .fancycheckbox')
|
||||
.first()
|
||||
.click()
|
||||
|
@ -104,7 +106,7 @@ describe('Task', () => {
|
|||
it('Can add a task to favorites', () => {
|
||||
TaskFactory.create(1)
|
||||
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.get('.tasks .task .favorite')
|
||||
.first()
|
||||
.click()
|
||||
|
@ -113,12 +115,12 @@ describe('Task', () => {
|
|||
})
|
||||
|
||||
it('Should show a task description icon if the task has a description', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/tasks**').as('loadTasks')
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/views/*/tasks**').as('loadTasks')
|
||||
TaskFactory.create(1, {
|
||||
description: 'Lorem Ipsum',
|
||||
})
|
||||
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.wait('@loadTasks')
|
||||
|
||||
cy.get('.tasks .task .project-task-icon')
|
||||
|
@ -126,12 +128,12 @@ describe('Task', () => {
|
|||
})
|
||||
|
||||
it('Should not show a task description icon if the task has an empty description', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/tasks**').as('loadTasks')
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/views/*/tasks**').as('loadTasks')
|
||||
TaskFactory.create(1, {
|
||||
description: '',
|
||||
})
|
||||
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.wait('@loadTasks')
|
||||
|
||||
cy.get('.tasks .task .project-task-icon')
|
||||
|
@ -139,12 +141,12 @@ describe('Task', () => {
|
|||
})
|
||||
|
||||
it('Should not show a task description icon if the task has a description containing only an empty p tag', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/tasks**').as('loadTasks')
|
||||
cy.intercept(Cypress.env('API_URL') + '/projects/1/views/*/tasks**').as('loadTasks')
|
||||
TaskFactory.create(1, {
|
||||
description: '<p></p>',
|
||||
})
|
||||
|
||||
cy.visit('/projects/1/list')
|
||||
cy.visit('/projects/1/1')
|
||||
cy.wait('@loadTasks')
|
||||
|
||||
cy.get('.tasks .task .project-task-icon')
|
||||
|
@ -314,8 +316,9 @@ describe('Task', () => {
|
|||
|
||||
it('Can move a task to another project', () => {
|
||||
const projects = ProjectFactory.create(2)
|
||||
const views = createDefaultViews(projects[0].id)
|
||||
BucketFactory.create(2, {
|
||||
project_id: '{increment}',
|
||||
project_view_id: views[3].id,
|
||||
})
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
|
@ -464,12 +467,11 @@ describe('Task', () => {
|
|||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
project_id: projects[0].id,
|
||||
bucket_id: buckets[0].id,
|
||||
})
|
||||
const labels = LabelFactory.create(1)
|
||||
LabelTaskFactory.truncate()
|
||||
|
||||
cy.visit(`/projects/${projects[0].id}/kanban`)
|
||||
cy.visit(`/projects/${projects[0].id}/4`)
|
||||
|
||||
cy.get('.bucket .task')
|
||||
.contains(tasks[0].title)
|
||||
|
@ -831,12 +833,11 @@ describe('Task', () => {
|
|||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
project_id: projects[0].id,
|
||||
bucket_id: buckets[0].id,
|
||||
})
|
||||
const labels = LabelFactory.create(1)
|
||||
LabelTaskFactory.truncate()
|
||||
|
||||
cy.visit(`/projects/${projects[0].id}/kanban`)
|
||||
cy.visit(`/projects/${projects[0].id}/4`)
|
||||
|
||||
cy.get('.bucket .task')
|
||||
.contains(tasks[0].title)
|
||||
|
|
|
@ -10,7 +10,7 @@ export class BucketFactory extends Factory {
|
|||
return {
|
||||
id: '{increment}',
|
||||
title: faker.lorem.words(3),
|
||||
project_id: 1,
|
||||
project_view_id: '{increment}',
|
||||
created_by_id: 1,
|
||||
created: now.toISOString(),
|
||||
updated: now.toISOString(),
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import {Factory} from '../support/factory'
|
||||
import {faker} from '@faker-js/faker'
|
||||
|
||||
export class ProjectViewFactory extends Factory {
|
||||
static table = 'project_views'
|
||||
|
||||
static factory() {
|
||||
const now = new Date()
|
||||
|
||||
return {
|
||||
id: '{increment}',
|
||||
title: faker.lorem.words(3),
|
||||
project_id: '{increment}',
|
||||
view_kind: 0,
|
||||
created: now.toISOString(),
|
||||
updated: now.toISOString(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ export class TaskFactory extends Factory {
|
|||
project_id: 1,
|
||||
created_by_id: 1,
|
||||
index: '{increment}',
|
||||
position: '{increment}',
|
||||
created: now.toISOString(),
|
||||
updated: now.toISOString()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import {Factory} from '../support/factory'
|
||||
|
||||
export class TaskBucketFactory extends Factory {
|
||||
static table = 'task_buckets'
|
||||
|
||||
static factory() {
|
||||
return {
|
||||
task_id: '{increment}',
|
||||
bucket_id: '{increment}',
|
||||
project_view_id: '{increment}',
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"homepage": "https://vikunja.io/",
|
||||
"funding": "https://opencollective.com/vikunja",
|
||||
"packageManager": "pnpm@8.15.4",
|
||||
"packageManager": "pnpm@8.15.5",
|
||||
"keywords": [
|
||||
"todo",
|
||||
"productivity",
|
||||
|
@ -55,9 +55,9 @@
|
|||
"@fortawesome/free-solid-svg-icons": "6.5.1",
|
||||
"@fortawesome/vue-fontawesome": "3.0.6",
|
||||
"@github/hotkey": "3.1.0",
|
||||
"@infectoone/vue-ganttastic": "2.2.0",
|
||||
"@infectoone/vue-ganttastic": "2.3.1",
|
||||
"@intlify/unplugin-vue-i18n": "3.0.1",
|
||||
"@kyvg/vue3-notification": "3.2.0",
|
||||
"@kyvg/vue3-notification": "3.2.1",
|
||||
"@sentry/tracing": "7.107.0",
|
||||
"@sentry/vue": "7.107.0",
|
||||
"@tiptap/core": "2.2.4",
|
||||
|
@ -101,9 +101,9 @@
|
|||
"blurhash": "2.0.5",
|
||||
"bulma-css-variables": "0.9.33",
|
||||
"camel-case": "4.1.2",
|
||||
"date-fns": "3.5.0",
|
||||
"date-fns": "3.6.0",
|
||||
"dayjs": "1.11.10",
|
||||
"dompurify": "3.0.9",
|
||||
"dompurify": "3.0.10",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"flatpickr": "4.6.13",
|
||||
"flexsearch": "0.7.31",
|
||||
|
@ -117,7 +117,7 @@
|
|||
"snake-case": "3.0.4",
|
||||
"sortablejs": "1.15.2",
|
||||
"tippy.js": "6.3.7",
|
||||
"ufo": "1.5.1",
|
||||
"ufo": "1.5.3",
|
||||
"vue": "3.4.21",
|
||||
"vue-advanced-cropper": "2.8.8",
|
||||
"vue-flatpickr-component": "11.0.5",
|
||||
|
@ -134,7 +134,7 @@
|
|||
"@faker-js/faker": "8.4.1",
|
||||
"@histoire/plugin-screenshot": "0.17.14",
|
||||
"@histoire/plugin-vue": "0.17.14",
|
||||
"@rushstack/eslint-patch": "1.7.2",
|
||||
"@rushstack/eslint-patch": "1.8.0",
|
||||
"@tsconfig/node18": "18.2.2",
|
||||
"@types/codemirror": "5.60.15",
|
||||
"@types/dompurify": "3.0.5",
|
||||
|
@ -142,11 +142,11 @@
|
|||
"@types/is-touch-device": "1.0.2",
|
||||
"@types/lodash.debounce": "4.0.9",
|
||||
"@types/marked": "5.0.2",
|
||||
"@types/node": "20.11.28",
|
||||
"@types/node": "20.11.30",
|
||||
"@types/postcss-preset-env": "7.7.0",
|
||||
"@types/sortablejs": "1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
"@typescript-eslint/parser": "7.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.3.1",
|
||||
"@typescript-eslint/parser": "7.3.1",
|
||||
"@vitejs/plugin-legacy": "5.3.2",
|
||||
"@vitejs/plugin-vue": "5.0.4",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
|
@ -154,20 +154,20 @@
|
|||
"@vue/tsconfig": "0.5.1",
|
||||
"autoprefixer": "10.4.18",
|
||||
"browserslist": "4.23.0",
|
||||
"caniuse-lite": "1.0.30001597",
|
||||
"caniuse-lite": "1.0.30001599",
|
||||
"css-has-pseudo": "6.0.2",
|
||||
"csstype": "3.1.3",
|
||||
"cypress": "13.7.0",
|
||||
"esbuild": "0.20.2",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-vue": "9.23.0",
|
||||
"happy-dom": "13.8.6",
|
||||
"happy-dom": "14.0.0",
|
||||
"histoire": "0.17.14",
|
||||
"postcss": "8.4.35",
|
||||
"postcss": "8.4.37",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
"postcss-easings": "4.0.0",
|
||||
"postcss-focus-within": "8.0.1",
|
||||
"postcss-preset-env": "9.5.1",
|
||||
"postcss-preset-env": "9.5.2",
|
||||
"rollup": "4.13.0",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"sass": "1.72.0",
|
||||
|
@ -175,7 +175,7 @@
|
|||
"typescript": "5.4.2",
|
||||
"vite": "5.1.6",
|
||||
"vite-plugin-inject-preload": "1.3.3",
|
||||
"vite-plugin-pwa": "0.19.4",
|
||||
"vite-plugin-pwa": "0.19.5",
|
||||
"vite-plugin-sentry": "1.4.0",
|
||||
"vite-svg-loader": "5.1.0",
|
||||
"vitest": "1.4.0",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -47,7 +47,7 @@ import AddToHomeScreen from '@/components/home/AddToHomeScreen.vue'
|
|||
import DemoMode from '@/components/home/DemoMode.vue'
|
||||
|
||||
const importAccountDeleteService = () => import('@/services/accountDelete')
|
||||
const importMessage = () => import('@/message')
|
||||
import {success} from '@/message'
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const authStore = useAuthStore()
|
||||
|
@ -69,11 +69,9 @@ watch(accountDeletionConfirm, async (accountDeletionConfirm) => {
|
|||
return
|
||||
}
|
||||
|
||||
const messageP = importMessage()
|
||||
const AccountDeleteService = (await importAccountDeleteService()).default
|
||||
const accountDeletionService = new AccountDeleteService()
|
||||
await accountDeletionService.confirm(accountDeletionConfirm)
|
||||
const {success} = await messageP
|
||||
success({message: t('user.deletion.confirmSuccess')})
|
||||
authStore.refreshUserInfo()
|
||||
}, { immediate: true })
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
v-slot="{ Component }"
|
||||
:route="routeWithModal"
|
||||
>
|
||||
<keep-alive :include="['project.list', 'project.gantt', 'project.table', 'project.kanban']">
|
||||
<keep-alive :include="['project.view']">
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
|
|
|
@ -33,11 +33,15 @@ import {useBaseStore} from '@/stores/base'
|
|||
|
||||
import Logo from '@/components/home/Logo.vue'
|
||||
import PoweredByLink from './PoweredByLink.vue'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const currentProject = computed(() => baseStore.currentProject)
|
||||
const background = computed(() => baseStore.background)
|
||||
const logoVisible = computed(() => baseStore.logoVisible)
|
||||
|
||||
const projectStore = useProjectStore()
|
||||
projectStore.loadAllProjects()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -62,7 +62,7 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
|
|||
},
|
||||
{
|
||||
title: 'project.kanban.title',
|
||||
available: (route) => route.name === 'project.kanban',
|
||||
available: (route) => route.name === 'project.view',
|
||||
shortcuts: [
|
||||
{
|
||||
title: 'keyboardShortcuts.task.done',
|
||||
|
|
|
@ -6,44 +6,17 @@
|
|||
<h1 class="project-title-print">
|
||||
{{ getProjectTitle(currentProject) }}
|
||||
</h1>
|
||||
|
||||
|
||||
<div class="switch-view-container d-print-none">
|
||||
<div class="switch-view">
|
||||
<BaseButton
|
||||
v-shortcut="'g l'"
|
||||
:title="$t('keyboardShortcuts.project.switchToListView')"
|
||||
v-for="v in views"
|
||||
:key="v.id"
|
||||
class="switch-view-button"
|
||||
:class="{'is-active': viewName === 'project'}"
|
||||
:to="{ name: 'project.list', params: { projectId } }"
|
||||
:class="{'is-active': v.id === viewId}"
|
||||
:to="{ name: 'project.view', params: { projectId, viewId: v.id } }"
|
||||
>
|
||||
{{ $t('project.list.title') }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-shortcut="'g g'"
|
||||
:title="$t('keyboardShortcuts.project.switchToGanttView')"
|
||||
class="switch-view-button"
|
||||
:class="{'is-active': viewName === 'gantt'}"
|
||||
:to="{ name: 'project.gantt', params: { projectId } }"
|
||||
>
|
||||
{{ $t('project.gantt.title') }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-shortcut="'g t'"
|
||||
:title="$t('keyboardShortcuts.project.switchToTableView')"
|
||||
class="switch-view-button"
|
||||
:class="{'is-active': viewName === 'table'}"
|
||||
:to="{ name: 'project.table', params: { projectId } }"
|
||||
>
|
||||
{{ $t('project.table.title') }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-shortcut="'g k'"
|
||||
:title="$t('keyboardShortcuts.project.switchToKanbanView')"
|
||||
class="switch-view-button"
|
||||
:class="{'is-active': viewName === 'kanban'}"
|
||||
:to="{ name: 'project.kanban', params: { projectId } }"
|
||||
>
|
||||
{{ $t('project.kanban.title') }}
|
||||
{{ getViewTitle(v) }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
<slot name="header" />
|
||||
|
@ -63,7 +36,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, watch} from 'vue'
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
@ -79,26 +52,27 @@ import {useTitle} from '@/composables/useTitle'
|
|||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const props = defineProps({
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
viewName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const {t} = useI18n()
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const projectStore = useProjectStore()
|
||||
const projectService = ref(new ProjectService())
|
||||
const loadedProjectId = ref(0)
|
||||
|
||||
const currentProject = computed(() => {
|
||||
const currentProject = computed<IProject>(() => {
|
||||
return typeof baseStore.currentProject === 'undefined' ? {
|
||||
id: 0,
|
||||
title: '',
|
||||
|
@ -108,13 +82,15 @@ const currentProject = computed(() => {
|
|||
})
|
||||
useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value) : '')
|
||||
|
||||
const views = computed(() => currentProject.value?.views)
|
||||
|
||||
// watchEffect would be called every time the prop would get a value assigned, even if that value was the same as before.
|
||||
// This resulted in loading and setting the project multiple times, even when navigating away from it.
|
||||
// This caused wired bugs where the project background would be set on the home page but only right after setting a new
|
||||
// project background and then navigating to home. It also highlighted the project in the menu and didn't allow changing any
|
||||
// of it, most likely due to the rights not being properly populated.
|
||||
watch(
|
||||
() => props.projectId,
|
||||
() => projectId,
|
||||
// loadProject
|
||||
async (projectIdToLoad: number) => {
|
||||
const projectData = {id: projectIdToLoad}
|
||||
|
@ -130,11 +106,11 @@ watch(
|
|||
)
|
||||
&& typeof currentProject.value !== 'undefined' && currentProject.value.maxRight !== null
|
||||
) {
|
||||
loadedProjectId.value = props.projectId
|
||||
loadedProjectId.value = projectId
|
||||
return
|
||||
}
|
||||
|
||||
console.debug(`Loading project, props.viewName = ${props.viewName}, $route.params =`, route.params, `, loadedProjectId = ${loadedProjectId.value}, currentProject = `, currentProject.value)
|
||||
console.debug('Loading project, $route.params =', route.params, `, loadedProjectId = ${loadedProjectId.value}, currentProject = `, currentProject.value)
|
||||
|
||||
// Set the current project to the one we're about to load so that the title is already shown at the top
|
||||
loadedProjectId.value = 0
|
||||
|
@ -149,31 +125,46 @@ watch(
|
|||
const loadedProject = await projectService.value.get(project)
|
||||
baseStore.handleSetCurrentProject({project: loadedProject})
|
||||
} finally {
|
||||
loadedProjectId.value = props.projectId
|
||||
loadedProjectId.value = projectId
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
function getViewTitle(view: IProjectView) {
|
||||
switch (view.title) {
|
||||
case 'List':
|
||||
return t('project.list.title')
|
||||
case 'Gantt':
|
||||
return t('project.gantt.title')
|
||||
case 'Table':
|
||||
return t('project.table.title')
|
||||
case 'Kanban':
|
||||
return t('project.kanban.title')
|
||||
}
|
||||
|
||||
return view.title
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.switch-view-container {
|
||||
@media screen and (max-width: $tablet) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
@media screen and (max-width: $tablet) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-view {
|
||||
background: var(--white);
|
||||
display: inline-flex;
|
||||
border-radius: $radius;
|
||||
font-size: .75rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
height: $switch-view-height;
|
||||
margin: 0 auto 1rem;
|
||||
padding: .5rem;
|
||||
background: var(--white);
|
||||
display: inline-flex;
|
||||
border-radius: $radius;
|
||||
font-size: .75rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
height: $switch-view-height;
|
||||
margin: 0 auto 1rem;
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.switch-view-button {
|
||||
|
@ -201,7 +192,7 @@ watch(
|
|||
|
||||
// FIXME: this should be in notification and set via a prop
|
||||
.is-archived .notification.is-warning {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.project-title-print {
|
||||
|
@ -209,7 +200,7 @@ watch(
|
|||
font-size: 1.75rem;
|
||||
text-align: center;
|
||||
margin-bottom: .5rem;
|
||||
|
||||
|
||||
@media print {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -21,13 +21,16 @@ import {
|
|||
LABEL_FIELDS,
|
||||
} from '@/helpers/filters'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
import {createRandomID} from '@/helpers/randomId'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
projectId,
|
||||
inputLabel = undefined,
|
||||
} = defineProps<{
|
||||
modelValue: string,
|
||||
projectId?: number,
|
||||
inputLabel?: string,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'blur'])
|
||||
|
@ -38,6 +41,8 @@ const {
|
|||
height,
|
||||
} = useAutoHeightTextarea(filterQuery)
|
||||
|
||||
const id = ref(createRandomID())
|
||||
|
||||
watch(
|
||||
() => modelValue,
|
||||
() => {
|
||||
|
@ -246,7 +251,12 @@ const blurDebounced = useDebounceFn(() => emit('blur'), 500)
|
|||
|
||||
<template>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('filters.query.title') }}</label>
|
||||
<label
|
||||
class="label"
|
||||
:for="id"
|
||||
>
|
||||
{{ inputLabel ?? $t('filters.query.title') }}
|
||||
</label>
|
||||
<AutocompleteDropdown
|
||||
:options="autocompleteResults"
|
||||
@blur="filterInput?.blur()"
|
||||
|
@ -257,10 +267,10 @@ const blurDebounced = useDebounceFn(() => emit('blur'), 500)
|
|||
>
|
||||
<div class="control filter-input">
|
||||
<textarea
|
||||
:id
|
||||
ref="filterInput"
|
||||
v-model="filterQuery"
|
||||
autocomplete="off"
|
||||
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</Fancycheckbox>
|
||||
</div>
|
||||
|
||||
<FilterInputDocs/>
|
||||
<FilterInputDocs />
|
||||
|
||||
<template
|
||||
v-if="hasFooter"
|
||||
|
|
|
@ -47,6 +47,12 @@
|
|||
>
|
||||
{{ $t('menu.edit') }}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
:to="{ name: 'project.settings.views', params: { projectId: project.id } }"
|
||||
icon="eye"
|
||||
>
|
||||
{{ $t('menu.views') }}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
v-if="backgroundsEnabled"
|
||||
:to="{ name: 'project.settings.background', params: { projectId: project.id } }"
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
<!-- Vikunja is a to-do list application to facilitate your life. -->
|
||||
<!-- Copyright 2018-present Vikunja and contributors. All rights reserved. -->
|
||||
<!-- -->
|
||||
<!-- This program is free software: you can redistribute it and/or modify -->
|
||||
<!-- it under the terms of the GNU Affero General Public Licensee as published by -->
|
||||
<!-- the Free Software Foundation, either version 3 of the License, or -->
|
||||
<!-- (at your option) any later version. -->
|
||||
<!-- -->
|
||||
<!-- This program is distributed in the hope that it will be useful, -->
|
||||
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
|
||||
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
|
||||
<!-- GNU Affero General Public Licensee for more details. -->
|
||||
<!-- -->
|
||||
<!-- You should have received a copy of the GNU Affero General Public Licensee -->
|
||||
<!-- along with this program. If not, see <https://www.gnu.org/licenses/>. -->
|
||||
|
||||
<template>
|
||||
<ProjectWrapper
|
||||
class="project-gantt"
|
||||
:project-id="filters.projectId"
|
||||
view-name="gantt"
|
||||
:view
|
||||
>
|
||||
<template #header>
|
||||
<card :has-content="false">
|
||||
|
@ -87,15 +103,19 @@ import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
|||
import TaskForm from '@/components/tasks/TaskForm.vue'
|
||||
|
||||
import {createAsyncComponent} from '@/helpers/createAsyncComponent'
|
||||
import {useGanttFilters} from './helpers/useGanttFilters'
|
||||
import {useGanttFilters} from '../../../views/project/helpers/useGanttFilters'
|
||||
import {RIGHTS} from '@/constants/rights'
|
||||
|
||||
import type {DateISO} from '@/types/DateISO'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
type Options = Flatpickr.Options.Options
|
||||
|
||||
const props = defineProps<{route: RouteLocationNormalized}>()
|
||||
const props = defineProps<{
|
||||
route: RouteLocationNormalized
|
||||
viewId: IProjectView['id']
|
||||
}>()
|
||||
|
||||
const GanttChart = createAsyncComponent(() => import('@/components/tasks/GanttChart.vue'))
|
||||
|
||||
|
@ -111,7 +131,7 @@ const {
|
|||
isLoading,
|
||||
addTask,
|
||||
updateTask,
|
||||
} = useGanttFilters(route)
|
||||
} = useGanttFilters(route, props.viewId)
|
||||
|
||||
const DEFAULT_DATE_RANGE_DAYS = 7
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<ProjectWrapper
|
||||
class="project-kanban"
|
||||
:project-id="projectId"
|
||||
view-name="kanban"
|
||||
:view-id
|
||||
>
|
||||
<template #header>
|
||||
<div class="filter-container">
|
||||
|
@ -277,7 +277,6 @@ import {RIGHTS as Rights} from '@/constants/rights'
|
|||
import BucketModel from '@/models/bucket'
|
||||
|
||||
import type {IBucket} from '@/modelTypes/IBucket'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
@ -301,11 +300,17 @@ import {isSavedFilter} from '@/services/savedFilter'
|
|||
import {success} from '@/message'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import type {TaskFilterParams} from '@/services/taskCollection'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import TaskPositionService from '@/services/taskPosition'
|
||||
import TaskPositionModel from '@/models/taskPosition'
|
||||
import {i18n} from '@/i18n'
|
||||
|
||||
const {
|
||||
projectId = undefined,
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
projectId: number,
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
const DRAG_OPTIONS = {
|
||||
|
@ -325,6 +330,7 @@ const baseStore = useBaseStore()
|
|||
const kanbanStore = useKanbanStore()
|
||||
const taskStore = useTaskStore()
|
||||
const projectStore = useProjectStore()
|
||||
const taskPositionService = ref(new TaskPositionService())
|
||||
|
||||
const taskContainerRefs = ref<{ [id: IBucket['id']]: HTMLElement }>({})
|
||||
const bucketLimitInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
@ -363,7 +369,7 @@ const params = ref<TaskFilterParams>({
|
|||
const getTaskDraggableTaskComponentData = computed(() => (bucket: IBucket) => {
|
||||
return {
|
||||
ref: (el: HTMLElement) => setTaskContainerRef(bucket.id, el),
|
||||
onScroll: (event: Event) => handleTaskContainerScroll(bucket.id, bucket.projectId, event.target as HTMLElement),
|
||||
onScroll: (event: Event) => handleTaskContainerScroll(bucket.id, event.target as HTMLElement),
|
||||
type: 'transition-group',
|
||||
name: !drag.value ? 'move-card' : null,
|
||||
class: [
|
||||
|
@ -387,19 +393,20 @@ const project = computed(() => projectId ? projectStore.projects[projectId] : nu
|
|||
const buckets = computed(() => kanbanStore.buckets)
|
||||
const loading = computed(() => kanbanStore.isLoading)
|
||||
|
||||
const taskLoading = computed(() => taskStore.isLoading)
|
||||
const taskLoading = computed(() => taskStore.isLoading || taskPositionService.value.loading)
|
||||
|
||||
watch(
|
||||
() => ({
|
||||
params: params.value,
|
||||
projectId,
|
||||
viewId,
|
||||
}),
|
||||
({params}) => {
|
||||
if (projectId === undefined || Number(projectId) === 0) {
|
||||
return
|
||||
}
|
||||
collapsedBuckets.value = getCollapsedBucketState(projectId)
|
||||
kanbanStore.loadBucketsForProject({projectId, params})
|
||||
kanbanStore.loadBucketsForProject(projectId, viewId, params)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -412,7 +419,7 @@ function setTaskContainerRef(id: IBucket['id'], el: HTMLElement) {
|
|||
taskContainerRefs.value[id] = el
|
||||
}
|
||||
|
||||
function handleTaskContainerScroll(id: IBucket['id'], projectId: IProject['id'], el: HTMLElement) {
|
||||
function handleTaskContainerScroll(id: IBucket['id'], el: HTMLElement) {
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
|
@ -424,6 +431,7 @@ function handleTaskContainerScroll(id: IBucket['id'], projectId: IProject['id'],
|
|||
|
||||
kanbanStore.loadNextTasksForBucket(
|
||||
projectId,
|
||||
viewId,
|
||||
params.value,
|
||||
id,
|
||||
)
|
||||
|
@ -473,7 +481,7 @@ async function updateTaskPosition(e) {
|
|||
|
||||
const newTask = klona(task) // cloning the task to avoid pinia store manipulation
|
||||
newTask.bucketId = newBucket.id
|
||||
newTask.kanbanPosition = calculateItemPosition(
|
||||
const position = calculateItemPosition(
|
||||
taskBefore !== null ? taskBefore.kanbanPosition : null,
|
||||
taskAfter !== null ? taskAfter.kanbanPosition : null,
|
||||
)
|
||||
|
@ -483,6 +491,8 @@ async function updateTaskPosition(e) {
|
|||
) {
|
||||
newTask.done = project.value?.doneBucketId === newBucket.id
|
||||
}
|
||||
|
||||
let bucketHasChanged = false
|
||||
if (
|
||||
oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe.
|
||||
newBucket.id !== oldBucket.id
|
||||
|
@ -495,10 +505,20 @@ async function updateTaskPosition(e) {
|
|||
...newBucket,
|
||||
count: newBucket.count + 1,
|
||||
})
|
||||
bucketHasChanged = true
|
||||
}
|
||||
|
||||
try {
|
||||
await taskStore.update(newTask)
|
||||
const newPosition = new TaskPositionModel({
|
||||
position,
|
||||
projectViewId: viewId,
|
||||
taskId: newTask.id,
|
||||
})
|
||||
await taskPositionService.value.update(newPosition)
|
||||
|
||||
if(bucketHasChanged) {
|
||||
await taskStore.update(newTask)
|
||||
}
|
||||
|
||||
// Make sure the first and second task don't both get position 0 assigned
|
||||
if (newTaskIndex === 0 && taskAfter !== null && taskAfter.kanbanPosition === 0) {
|
||||
|
@ -556,6 +576,7 @@ async function createNewBucket() {
|
|||
await kanbanStore.createBucket(new BucketModel({
|
||||
title: newBucketTitle.value,
|
||||
projectId: project.value.id,
|
||||
projectViewId: viewId,
|
||||
}))
|
||||
newBucketTitle.value = ''
|
||||
}
|
||||
|
@ -575,6 +596,7 @@ async function deleteBucket() {
|
|||
bucket: new BucketModel({
|
||||
id: bucketToDelete.value,
|
||||
projectId: project.value.id,
|
||||
projectViewId: viewId,
|
||||
}),
|
||||
params: params.value,
|
||||
})
|
||||
|
@ -593,10 +615,19 @@ async function focusBucketTitle(e: Event) {
|
|||
}
|
||||
|
||||
async function saveBucketTitle(bucketId: IBucket['id'], bucketTitle: string) {
|
||||
await kanbanStore.updateBucketTitle({
|
||||
|
||||
const bucket = kanbanStore.getBucketById(bucketId)
|
||||
if (bucket?.title === bucketTitle) {
|
||||
bucketTitleEditable.value = false
|
||||
return
|
||||
}
|
||||
|
||||
await kanbanStore.updateBucket({
|
||||
id: bucketId,
|
||||
title: bucketTitle,
|
||||
projectId,
|
||||
})
|
||||
success({message: i18n.global.t('project.kanban.bucketTitleSavedSuccess')})
|
||||
bucketTitleEditable.value = false
|
||||
}
|
||||
|
||||
|
@ -616,6 +647,7 @@ function updateBucketPosition(e: { newIndex: number }) {
|
|||
|
||||
kanbanStore.updateBucket({
|
||||
id: bucket.id,
|
||||
projectId,
|
||||
position: calculateItemPosition(
|
||||
bucketBefore !== null ? bucketBefore.position : null,
|
||||
bucketAfter !== null ? bucketAfter.position : null,
|
||||
|
@ -630,6 +662,7 @@ async function saveBucketLimit(bucketId: IBucket['id'], limit: number) {
|
|||
|
||||
await kanbanStore.updateBucket({
|
||||
...kanbanStore.getBucketById(bucketId),
|
||||
projectId,
|
||||
limit,
|
||||
})
|
||||
success({message: t('project.kanban.bucketLimitSavedSuccess')})
|
|
@ -2,7 +2,7 @@
|
|||
<ProjectWrapper
|
||||
class="project-list"
|
||||
:project-id="projectId"
|
||||
view-name="project"
|
||||
:view-id
|
||||
>
|
||||
<template #header>
|
||||
<div class="filter-container">
|
||||
|
@ -114,14 +114,18 @@ import type {ITask} from '@/modelTypes/ITask'
|
|||
import {isSavedFilter} from '@/services/savedFilter'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import TaskPositionService from '@/services/taskPosition'
|
||||
import TaskPositionModel from '@/models/taskPosition'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
const ctaVisible = ref(false)
|
||||
|
@ -140,7 +144,9 @@ const {
|
|||
loadTasks,
|
||||
params,
|
||||
sortByParam,
|
||||
} = useTaskList(() => projectId, {position: 'asc'})
|
||||
} = useTaskList(() => projectId, () => viewId, {position: 'asc'})
|
||||
|
||||
const taskPositionService = ref(new TaskPositionService())
|
||||
|
||||
const tasks = ref<ITask[]>([])
|
||||
watch(
|
||||
|
@ -182,7 +188,6 @@ const firstNewPosition = computed(() => {
|
|||
return calculateItemPosition(null, tasks.value[0].position)
|
||||
})
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const baseStore = useBaseStore()
|
||||
const project = computed(() => baseStore.currentProject)
|
||||
|
||||
|
@ -231,13 +236,17 @@ async function saveTaskPosition(e) {
|
|||
const taskBefore = tasks.value[e.newIndex - 1] ?? null
|
||||
const taskAfter = tasks.value[e.newIndex + 1] ?? null
|
||||
|
||||
const newTask = {
|
||||
...task,
|
||||
position: calculateItemPosition(taskBefore !== null ? taskBefore.position : null, taskAfter !== null ? taskAfter.position : null),
|
||||
}
|
||||
const position = calculateItemPosition(taskBefore !== null ? taskBefore.position : null, taskAfter !== null ? taskAfter.position : null)
|
||||
|
||||
const updatedTask = await taskStore.update(newTask)
|
||||
tasks.value[e.newIndex] = updatedTask
|
||||
await taskPositionService.value.update(new TaskPositionModel({
|
||||
position,
|
||||
projectViewId: viewId,
|
||||
taskId: task.id,
|
||||
}))
|
||||
tasks.value[e.newIndex] = {
|
||||
...task,
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
function prepareFiltersAndLoadTasks() {
|
|
@ -2,7 +2,7 @@
|
|||
<ProjectWrapper
|
||||
class="project-table"
|
||||
:project-id="projectId"
|
||||
view-name="table"
|
||||
:view-id
|
||||
>
|
||||
<template #header>
|
||||
<div class="filter-container">
|
||||
|
@ -289,11 +289,14 @@ import {useTaskList} from '@/composables/useTaskList'
|
|||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import AssigneeList from '@/components/tasks/partials/assigneeList.vue'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
|
||||
const ACTIVE_COLUMNS_DEFAULT = {
|
||||
|
@ -320,7 +323,7 @@ const SORT_BY_DEFAULT: SortBy = {
|
|||
const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT})
|
||||
const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT})
|
||||
|
||||
const taskList = useTaskList(() => projectId, sortBy.value)
|
||||
const taskList = useTaskList(() => projectId, () => viewId, sortBy.value)
|
||||
|
||||
const {
|
||||
loading,
|
|
@ -0,0 +1,180 @@
|
|||
<script setup lang="ts">
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import XButton from '@/components/input/button.vue'
|
||||
import FilterInput from '@/components/project/partials/FilterInput.vue'
|
||||
import {ref} from 'vue'
|
||||
|
||||
const model = defineModel<IProjectView>()
|
||||
const titleValid = ref(true)
|
||||
function validateTitle() {
|
||||
titleValid.value = model.value.title !== ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form>
|
||||
<div class="field">
|
||||
<label
|
||||
class="label"
|
||||
for="title"
|
||||
>
|
||||
{{ $t('project.views.title') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
id="title"
|
||||
v-model="model.title"
|
||||
v-focus
|
||||
class="input"
|
||||
:placeholder="$t('project.share.links.namePlaceholder')"
|
||||
@blur="validateTitle"
|
||||
>
|
||||
</div>
|
||||
<p
|
||||
v-if="!titleValid"
|
||||
class="help is-danger"
|
||||
>
|
||||
{{ $t('project.views.titleRequired') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label
|
||||
class="label"
|
||||
for="kind"
|
||||
>
|
||||
{{ $t('project.views.kind') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select
|
||||
id="kind"
|
||||
v-model="model.viewKind"
|
||||
>
|
||||
<option value="list">
|
||||
{{ $t('project.list.title') }}
|
||||
</option>
|
||||
<option value="gantt">
|
||||
{{ $t('project.gantt.title') }}
|
||||
</option>
|
||||
<option value="table">
|
||||
{{ $t('project.table.title') }}
|
||||
</option>
|
||||
<option value="kanban">
|
||||
{{ $t('project.kanban.title') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FilterInput
|
||||
v-model="model.filter"
|
||||
:input-label="$t('project.views.filter')"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="model.viewKind === 'kanban'"
|
||||
class="field"
|
||||
>
|
||||
<label
|
||||
class="label"
|
||||
for="configMode"
|
||||
>
|
||||
{{ $t('project.views.bucketConfigMode') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select
|
||||
id="configMode"
|
||||
v-model="model.bucketConfigurationMode"
|
||||
>
|
||||
<option value="manual">
|
||||
{{ $t('project.views.bucketConfigManual') }}
|
||||
</option>
|
||||
<option value="filter">
|
||||
{{ $t('project.views.filter') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="model.viewKind === 'kanban' && model.bucketConfigurationMode === 'filter'"
|
||||
class="field"
|
||||
>
|
||||
<label class="label">
|
||||
{{ $t('project.views.bucketConfig') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<div
|
||||
v-for="(b, index) in model.bucketConfiguration"
|
||||
:key="'bucket_'+index"
|
||||
class="filter-bucket"
|
||||
>
|
||||
<button
|
||||
class="is-danger"
|
||||
@click.prevent="() => model.bucketConfiguration.splice(index, 1)"
|
||||
>
|
||||
<icon icon="trash-alt" />
|
||||
</button>
|
||||
<div class="filter-bucket-form">
|
||||
<div class="field">
|
||||
<label
|
||||
class="label"
|
||||
:for="'bucket_'+index+'_title'"
|
||||
>
|
||||
{{ $t('project.views.title') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
:id="'bucket_'+index+'_title'"
|
||||
v-model="model.bucketConfiguration[index].title"
|
||||
class="input"
|
||||
:placeholder="$t('project.share.links.namePlaceholder')"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FilterInput
|
||||
v-model="model.bucketConfiguration[index].filter"
|
||||
:input-label="$t('project.views.filter')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-flex is-justify-content-end">
|
||||
<XButton
|
||||
variant="secondary"
|
||||
icon="plus"
|
||||
@click="() => model.bucketConfiguration.push({title: '', filter: ''})"
|
||||
>
|
||||
{{ $t('project.kanban.addBucket') }}
|
||||
</XButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.filter-bucket {
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--danger);
|
||||
padding-right: .75rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-form {
|
||||
margin-bottom: .5rem;
|
||||
padding: .5rem;
|
||||
border: 1px solid var(--grey-200);
|
||||
border-radius: $radius;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -173,11 +173,11 @@
|
|||
<div class="select">
|
||||
<select v-model="selectedView[s.id]">
|
||||
<option
|
||||
v-for="(title, key) in availableViews"
|
||||
:key="key"
|
||||
:value="key"
|
||||
v-for="(view) in availableViews"
|
||||
:key="view.id"
|
||||
:value="view.id"
|
||||
>
|
||||
{{ title }}
|
||||
{{ view.title }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -230,9 +230,9 @@ import LinkShareService from '@/services/linkShare'
|
|||
import {useCopyToClipboard} from '@/composables/useCopyToClipboard'
|
||||
import {success} from '@/message'
|
||||
import {getDisplayName} from '@/models/user'
|
||||
import type {ProjectView} from '@/types/ProjectView'
|
||||
import {PROJECT_VIEWS} from '@/types/ProjectView'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
const props = defineProps({
|
||||
projectId: {
|
||||
|
@ -252,17 +252,13 @@ const showDeleteModal = ref(false)
|
|||
const linkIdToDelete = ref(0)
|
||||
const showNewForm = ref(false)
|
||||
|
||||
type SelectedViewMapper = Record<IProject['id'], ProjectView>
|
||||
type SelectedViewMapper = Record<IProject['id'], IProjectView['id']>
|
||||
|
||||
const selectedView = ref<SelectedViewMapper>({})
|
||||
|
||||
const availableViews = computed<Record<ProjectView, string>>(() => ({
|
||||
list: t('project.list.title'),
|
||||
gantt: t('project.gantt.title'),
|
||||
table: t('project.table.title'),
|
||||
kanban: t('project.kanban.title'),
|
||||
}))
|
||||
const projectStore = useProjectStore()
|
||||
|
||||
const availableViews = computed<IProjectView[]>(() => projectStore.projects[props.projectId]?.views || [])
|
||||
const copy = useCopyToClipboard()
|
||||
watch(
|
||||
() => props.projectId,
|
||||
|
@ -281,7 +277,7 @@ async function load(projectId: IProject['id']) {
|
|||
|
||||
const links = await linkShareService.getAll({projectId})
|
||||
links.forEach((l: ILinkShare) => {
|
||||
selectedView.value[l.id] = 'list'
|
||||
selectedView.value[l.id] = availableViews.value[0].id
|
||||
})
|
||||
linkShares.value = links
|
||||
}
|
||||
|
@ -315,8 +311,8 @@ async function remove(projectId: IProject['id']) {
|
|||
}
|
||||
}
|
||||
|
||||
function getShareLink(hash: string, view: ProjectView = PROJECT_VIEWS.LIST) {
|
||||
return frontendUrl.value + 'share/' + hash + '/auth?view=' + view
|
||||
function getShareLink(hash: string, viewId: IProjectView['id']) {
|
||||
return frontendUrl.value + 'share/' + hash + '/auth?view=' + viewId
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, watch} from 'vue'
|
||||
import {ref, computed, watch, onBeforeUnmount} from 'vue'
|
||||
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
import Editor from '@/components/input/AsyncEditor'
|
||||
|
@ -88,6 +88,12 @@ async function saveWithDelay() {
|
|||
}, 5000)
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (changeTimeout.value !== null) {
|
||||
clearTimeout(changeTimeout.value)
|
||||
}
|
||||
})
|
||||
|
||||
async function save() {
|
||||
if (changeTimeout.value !== null) {
|
||||
clearTimeout(changeTimeout.value)
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
<ReminderPeriod
|
||||
v-if="activeForm === 'relative'"
|
||||
v-model="reminder"
|
||||
@update:modelValue="updateDataAndMaybeClose(close)"
|
||||
/>
|
||||
|
||||
<DatepickerInline
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<router-link
|
||||
v-if="showProject && typeof project !== 'undefined'"
|
||||
v-tooltip="$t('task.detail.belongsToProject', {project: project.title})"
|
||||
:to="{ name: 'project.list', params: { projectId: task.projectId } }"
|
||||
:to="{ name: 'project.index', params: { projectId: task.projectId } }"
|
||||
class="task-project mr-1"
|
||||
:class="{'mr-2': task.hexColor !== ''}"
|
||||
>
|
||||
|
@ -136,7 +136,7 @@
|
|||
<router-link
|
||||
v-if="showProjectSeparately"
|
||||
v-tooltip="$t('task.detail.belongsToProject', {project: project.title})"
|
||||
:to="{ name: 'project.list', params: { projectId: task.projectId } }"
|
||||
:to="{ name: 'project.index', params: { projectId: task.projectId } }"
|
||||
class="task-project"
|
||||
>
|
||||
{{ project.title }}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import {computed, shallowRef, watchEffect, h, type VNode} from 'vue'
|
||||
import {computed, h, shallowRef, type VNode, watchEffect} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
|
||||
export function useRouteWithModal() {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const backdropView = computed(() => route.fullPath && window.history.state.backdropView)
|
||||
const baseStore = useBaseStore()
|
||||
const projectStore = useProjectStore()
|
||||
|
||||
const routeWithModal = computed(() => {
|
||||
return backdropView.value
|
||||
|
@ -29,7 +31,7 @@ export function useRouteWithModal() {
|
|||
if (routePropsOption === true) {
|
||||
routeProps = route.params
|
||||
} else {
|
||||
if(typeof routePropsOption === 'function') {
|
||||
if (typeof routePropsOption === 'function') {
|
||||
routeProps = routePropsOption(route)
|
||||
} else {
|
||||
routeProps = routePropsOption
|
||||
|
@ -52,7 +54,7 @@ export function useRouteWithModal() {
|
|||
}
|
||||
currentModal.value = h(component, routeProps)
|
||||
})
|
||||
|
||||
|
||||
const historyState = computed(() => route.fullPath && window.history.state)
|
||||
|
||||
function closeModal() {
|
||||
|
@ -60,12 +62,23 @@ export function useRouteWithModal() {
|
|||
// If the current project was changed because the user moved the currently opened task while coming from kanban,
|
||||
// we need to reflect that change in the route when they close the task modal.
|
||||
// The last route is only available as resolved string, therefore we need to use a regex for matching here
|
||||
const kanbanRouteMatch = new RegExp('\\/projects\\/\\d+\\/kanban', 'g')
|
||||
const kanbanRouter = {name: 'project.kanban', params: {projectId: baseStore.currentProject?.id}}
|
||||
if (kanbanRouteMatch.test(historyState.value.back)
|
||||
&& baseStore.currentProject
|
||||
&& historyState.value.back !== router.resolve(kanbanRouter).fullPath) {
|
||||
router.push(kanbanRouter)
|
||||
const routeMatch = new RegExp('\\/projects\\/\\d+\\/(\\d+)', 'g')
|
||||
const match = routeMatch.exec(historyState.value.back)
|
||||
if (match !== null && baseStore.currentProject) {
|
||||
let viewId: string | number = match[1]
|
||||
|
||||
if (!viewId) {
|
||||
viewId = projectStore.projects[baseStore.currentProject?.id].views[0]?.id
|
||||
}
|
||||
|
||||
const newRoute = {
|
||||
name: 'project.view',
|
||||
params: {
|
||||
projectId: baseStore.currentProject?.id,
|
||||
viewId,
|
||||
},
|
||||
}
|
||||
router.push(newRoute)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import type {ITask} from '@/modelTypes/ITask'
|
|||
import {error} from '@/message'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
export type Order = 'asc' | 'desc' | 'none'
|
||||
|
||||
|
@ -54,9 +55,14 @@ const SORT_BY_DEFAULT: SortBy = {
|
|||
/**
|
||||
* This mixin provides a base set of methods and properties to get tasks.
|
||||
*/
|
||||
export function useTaskList(projectIdGetter: ComputedGetter<IProject['id']>, sortByDefault: SortBy = SORT_BY_DEFAULT) {
|
||||
export function useTaskList(
|
||||
projectIdGetter: ComputedGetter<IProject['id']>,
|
||||
projectViewIdGetter: ComputedGetter<IProjectView['id']>,
|
||||
sortByDefault: SortBy = SORT_BY_DEFAULT,
|
||||
) {
|
||||
|
||||
const projectId = computed(() => projectIdGetter())
|
||||
const projectViewId = computed(() => projectViewIdGetter())
|
||||
|
||||
const params = ref<TaskFilterParams>({...getDefaultTaskFilterParams()})
|
||||
|
||||
|
@ -87,7 +93,10 @@ export function useTaskList(projectIdGetter: ComputedGetter<IProject['id']>, sor
|
|||
|
||||
const getAllTasksParams = computed(() => {
|
||||
return [
|
||||
{projectId: projectId.value},
|
||||
{
|
||||
projectId: projectId.value,
|
||||
viewId: projectViewId.value,
|
||||
},
|
||||
{
|
||||
...allParams.value,
|
||||
filter_timezone: authStore.settings.timezone,
|
||||
|
|
|
@ -1,64 +1,17 @@
|
|||
import type { RouteRecordName } from 'vue-router'
|
||||
import router from '@/router'
|
||||
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
export type ProjectRouteName = Extract<RouteRecordName, string>
|
||||
export type ProjectViewSettings = Record<
|
||||
IProject['id'],
|
||||
Extract<RouteRecordName, ProjectRouteName>
|
||||
>
|
||||
export type ProjectViewSettings = Record<IProject['id'], number>
|
||||
|
||||
const SETTINGS_KEY_PROJECT_VIEW = 'projectView'
|
||||
|
||||
// TODO: remove migration when releasing 1.0
|
||||
type ListViewSettings = ProjectViewSettings
|
||||
const SETTINGS_KEY_DEPRECATED_LIST_VIEW = 'listView'
|
||||
function migrateStoredProjectRouteSettings() {
|
||||
try {
|
||||
const listViewSettingsString = localStorage.getItem(SETTINGS_KEY_DEPRECATED_LIST_VIEW)
|
||||
if (listViewSettingsString === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// A) the first version stored one setting for all lists in a string
|
||||
if (listViewSettingsString.startsWith('list.')) {
|
||||
const projectView = listViewSettingsString.replace('list.', 'project.')
|
||||
|
||||
if (!router.hasRoute(projectView)) {
|
||||
return
|
||||
}
|
||||
return projectView as RouteRecordName
|
||||
}
|
||||
|
||||
// B) the last version used a 'list.' prefix
|
||||
const listViewSettings: ListViewSettings = JSON.parse(listViewSettingsString)
|
||||
|
||||
const projectViewSettingEntries = Object.entries(listViewSettings).map(([id, value]) => {
|
||||
return [id, value.replace('list.', 'project.')]
|
||||
})
|
||||
const projectViewSettings = Object.fromEntries(projectViewSettingEntries)
|
||||
|
||||
localStorage.setItem(SETTINGS_KEY_PROJECT_VIEW, JSON.stringify(projectViewSettings))
|
||||
} catch(e) {
|
||||
//
|
||||
} finally {
|
||||
localStorage.removeItem(SETTINGS_KEY_DEPRECATED_LIST_VIEW)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current project view to local storage
|
||||
*/
|
||||
export function saveProjectView(projectId: IProject['id'], routeName: string) {
|
||||
if (routeName.includes('settings.')) {
|
||||
export function saveProjectView(projectId: IProject['id'], viewId: number) {
|
||||
if (!projectId || !viewId) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!projectId) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// We use local storage and not the store here to make it persistent across reloads.
|
||||
const savedProjectView = localStorage.getItem(SETTINGS_KEY_PROJECT_VIEW)
|
||||
let savedProjectViewSettings: ProjectViewSettings | false = false
|
||||
|
@ -71,30 +24,19 @@ export function saveProjectView(projectId: IProject['id'], routeName: string) {
|
|||
projectViewSettings = savedProjectViewSettings
|
||||
}
|
||||
|
||||
projectViewSettings[projectId] = routeName
|
||||
projectViewSettings[projectId] = viewId
|
||||
localStorage.setItem(SETTINGS_KEY_PROJECT_VIEW, JSON.stringify(projectViewSettings))
|
||||
}
|
||||
|
||||
export const getProjectView = (projectId: IProject['id']) => {
|
||||
// TODO: remove migration when releasing 1.0
|
||||
const migratedProjectView = migrateStoredProjectRouteSettings()
|
||||
|
||||
if (migratedProjectView !== undefined && router.hasRoute(migratedProjectView)) {
|
||||
return migratedProjectView
|
||||
export function getProjectViewId(projectId: IProject['id']): number {
|
||||
const projectViewSettingsString = localStorage.getItem(SETTINGS_KEY_PROJECT_VIEW)
|
||||
if (!projectViewSettingsString) {
|
||||
return 0
|
||||
}
|
||||
|
||||
try {
|
||||
const projectViewSettingsString = localStorage.getItem(SETTINGS_KEY_PROJECT_VIEW)
|
||||
if (!projectViewSettingsString) {
|
||||
throw new Error()
|
||||
}
|
||||
|
||||
const projectViewSettings = JSON.parse(projectViewSettingsString) as ProjectViewSettings
|
||||
if (!router.hasRoute(projectViewSettings[projectId])) {
|
||||
throw new Error()
|
||||
}
|
||||
return projectViewSettings[projectId]
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
const projectViewSettings = JSON.parse(projectViewSettingsString) as ProjectViewSettings
|
||||
if (isNaN(projectViewSettings[projectId])) {
|
||||
return 0
|
||||
}
|
||||
return projectViewSettings[projectId]
|
||||
}
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "مشروع جديد",
|
||||
"createProject": "إنشاء مشروع",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "رابط Vikunja",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Tajný klíč",
|
||||
"secretHint": "Pokud je zadáno, všechny požadavky na cílovou adresu URL webhooku budou podepsány pomocí HMAC.",
|
||||
"secretDocs": "Další podrobnosti o používání tajných klíčů naleznete v dokumentaci."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Nový projekt",
|
||||
"createProject": "Vytvořit projekt",
|
||||
"cantArchiveIsDefault": "Nemůžete archivovat svůj výchozí projekt.",
|
||||
"cantDeleteIsDefault": "Nemůžete smazat svůj výchozí projekt."
|
||||
"cantDeleteIsDefault": "Nemůžete smazat svůj výchozí projekt.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Schlüssel",
|
||||
"secretHint": "Wenn angegeben, werden alle Anfragen an die Webhook Ziel-URL mit HMAC signiert.",
|
||||
"secretDocs": "In der Dokumentation findest du weitere Informationen zum Umgang mit Schlüsseln."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Neues Projekt",
|
||||
"createProject": "Projekt erstellen",
|
||||
"cantArchiveIsDefault": "Du kannst dieses Projekt nicht archivieren, da es dein Standardprojekt ist.",
|
||||
"cantDeleteIsDefault": "Du kannst dieses Projekt nicht löschen, da es dein Standardprojekt ist."
|
||||
"cantDeleteIsDefault": "Du kannst dieses Projekt nicht löschen, da es dein Standardprojekt ist.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja-URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Schlüssel",
|
||||
"secretHint": "Wenn angegeben, werden alle Anfragen an die Webhook Ziel-URL mit HMAC signiert.",
|
||||
"secretDocs": "In der Dokumentation findest du weitere Informationen zum Umgang mit Schlüsseln."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Neues Projekt",
|
||||
"createProject": "Projekt erstellen",
|
||||
"cantArchiveIsDefault": "Du kannst dieses Projekt nicht archivieren, da es dein Standardprojekt ist.",
|
||||
"cantDeleteIsDefault": "Du kannst dieses Projekt nicht löschen, da es dein Standardprojekt ist."
|
||||
"cantDeleteIsDefault": "Du kannst dieses Projekt nicht löschen, da es dein Standardprojekt ist.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1049,7 +1065,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Nuevo proyecto",
|
||||
"createProject": "Crear proyecto",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "URL de Vikunja",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Nouveau projet",
|
||||
"createProject": "Créer un projet",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "URL Vikunja",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"welcomeEvening": "Jó estét {username}!",
|
||||
"lastViewed": "Utoljára megtekintve",
|
||||
"addToHomeScreen": "Adja hozzá ezt az alkalmazást a kezdőképernyőhöz a gyorsabb hozzáférés és a jobb élmény érdekében.",
|
||||
"goToOverview": "Go to overview",
|
||||
"goToOverview": "Tovább az áttekintéshez",
|
||||
"project": {
|
||||
"importText": "Importálja projektjeit és feladatait más szolgáltatásokból a Vikunjába:",
|
||||
"import": "Importálja adatait a Vikunjába"
|
||||
|
@ -57,11 +57,11 @@
|
|||
"logout": "Kijelentkezés",
|
||||
"emailInvalid": "Kérjük, adjon meg egy valós email címet!",
|
||||
"usernameRequired": "Kérjük adjon meg egy felhasználónevet.",
|
||||
"usernameMustNotContainSpace": "The username must not contain spaces.",
|
||||
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
|
||||
"usernameMustNotContainSpace": "A felhasználónév nem tartalmazhat szóközt.",
|
||||
"usernameMustNotLookLikeUrl": "A felhasználónév nem nézhet ki URL-nek.",
|
||||
"passwordRequired": "Kérjük, adjon meg új jelszót.",
|
||||
"passwordNotMin": "Password must have at least 8 characters.",
|
||||
"passwordNotMax": "Password must have at most 250 characters.",
|
||||
"passwordNotMin": "A jelszónak legalább 8 karakterből kell állnia.",
|
||||
"passwordNotMax": "A jelszó legfeljebb 250 karakterből állhat.",
|
||||
"showPassword": "Jelszó megjelenítése",
|
||||
"hidePassword": "A jelszó elrejtése",
|
||||
"noAccountYet": "Még nincs fiókja?",
|
||||
|
@ -164,7 +164,7 @@
|
|||
"expired": "Ez a token lejárt {ago}.",
|
||||
"tokenCreatedSuccess": "Íme az új API tokenje: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Tárolja el biztonságos helyen, többé nem fogja látni!",
|
||||
"selectAll": "Select all",
|
||||
"selectAll": "Összes kijelölése",
|
||||
"delete": {
|
||||
"header": "Token törlése",
|
||||
"text1": "Biztos benne, hogy törölni akarja ezt a tokent \"{token}\"?",
|
||||
|
@ -248,7 +248,7 @@
|
|||
"text2": "Ez magában foglalja az összes feladatot és NEM VISSZAVONHATÓ!",
|
||||
"success": "A projekt sikeresen törölve.",
|
||||
"tasksToDelete": "Ezzel visszavonhatatlanul eltávolítjuk kb. {count} feladatát.",
|
||||
"tasksAndChildProjectsToDelete": "This will irrevocably remove approx. {tasks} tasks and {projects} projects.",
|
||||
"tasksAndChildProjectsToDelete": "Ezzel visszavonhatatlanul eltávolítunk kb. {tasks} feladatot és {projects} projektet.",
|
||||
"noTasksToDelete": "Ez a projekt nem tartalmaz feladatokat, biztonságosan törölhető."
|
||||
},
|
||||
"duplicate": {
|
||||
|
@ -265,7 +265,7 @@
|
|||
"identifier": "Projektazonosító",
|
||||
"identifierPlaceholder": "Írja be a projekt projektazonosítót...",
|
||||
"description": "Leírás",
|
||||
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
|
||||
"descriptionPlaceholder": "Adja meg a projekt leírását, a további lehetőségekért nyomja meg a „/” gombot…",
|
||||
"color": "Szín",
|
||||
"success": "A projekt sikeresen frissítve."
|
||||
},
|
||||
|
@ -368,30 +368,46 @@
|
|||
}
|
||||
},
|
||||
"webhooks": {
|
||||
"title": "Webhooks",
|
||||
"targetUrl": "Target URL",
|
||||
"targetUrlInvalid": "Please provide a valid URL.",
|
||||
"events": "Events",
|
||||
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
|
||||
"mustSelectEvents": "You must select at least one event.",
|
||||
"delete": "Delete this webhook",
|
||||
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
|
||||
"deleteSuccess": "The webhook was successfully deleted.",
|
||||
"create": "Create webhook",
|
||||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
"title": "Webhookok",
|
||||
"targetUrl": "Cél URL",
|
||||
"targetUrlInvalid": "Kérjük adjon meg egy érvényes URL-t.",
|
||||
"events": "Események",
|
||||
"eventsHint": "Válassza ki az összes olyan eseményt, amelyhez ez a webhook frissítéseket kap (az aktuális projekten belül).",
|
||||
"mustSelectEvents": "Legalább egy eseményt ki kell választania.",
|
||||
"delete": "Webhook törlése",
|
||||
"deleteText": "Biztosan törli ezt a webhook-ot? A külső célpontok többé nem kapnak értesítést az eseményeiről.",
|
||||
"deleteSuccess": "A webhook törlése sikeresen megtörtént.",
|
||||
"create": "Webhook létrehozása",
|
||||
"secret": "Kulcs",
|
||||
"secretHint": "Amennyiben meg van adva, a webhook cél URL-címére irányuló összes kérés HMAC használatával lesz aláírva.",
|
||||
"secretDocs": "Tekintse meg a dokumentációt a titkok használatának további részleteiért."
|
||||
},
|
||||
"views": {
|
||||
"header": "Nézetek szerkesztése",
|
||||
"title": "Cím",
|
||||
"actions": "Műveletek",
|
||||
"kind": "Fajta",
|
||||
"bucketConfigMode": "Vödör konfigurációs mód",
|
||||
"bucketConfig": "Vödör konfiguráció",
|
||||
"bucketConfigManual": "Manuális",
|
||||
"filter": "Szűrő",
|
||||
"create": "Nézet létrehozása",
|
||||
"createSuccess": "A nézet létrehozása sikeres volt.",
|
||||
"titleRequired": "Kérjük, adjon meg egy címet.",
|
||||
"delete": "Törölje ezt a nézetet",
|
||||
"deleteText": "Biztosan eltávolítja ezt a nézetet? A továbbiakban nem lesz használható a projektben szereplő feladatok megtekintésére. Ez a művelet nem töröl semmilyen feladatot. Ezt nem lehet visszacsinálni!",
|
||||
"deleteSuccess": "A nézet sikeresen törölve"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"title": "Szűrők",
|
||||
"clear": "Szűrők törlése",
|
||||
"showResults": "Show results",
|
||||
"showResults": "Eredmények megjelenítése",
|
||||
"attributes": {
|
||||
"title": "Cím",
|
||||
"titlePlaceholder": "A mentett szűrő címe ide kerül…",
|
||||
"description": "Leírás",
|
||||
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
|
||||
"descriptionPlaceholder": "Adjon hozzá leírást ehhez a szűrőhöz, nyomja meg a „/” gombot a további lehetőségekért…",
|
||||
"includeNulls": "Tartalmazza az olyan feladatokat, amelyeknek nincs beállított értéke",
|
||||
"requireAll": "A feladat megjelenítéséhez minden szűrőnek igaznak kell lennie",
|
||||
"showDoneTasks": "Elkészült feladatok megjelenítése",
|
||||
|
@ -419,48 +435,48 @@
|
|||
"success": "A szűrőt sikeresen mentette."
|
||||
},
|
||||
"query": {
|
||||
"title": "Query",
|
||||
"placeholder": "Type a search or filter query…",
|
||||
"title": "Lekérdezés",
|
||||
"placeholder": "Írjon be egy keresési, vagy szűrési lekérdezést…",
|
||||
"help": {
|
||||
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
|
||||
"link": "How does this work?",
|
||||
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
|
||||
"intro": "A feladatok szűréséhez az SQL-hez hasonló lekérdezési szintaxist használhat. A szűréshez rendelkezésre álló mezők a következők:",
|
||||
"link": "Hogyan működik?",
|
||||
"canUseDatemath": "A relatív dátumok beállításához a matematikai dátumokat is használhat. További információért kattintson a dátum értékére a lekérdezésben.",
|
||||
"fields": {
|
||||
"done": "Whether the task is completed or not",
|
||||
"priority": "The priority level of the task (1-5)",
|
||||
"percentDone": "The percentage of completion for the task (0-100)",
|
||||
"dueDate": "The due date of the task",
|
||||
"startDate": "The start date of the task",
|
||||
"endDate": "The end date of the task",
|
||||
"doneAt": "The date and time when the task was completed",
|
||||
"assignees": "The assignees of the task",
|
||||
"labels": "The labels associated with the task",
|
||||
"project": "The project the task belongs to (only available for saved filters, not on a project level)"
|
||||
"done": "Akár befejeződött a feladat, akár nem",
|
||||
"priority": "A feladat prioritási szintje (1-5)",
|
||||
"percentDone": "A feladat teljesítésének százalékos aránya (0-100)",
|
||||
"dueDate": "A feladat teljesítésének határideje",
|
||||
"startDate": "A feladat kezdési dátuma",
|
||||
"endDate": "A feladat befejezési dátuma",
|
||||
"doneAt": "A feladat befejezésének dátuma és időpontja",
|
||||
"assignees": "A feladattal megbízottak",
|
||||
"labels": "A feladathoz társított címkék",
|
||||
"project": "A projekt, amelyhez a feladat tartozik (csak a mentett szűrőkhöz érhető el, projekt szinten nem)"
|
||||
},
|
||||
"operators": {
|
||||
"intro": "The available operators for filtering include:",
|
||||
"notEqual": "Not equal to",
|
||||
"equal": "Equal to",
|
||||
"greaterThan": "Greater than",
|
||||
"greaterThanOrEqual": "Greater than or equal to",
|
||||
"lessThan": "Less than",
|
||||
"lessThanOrEqual": "Less than or equal to",
|
||||
"like": "Matches a pattern (using wildcard %)",
|
||||
"in": "Matches any value in a comma-seperated list of values"
|
||||
"intro": "A szűréshez elérhető operátorok a következők:",
|
||||
"notEqual": "Nem egyenlő",
|
||||
"equal": "Egyenlő",
|
||||
"greaterThan": "Nagyobb, mint",
|
||||
"greaterThanOrEqual": "Nagyobb, vagy egyenlő",
|
||||
"lessThan": "Kevesebb, mint",
|
||||
"lessThanOrEqual": "Kevesebb, vagy egyenlő mint",
|
||||
"like": "Megfelel egy mintának (helyettesítő karakterrel %)",
|
||||
"in": "A vesszővel elválasztott értéklistában szereplő bármely értéknek megfelel"
|
||||
},
|
||||
"logicalOperators": {
|
||||
"intro": "To combine multiple conditions, you can use the following logical operators:",
|
||||
"and": "AND operator, matches if all conditions are true",
|
||||
"or": "OR operator, matches if any of the conditions are true",
|
||||
"parentheses": "Parentheses for grouping conditions"
|
||||
"intro": "Több feltétel kombinálásához a következő logikai operátorokat használhatja:",
|
||||
"and": "ÉS operátor, akkor egyezik, ha minden feltétel igaz",
|
||||
"or": "VAGY operátor, akkor felel meg, ha valamelyik feltétel igaz",
|
||||
"parentheses": "Zárójelek a csoportosítási feltételekhez"
|
||||
},
|
||||
"examples": {
|
||||
"intro": "Here are some examples of filter queries:",
|
||||
"priorityEqual": "Matches tasks with priority level 4",
|
||||
"dueDatePast": "Matches tasks with a due date in the past",
|
||||
"undoneHighPriority": "Matches undone tasks with priority level 3 or higher",
|
||||
"assigneesIn": "Matches tasks assigned to either \"user1\" or \"user2\"",
|
||||
"priorityOneOrTwoPastDue": "Matches tasks with priority level 1 or 2 and a due date in the past"
|
||||
"intro": "Íme néhány példa a szűrőlekérdezésekre:",
|
||||
"priorityEqual": "4. prioritási szintű feladatokat egyezteti",
|
||||
"dueDatePast": "A múltbeli esedékességgel rendelkező feladatokat egyezteti",
|
||||
"undoneHighPriority": "Megfelel a 3-as, vagy magasabb prioritási szintű még nem elvégzett feladatoknak",
|
||||
"assigneesIn": "Megfelel a \"felhasználó1\", vagy a \"felhasználó2\"-nek rendelt feladatoknak",
|
||||
"priorityOneOrTwoPastDue": "Megfelel az 1-es, vagy 2-es prioritási szinttel és a múltbeli esedékességgel rendelkező feladatoknak"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -479,8 +495,8 @@
|
|||
"confirm": "Biztos vagyok benne, kezdje el a migrációt most!",
|
||||
"importUpload": "Ha adatokat szeretne importálni a(z) {name} webhelyről a Vikunjába, kattintson az alábbi gombra a fájl kiválasztásához.",
|
||||
"upload": "Fájl feltöltése",
|
||||
"migrationStartedWillReciveEmail": "Vikunja will now import your lists/projects, tasks, notes, reminders and files from {service}. As this will take a while, we will send you an email once done. You can close this window now.",
|
||||
"migrationInProgress": "A migration is currently in progress. Please wait until it is done."
|
||||
"migrationStartedWillReciveEmail": "A Vikunja mostantól importálja listáit/projektjeit, feladatait, jegyzeteit, emlékeztetőit és fájljait a {service} szolgáltatásból. Mivel ez eltart egy ideig, amint elkészült, küldünk Önnek egy e-mailt. Most bezárhatja ezt az ablakot.",
|
||||
"migrationInProgress": "A migráció folyamatban van. Kérjük várjon, amíg elkészül."
|
||||
},
|
||||
"label": {
|
||||
"title": "Címkék",
|
||||
|
@ -551,7 +567,7 @@
|
|||
"custom": "Egyéni",
|
||||
"id": "Azonosító",
|
||||
"created": "Létrehozva ekkor:",
|
||||
"createdBy": "Created by {0}",
|
||||
"createdBy": "Létrehozta: {0}",
|
||||
"actions": "Műveletek",
|
||||
"cannotBeUndone": "Ezt nem lehet visszavonni!"
|
||||
},
|
||||
|
@ -570,59 +586,59 @@
|
|||
"edit": "Szerkesztés",
|
||||
"done": "Befejezve",
|
||||
"heading1": "Címsor 1",
|
||||
"heading1Tooltip": "Big section heading.",
|
||||
"heading1Tooltip": "Nagy szakasz címsora.",
|
||||
"heading2": "Címsor 2",
|
||||
"heading2Tooltip": "Medium section heading.",
|
||||
"heading2Tooltip": "Közepes szakasz címsora.",
|
||||
"heading3": "Címsor 3",
|
||||
"heading3Tooltip": "Smaller section header.",
|
||||
"heading3Tooltip": "Kisebb szakasz címsora.",
|
||||
"headingSmaller": "Kisebb címsor",
|
||||
"headingBigger": "Nagyobb címsor",
|
||||
"bold": "Félkövér",
|
||||
"italic": "Dőlt",
|
||||
"strikethrough": "Áthúzott",
|
||||
"underline": "Underline",
|
||||
"underline": "Aláhúzott",
|
||||
"code": "Kód",
|
||||
"codeTooltip": "Capture a code snippet.",
|
||||
"codeTooltip": "Rögzítsen egy kódrészletet.",
|
||||
"quote": "Idézet",
|
||||
"quoteTooltip": "Capture a quote.",
|
||||
"bulletList": "Bullet list",
|
||||
"bulletListTooltip": "Create a simple bullet list.",
|
||||
"unorderedList": "Unordered list",
|
||||
"orderedList": "Ordered list",
|
||||
"orderedListTooltip": "Create a list with numbering.",
|
||||
"quoteTooltip": "Rögzítsen egy idézetet.",
|
||||
"bulletList": "Felsorolásos lista",
|
||||
"bulletListTooltip": "Hozzon létre egy egyszerű felsoroláslistát.",
|
||||
"unorderedList": "Rendezetlen lista",
|
||||
"orderedList": "Rendezett lista",
|
||||
"orderedListTooltip": "Készítsen listát számozással.",
|
||||
"cleanBlock": "Blokk kitisztítása",
|
||||
"link": "Hivatkozás",
|
||||
"image": "Kép",
|
||||
"imageTooltip": "Upload an image from your computer.",
|
||||
"imageTooltip": "Fájl feltöltése a számítógépről.",
|
||||
"table": {
|
||||
"title": "Table",
|
||||
"insert": "Insert table",
|
||||
"addColumnBefore": "Add column before",
|
||||
"addColumnAfter": "Add column after",
|
||||
"deleteColumn": "Delete column",
|
||||
"addRowBefore": "Add row before",
|
||||
"addRowAfter": "Add row after",
|
||||
"deleteRow": "Delete row",
|
||||
"deleteTable": "Delete table",
|
||||
"mergeCells": "Merge cells",
|
||||
"splitCell": "Split cell",
|
||||
"toggleHeaderColumn": "Toggle header column",
|
||||
"toggleHeaderRow": "Toggle header row",
|
||||
"toggleHeaderCell": "Toggle header cell",
|
||||
"mergeOrSplit": "Merge or split",
|
||||
"fixTables": "Fix tables"
|
||||
"title": "Táblázat",
|
||||
"insert": "Táblázat beszúrása",
|
||||
"addColumnBefore": "Oszlop hozzáadása előtte",
|
||||
"addColumnAfter": "Oszlop hozzáadása utána",
|
||||
"deleteColumn": "Oszlop törlése",
|
||||
"addRowBefore": "Sor hozzáadása előtte",
|
||||
"addRowAfter": "Sor hozzáadása utána",
|
||||
"deleteRow": "Sor törlése",
|
||||
"deleteTable": "Táblázat törlése",
|
||||
"mergeCells": "Cellák egyesítése",
|
||||
"splitCell": "Cellák szétválasztása",
|
||||
"toggleHeaderColumn": "A fejléc oszlopának váltása",
|
||||
"toggleHeaderRow": "Váltás a fejlécsorra",
|
||||
"toggleHeaderCell": "Kapcsolja be a fejléccellát",
|
||||
"mergeOrSplit": "Egyesítés vagy felosztás",
|
||||
"fixTables": "Táblázatok javítása"
|
||||
},
|
||||
"horizontalRule": "Vízszintes vonal",
|
||||
"horizontalRuleTooltip": "Divide a section.",
|
||||
"horizontalRuleTooltip": "Egy szakasz felosztása.",
|
||||
"sideBySide": "Egymás mellett",
|
||||
"guide": "Útmutató",
|
||||
"text": "Text",
|
||||
"textTooltip": "Just start typing with plain text.",
|
||||
"taskList": "Task list",
|
||||
"taskListTooltip": "Track tasks with a to-do list.",
|
||||
"undo": "Undo",
|
||||
"redo": "Redo",
|
||||
"placeholder": "Type some text or hit '/' to see more options…"
|
||||
"text": "Szöveg",
|
||||
"textTooltip": "Csak kezdje el a gépelést egyszerű szöveggel.",
|
||||
"taskList": "Feladatlista",
|
||||
"taskListTooltip": "Kövesse nyomon a feladatokat egy teendőlistával.",
|
||||
"undo": "Visszavonás",
|
||||
"redo": "Újra",
|
||||
"placeholder": "Írjon be egy szöveget, vagy nyomja meg a „/” gombot a további lehetőségek megtekintéséhez…"
|
||||
},
|
||||
"multiselect": {
|
||||
"createPlaceholder": "Új létrehozása",
|
||||
|
@ -632,7 +648,7 @@
|
|||
"to": "Eddig",
|
||||
"from": "Ettől",
|
||||
"fromto": "{from} - tól {to} - ig",
|
||||
"date": "Date",
|
||||
"date": "Dátum",
|
||||
"ranges": {
|
||||
"today": "Ma",
|
||||
"thisWeek": "Ezen a héten",
|
||||
|
@ -649,25 +665,25 @@
|
|||
"restOfThisYear": "Az év hátralévő része"
|
||||
},
|
||||
"values": {
|
||||
"now": "Now",
|
||||
"startOfToday": "Start of today",
|
||||
"endOfToday": "End of today",
|
||||
"beginningOflastWeek": "Beginning of last week",
|
||||
"endOfLastWeek": "End of last week",
|
||||
"beginningOfThisWeek": "Beginning of this week",
|
||||
"endOfThisWeek": "End of this week",
|
||||
"startOfNextWeek": "Start of next week",
|
||||
"endOfNextWeek": "End of next week",
|
||||
"in7Days": "In 7 days",
|
||||
"beginningOfLastMonth": "Beginning of last month",
|
||||
"endOfLastMonth": "End of last month",
|
||||
"startOfThisMonth": "Start of this month",
|
||||
"endOfThisMonth": "End of this month",
|
||||
"startOfNextMonth": "Start of next month",
|
||||
"endOfNextMonth": "End of next month",
|
||||
"in30Days": "In 30 days",
|
||||
"startOfThisYear": "Beginning of this year",
|
||||
"endOfThisYear": "End of this year"
|
||||
"now": "Most",
|
||||
"startOfToday": "A mai nap kezdete",
|
||||
"endOfToday": "A mai nap vége",
|
||||
"beginningOflastWeek": "Múlt hét eleje",
|
||||
"endOfLastWeek": "Múlt hét vége",
|
||||
"beginningOfThisWeek": "E hét eleje",
|
||||
"endOfThisWeek": "E hét vége",
|
||||
"startOfNextWeek": "Jövő hét eleje",
|
||||
"endOfNextWeek": "Jövő hét vége",
|
||||
"in7Days": "7 napon belül",
|
||||
"beginningOfLastMonth": "Múlt hónap eleje",
|
||||
"endOfLastMonth": "Múlt hónap vége",
|
||||
"startOfThisMonth": "E hónap eleje",
|
||||
"endOfThisMonth": "E hónap vége",
|
||||
"startOfNextMonth": "Jövő hónap eleje",
|
||||
"endOfNextMonth": "Jövő hónap vége",
|
||||
"in30Days": "30 napon belül",
|
||||
"startOfThisYear": "Ez év eleje",
|
||||
"endOfThisYear": "Ez év vége"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
|
@ -783,7 +799,7 @@
|
|||
"startDate": "Kezdő dátum",
|
||||
"title": "Cím",
|
||||
"updated": "Frissítve",
|
||||
"doneAt": "Done At"
|
||||
"doneAt": "Befejezve ekkor"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedTaskThroughParentProject": "Itt nem iratkozhat le, mert a projektjén keresztül feliratkozott erre a feladatra.",
|
||||
|
@ -819,7 +835,7 @@
|
|||
"loading": "Hozzászólások betöltése…",
|
||||
"edited": "Szerkesztve: {date}",
|
||||
"creating": "Hozzászólás létrehozása…",
|
||||
"placeholder": "Add your comment, hit '/' for more options…",
|
||||
"placeholder": "Adja hozzá megjegyzését, nyomja meg a „/” gombot a további lehetőségekért…",
|
||||
"comment": "Hozzászólás",
|
||||
"delete": "Hozzászólás törlése",
|
||||
"deleteText1": "Biztos benne, hogy törölni akarja ezt a hozzászólást?",
|
||||
|
@ -833,7 +849,7 @@
|
|||
"1week": "1 hét"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "Enter a description, hit '/' for more options…",
|
||||
"placeholder": "Írja be a leírást, nyomja meg a '/' gombot a további lehetőségekért…",
|
||||
"empty": "Nem érhető el leírás."
|
||||
},
|
||||
"assignee": {
|
||||
|
@ -984,11 +1000,11 @@
|
|||
"namePlaceholder": "A csapat nevét ide írja…",
|
||||
"nameRequired": "Kérjük, adjon meg egy nevet.",
|
||||
"description": "Leírás",
|
||||
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
|
||||
"descriptionPlaceholder": "Jellemezze a csapatot itt, kattintson a '/' gombra a további lehetőségekért…",
|
||||
"admin": "Adminisztrátor",
|
||||
"member": "Tag",
|
||||
"isPublic": "Public Team",
|
||||
"isPublicDescription": "Make the team publicly discoverable. When enabled, anyone can share projects with this team even when not being a direct member."
|
||||
"isPublic": "Nyilvános csapat",
|
||||
"isPublicDescription": "Tedd nyilvánosan felfedezhetővé a csapatot. Ha engedélyezve van, bárki megoszthat projekteket ezzel a csapattal, még akkor is, ha nem közvetlen tagja."
|
||||
}
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
|
@ -1047,8 +1063,9 @@
|
|||
"share": "Megosztás",
|
||||
"newProject": "Új projekt",
|
||||
"createProject": "Projekt létrehozása",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantArchiveIsDefault": "Ezt nem archiválhatja, mert ez az alapértelmezett projektje.",
|
||||
"cantDeleteIsDefault": "Ezt nem törölheti, mert ez az alapértelmezett projektje.",
|
||||
"views": "Nézetek"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
@ -1068,8 +1085,8 @@
|
|||
"title": "Értesítések",
|
||||
"none": "Nincsenek értesítései. Legyen szép napja!",
|
||||
"explainer": "Az értesítések itt jelennek meg, amikor olyan projektek vagy feladatok történnek, amelyekre feliratkozott.",
|
||||
"markAllRead": "Mark all notifications as read",
|
||||
"markAllReadSuccess": "Successfully marked all notifications as read."
|
||||
"markAllRead": "Minden értesítés megjelölése olvasottként",
|
||||
"markAllReadSuccess": "Az összes értesítést olvasottként jelölte meg."
|
||||
},
|
||||
"quickActions": {
|
||||
"commands": "Parancsok",
|
||||
|
@ -1096,10 +1113,10 @@
|
|||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
"reactedWith": "{user} a következővel reagált: {value}",
|
||||
"reactedWithAnd": "{users} és {lastUser} a következővel reagált: {value}",
|
||||
"reactedWithAndMany": "{users} és további {num} felhasználó reagált a következővel: {value}",
|
||||
"add": "Adja hozzá reakcióját"
|
||||
},
|
||||
"error": {
|
||||
"error": "Hiba",
|
||||
|
@ -1174,7 +1191,7 @@
|
|||
},
|
||||
"about": {
|
||||
"title": "Névjegy",
|
||||
"version": "Version: {version}"
|
||||
"version": "Verzió: {version}"
|
||||
},
|
||||
"time": {
|
||||
"units": {
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "URL Vikunja",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "新しいプロジェクトの作成",
|
||||
"createProject": "プロジェクトの作成",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Nytt prosjekt",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -57,11 +57,11 @@
|
|||
"logout": "Wyloguj",
|
||||
"emailInvalid": "Proszę podać poprawny adres e-mail.",
|
||||
"usernameRequired": "Proszę podać nazwę użytkownika.",
|
||||
"usernameMustNotContainSpace": "The username must not contain spaces.",
|
||||
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
|
||||
"usernameMustNotContainSpace": "Nazwa użytkownika nie może zawierać spacji.",
|
||||
"usernameMustNotLookLikeUrl": "Nazwa użytkownika nie może wyglądać jak adres URL.",
|
||||
"passwordRequired": "Proszę podać hasło.",
|
||||
"passwordNotMin": "Password must have at least 8 characters.",
|
||||
"passwordNotMax": "Password must have at most 250 characters.",
|
||||
"passwordNotMin": "Hasło musi zawierać co najmniej 8 znaków.",
|
||||
"passwordNotMax": "Hasło musi zawierać co najwyżej 250 znaków.",
|
||||
"showPassword": "Pokaż hasło",
|
||||
"hidePassword": "Ukryj hasło",
|
||||
"noAccountYet": "Nie masz jeszcze konta?",
|
||||
|
@ -248,7 +248,7 @@
|
|||
"text2": "Dotyczy to wszystkich zadań i tego NIE DA SIĘ COFNĄĆ!",
|
||||
"success": "Projekt został pomyślnie usunięty.",
|
||||
"tasksToDelete": "To nieodwracalnie usunie około {count} zadań.",
|
||||
"tasksAndChildProjectsToDelete": "This will irrevocably remove approx. {tasks} tasks and {projects} projects.",
|
||||
"tasksAndChildProjectsToDelete": "To nieodwracalnie usunie ok. {tasks} zadań i {projects} projektów.",
|
||||
"noTasksToDelete": "Ten projekt nie zawiera żadnych zadań, więc można go bezpiecznie usunąć."
|
||||
},
|
||||
"duplicate": {
|
||||
|
@ -381,12 +381,28 @@
|
|||
"secret": "Sekret",
|
||||
"secretHint": "Jeśli podane, wszystkie żądania do adresu docelowego webhooka zostaną podpisane przy użyciu HMAC.",
|
||||
"secretDocs": "Sprawdź dokumentację, aby uzyskać więcej informacji na temat korzystania z sekretów."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edytuj widoki",
|
||||
"title": "Tytuł",
|
||||
"actions": "Działania",
|
||||
"kind": "Rodzaj",
|
||||
"bucketConfigMode": "Tryb konfiguracji kolumny",
|
||||
"bucketConfig": "Konfiguracja kolumny",
|
||||
"bucketConfigManual": "Instrukcja",
|
||||
"filter": "Filtr",
|
||||
"create": "Utwórz widok",
|
||||
"createSuccess": "Widok utworzony pomyślnie.",
|
||||
"titleRequired": "Proszę podać tytuł.",
|
||||
"delete": "Usuń ten widok",
|
||||
"deleteText": "Czy na pewno chcesz usunąć ten widok? Nie będzie już możliwe wyświetlanie zadań w tym projekcie. Ta akcja nie usunie żadnych zadań. Tej operacji nie można cofnąć!",
|
||||
"deleteSuccess": "Widok został pomyślnie usunięty"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"title": "Filtry",
|
||||
"clear": "Wyczyść filtry",
|
||||
"showResults": "Show results",
|
||||
"showResults": "Wyświetl wyniki",
|
||||
"attributes": {
|
||||
"title": "Tytuł",
|
||||
"titlePlaceholder": "Tu wpisz tytuł filtra stałego…",
|
||||
|
@ -419,48 +435,48 @@
|
|||
"success": "Filtr został pomyślnie zapisany."
|
||||
},
|
||||
"query": {
|
||||
"title": "Query",
|
||||
"placeholder": "Type a search or filter query…",
|
||||
"title": "Zapytanie",
|
||||
"placeholder": "Wpisz zapytanie wyszukiwania lub filtruj…",
|
||||
"help": {
|
||||
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
|
||||
"link": "How does this work?",
|
||||
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
|
||||
"intro": "Aby filtrować zadania, możesz użyć składni zapytań podobnej do SQL. Pola dostępne do filtrowania to:",
|
||||
"link": "Jak to działa?",
|
||||
"canUseDatemath": "Możesz użyć matematyki dat, aby ustawić daty względne. Kliknij wartość daty w zapytaniu, aby dowiedzieć się więcej.",
|
||||
"fields": {
|
||||
"done": "Whether the task is completed or not",
|
||||
"priority": "The priority level of the task (1-5)",
|
||||
"percentDone": "The percentage of completion for the task (0-100)",
|
||||
"dueDate": "The due date of the task",
|
||||
"startDate": "The start date of the task",
|
||||
"endDate": "The end date of the task",
|
||||
"doneAt": "The date and time when the task was completed",
|
||||
"assignees": "The assignees of the task",
|
||||
"labels": "The labels associated with the task",
|
||||
"project": "The project the task belongs to (only available for saved filters, not on a project level)"
|
||||
"done": "Czy zadanie zostało zakończone, czy nie",
|
||||
"priority": "Priorytet zadania (1-5)",
|
||||
"percentDone": "Procent ukończenia zadania (0-100)",
|
||||
"dueDate": "Termin wykonania zadania",
|
||||
"startDate": "Data rozpoczęcia zadania",
|
||||
"endDate": "Data zakończenia zadania",
|
||||
"doneAt": "Data i czas ukończenia zadania",
|
||||
"assignees": "Osoby przypisane do zadania",
|
||||
"labels": "Etykiety przypisane do zadania",
|
||||
"project": "Projekt, do którego należy zadanie (dostępne tylko dla zapisanych filtrów, nie na poziomie projektu)"
|
||||
},
|
||||
"operators": {
|
||||
"intro": "The available operators for filtering include:",
|
||||
"notEqual": "Not equal to",
|
||||
"equal": "Equal to",
|
||||
"greaterThan": "Greater than",
|
||||
"greaterThanOrEqual": "Greater than or equal to",
|
||||
"lessThan": "Less than",
|
||||
"lessThanOrEqual": "Less than or equal to",
|
||||
"like": "Matches a pattern (using wildcard %)",
|
||||
"in": "Matches any value in a comma-seperated list of values"
|
||||
"intro": "Dostępne operatory do filtrowania to:",
|
||||
"notEqual": "Nie równa się",
|
||||
"equal": "Równa się",
|
||||
"greaterThan": "Większe niż",
|
||||
"greaterThanOrEqual": "Większe niż lub równe",
|
||||
"lessThan": "Mniejsze niż",
|
||||
"lessThanOrEqual": "Mniejsze niż lub równe",
|
||||
"like": "Pasuje do wzorca (używając symbolu %)",
|
||||
"in": "Dopasuje dowolną wartość z listy wartości oddzielonych przecinkami"
|
||||
},
|
||||
"logicalOperators": {
|
||||
"intro": "To combine multiple conditions, you can use the following logical operators:",
|
||||
"and": "AND operator, matches if all conditions are true",
|
||||
"or": "OR operator, matches if any of the conditions are true",
|
||||
"parentheses": "Parentheses for grouping conditions"
|
||||
"intro": "Aby połączyć wiele warunków, możesz użyć następujących operatorów logicznych:",
|
||||
"and": "Operator AND, dopasowuje, jeśli wszystkie warunki są prawdziwe",
|
||||
"or": "Operator OR, dopasowuje, jeśli którykolwiek z warunków jest spełniony",
|
||||
"parentheses": "Nawiasy do grupowania warunków"
|
||||
},
|
||||
"examples": {
|
||||
"intro": "Here are some examples of filter queries:",
|
||||
"priorityEqual": "Matches tasks with priority level 4",
|
||||
"dueDatePast": "Matches tasks with a due date in the past",
|
||||
"undoneHighPriority": "Matches undone tasks with priority level 3 or higher",
|
||||
"assigneesIn": "Matches tasks assigned to either \"user1\" or \"user2\"",
|
||||
"priorityOneOrTwoPastDue": "Matches tasks with priority level 1 or 2 and a due date in the past"
|
||||
"intro": "Oto kilka przykładów zapytań filtrujących:",
|
||||
"priorityEqual": "Dopasowuje zadania z priorytetem 4",
|
||||
"dueDatePast": "Dopasowuje zadania z terminem wykonania w przeszłości",
|
||||
"undoneHighPriority": "Dopasowuje niewykonane zadania z poziomem 3 lub wyższym",
|
||||
"assigneesIn": "Dopasowuje zadania przypisane do \"user1\" lub \"user2\"",
|
||||
"priorityOneOrTwoPastDue": "Dopasowuje zadania z poziomem 1 lub 2 i terminem wykonania w przeszłości"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +648,7 @@
|
|||
"to": "Do",
|
||||
"from": "Od",
|
||||
"fromto": "{from} do {to}",
|
||||
"date": "Date",
|
||||
"date": "Data",
|
||||
"ranges": {
|
||||
"today": "Dziś",
|
||||
"thisWeek": "W tym tygodniu",
|
||||
|
@ -649,25 +665,25 @@
|
|||
"restOfThisYear": "Reszta tego roku"
|
||||
},
|
||||
"values": {
|
||||
"now": "Now",
|
||||
"startOfToday": "Start of today",
|
||||
"endOfToday": "End of today",
|
||||
"beginningOflastWeek": "Beginning of last week",
|
||||
"endOfLastWeek": "End of last week",
|
||||
"beginningOfThisWeek": "Beginning of this week",
|
||||
"endOfThisWeek": "End of this week",
|
||||
"startOfNextWeek": "Start of next week",
|
||||
"endOfNextWeek": "End of next week",
|
||||
"in7Days": "In 7 days",
|
||||
"beginningOfLastMonth": "Beginning of last month",
|
||||
"endOfLastMonth": "End of last month",
|
||||
"startOfThisMonth": "Start of this month",
|
||||
"endOfThisMonth": "End of this month",
|
||||
"startOfNextMonth": "Start of next month",
|
||||
"endOfNextMonth": "End of next month",
|
||||
"in30Days": "In 30 days",
|
||||
"startOfThisYear": "Beginning of this year",
|
||||
"endOfThisYear": "End of this year"
|
||||
"now": "Teraz",
|
||||
"startOfToday": "Początek dzisiejszego dnia",
|
||||
"endOfToday": "Koniec dzisiejszego dnia",
|
||||
"beginningOflastWeek": "Początek zeszłego tygodnia",
|
||||
"endOfLastWeek": "Koniec zeszłego tygodnia",
|
||||
"beginningOfThisWeek": "Początek tego tygodnia",
|
||||
"endOfThisWeek": "Koniec tego tygodnia",
|
||||
"startOfNextWeek": "Początek następnego tygodnia",
|
||||
"endOfNextWeek": "Koniec następnego tygodnia",
|
||||
"in7Days": "Za 7 dni",
|
||||
"beginningOfLastMonth": "Początek zeszłego miesiąca",
|
||||
"endOfLastMonth": "Koniec zeszłego miesiąca",
|
||||
"startOfThisMonth": "Początek tego miesiąca",
|
||||
"endOfThisMonth": "Koniec tego miesiąca",
|
||||
"startOfNextMonth": "Początek następnego miesiąca",
|
||||
"endOfNextMonth": "Koniec następnego miesiąca",
|
||||
"in30Days": "Za 30 dni",
|
||||
"startOfThisYear": "Początek tego roku",
|
||||
"endOfThisYear": "Koniec tego roku"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
|
@ -783,7 +799,7 @@
|
|||
"startDate": "Data rozpoczęcia",
|
||||
"title": "Tytuł",
|
||||
"updated": "Zaktualizowano",
|
||||
"doneAt": "Done At"
|
||||
"doneAt": "Wykonano"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedTaskThroughParentProject": "Nie możesz zrezygnować z subskrypcji, ponieważ subskrybujesz to zadanie poprzez jego projekt.",
|
||||
|
@ -987,8 +1003,8 @@
|
|||
"descriptionPlaceholder": "Opisz tutaj zespół, naciśnij '/' aby uzyskać więcej opcji…",
|
||||
"admin": "Administrator",
|
||||
"member": "Członek",
|
||||
"isPublic": "Public Team",
|
||||
"isPublicDescription": "Make the team publicly discoverable. When enabled, anyone can share projects with this team even when not being a direct member."
|
||||
"isPublic": "Publiczny zespół",
|
||||
"isPublicDescription": "Udostępnij zespół publicznie. Gdy ta opcja jest włączona, każdy może udostępniać projekty temu zespołowi, nawet nie będąc jego bezpośrednim członkiem."
|
||||
}
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
|
@ -1047,8 +1063,9 @@
|
|||
"share": "Udostępnij",
|
||||
"newProject": "Nowy projekt",
|
||||
"createProject": "Utwórz projekt",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantArchiveIsDefault": "Nie możesz tego zarchiwizować, ponieważ jest to twój domyślny projekt.",
|
||||
"cantDeleteIsDefault": "Nie możesz tego usunąć, ponieważ jest to twój domyślny projekt.",
|
||||
"views": "Widoki"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "URL Vikunji",
|
||||
|
@ -1096,10 +1113,10 @@
|
|||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
"reactedWith": "{user} zareagował z {value}",
|
||||
"reactedWithAnd": "{users} i {lastUser} zareagowali z {value}",
|
||||
"reactedWithAndMany": "{users} i {num} innych osób zareagowali z {value}",
|
||||
"add": "Dodaj swoją reakcję"
|
||||
},
|
||||
"error": {
|
||||
"error": "Błąd",
|
||||
|
@ -1174,7 +1191,7 @@
|
|||
},
|
||||
"about": {
|
||||
"title": "O aplikacji",
|
||||
"version": "Version: {version}"
|
||||
"version": "Wersja: {version}"
|
||||
},
|
||||
"time": {
|
||||
"units": {
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Segredo",
|
||||
"secretHint": "Se fornecido, todas as requisições para a URL de destino do webhook serão assinadas usando HMAC.",
|
||||
"secretDocs": "Confira a documentação para obter mais detalhes sobre como usar segredos."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Novo projeto",
|
||||
"createProject": "Criar projeto",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Segredo",
|
||||
"secretHint": "Se fornecido, todos os pedidos para o URL de destino do webhook serão assinados utilizando HMAC.",
|
||||
"secretDocs": "Verifica a documentação para mais detalhes sobre como utilizar segredos."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Novo projeto",
|
||||
"createProject": "Criar projeto",
|
||||
"cantArchiveIsDefault": "Não podes arquivar isto porque é o teu projeto padrão.",
|
||||
"cantDeleteIsDefault": "Não podes eliminar isto porque é o teu projeto padrão."
|
||||
"cantDeleteIsDefault": "Não podes eliminar isto porque é o teu projeto padrão.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "URL do Vikunja",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Секрет",
|
||||
"secretHint": "Если указан, все запросы к URL обработчика будут подписаны с помощью HMAC.",
|
||||
"secretDocs": "Подробнее об использовании секретов в документации."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Создать проект",
|
||||
"createProject": "Создать проект",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Skrita koda",
|
||||
"secretHint": "Če je podana, bodo vse zahteve do ciljnega webhook URL-ja podpisane s HMAC.",
|
||||
"secretDocs": "Poglejte si dokumentacijo za več podrobnosti o uporabi skrite kode."
|
||||
},
|
||||
"views": {
|
||||
"header": "Uredi pogled",
|
||||
"title": "Naslov",
|
||||
"actions": "Dejanja",
|
||||
"kind": "Vrsta",
|
||||
"bucketConfigMode": "Način nastavitve vedra",
|
||||
"bucketConfig": "Nastavitev vedra",
|
||||
"bucketConfigManual": "Ročno",
|
||||
"filter": "Filter",
|
||||
"create": "Ustvari pogled",
|
||||
"createSuccess": "Pogled je bil uspešno ustvarjen.",
|
||||
"titleRequired": "Prosim navedite naslov.",
|
||||
"delete": "Izbriši pogled",
|
||||
"deleteText": "Ali ste prepričani, da želite odstraniti ta pogled? Ne bo ga več mogoče uporabljati za ogled nalog v tem projektu. To dejanje ne bo izbrisalo nobenih opravil. Tega ni mogoče razveljaviti!",
|
||||
"deleteSuccess": "Pogled je bil uspešno izbrisan"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Nov projekt",
|
||||
"createProject": "Ustvari projekt",
|
||||
"cantArchiveIsDefault": "Tega ne morete arhivirati, ker je to vaš privzeti projekt.",
|
||||
"cantDeleteIsDefault": "Tega ne morete izbrisati, ker je to vaš privzeti projekt."
|
||||
"cantDeleteIsDefault": "Tega ne morete izbrisati, ker je to vaš privzeti projekt.",
|
||||
"views": "Pogledi"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "Nytt projekt",
|
||||
"createProject": "Skapa projekt",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "URL Vikunja",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "密钥",
|
||||
"secretHint": "如果提供了 webhook 目标 URL 的所有请求都将使用 HMAC签名。",
|
||||
"secretDocs": "查看文档了解如何使用秘密的更多详情。"
|
||||
},
|
||||
"views": {
|
||||
"header": "编辑视图",
|
||||
"title": "标题",
|
||||
"actions": "操作",
|
||||
"kind": "类别",
|
||||
"bucketConfigMode": "桶配置模式",
|
||||
"bucketConfig": "桶配置",
|
||||
"bucketConfigManual": "手册",
|
||||
"filter": "过滤器",
|
||||
"create": "创建视图",
|
||||
"createSuccess": "视图创建成功。",
|
||||
"titleRequired": "请提供标题。",
|
||||
"delete": "删除此视图",
|
||||
"deleteText": "您确定要删除此视图吗?它将不再可能使用它来查看此项目中的任务。 此操作不会删除任何任务。此操作不能撤销!",
|
||||
"deleteSuccess": "视图已成功删除"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "新项目",
|
||||
"createProject": "创建项目",
|
||||
"cantArchiveIsDefault": "您不能归档,因为这是您的默认项目。",
|
||||
"cantDeleteIsDefault": "您不能删除这个项目,因为这是您的默认项目。"
|
||||
"cantDeleteIsDefault": "您不能删除这个项目,因为这是您的默认项目。",
|
||||
"views": "视图"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -381,6 +381,22 @@
|
|||
"secret": "Secret",
|
||||
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
|
||||
"secretDocs": "Check out the docs for more details about how to use secrets."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"deleteSuccess": "The view was successfully deleted"
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
|
@ -1048,7 +1064,8 @@
|
|||
"newProject": "New project",
|
||||
"createProject": "Create project",
|
||||
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
|
||||
"cantDeleteIsDefault": "You cannot delete this because it is your default project.",
|
||||
"views": "Views"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type {IAbstract} from './IAbstract'
|
||||
import type {IUser} from './IUser'
|
||||
import type {ITask} from './ITask'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
export interface IBucket extends IAbstract {
|
||||
id: number
|
||||
|
@ -10,6 +11,7 @@ export interface IBucket extends IAbstract {
|
|||
tasks: ITask[]
|
||||
position: number
|
||||
count: number
|
||||
projectViewId: IProjectView['id']
|
||||
|
||||
createdBy: IUser
|
||||
created: Date
|
||||
|
|
|
@ -2,6 +2,7 @@ import type {IAbstract} from './IAbstract'
|
|||
import type {ITask} from './ITask'
|
||||
import type {IUser} from './IUser'
|
||||
import type {ISubscription} from './ISubscription'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
|
||||
export interface IProject extends IAbstract {
|
||||
|
@ -21,6 +22,7 @@ export interface IProject extends IAbstract {
|
|||
parentProjectId: number
|
||||
doneBucketId: number
|
||||
defaultBucketId: number
|
||||
views: IProjectView[]
|
||||
|
||||
created: Date
|
||||
updated: Date
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import type {IAbstract} from './IAbstract'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
export const PROJECT_VIEW_KINDS = ['list', 'gantt', 'table', 'kanban']
|
||||
export type ProjectViewKind = typeof PROJECT_VIEW_KINDS[number]
|
||||
|
||||
export const PROJECT_VIEW_BUCKET_CONFIGURATION_MODES = ['none', 'manual', 'filter']
|
||||
export type ProjectViewBucketConfigurationMode = typeof PROJECT_VIEW_BUCKET_CONFIGURATION_MODES[number]
|
||||
|
||||
export interface IProjectViewBucketConfiguration {
|
||||
title: string
|
||||
filter: string
|
||||
}
|
||||
|
||||
export interface IProjectView extends IAbstract {
|
||||
id: number
|
||||
title: string
|
||||
projectId: IProject['id']
|
||||
viewKind: ProjectViewKind
|
||||
|
||||
filter: string
|
||||
position: number
|
||||
|
||||
bucketConfigurationMode: ProjectViewBucketConfigurationMode
|
||||
bucketConfiguration: IProjectViewBucketConfiguration[]
|
||||
defaultBucketId: number
|
||||
doneBucketId: number
|
||||
|
||||
created: Date
|
||||
updated: Date
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import type {IAbstract} from '@/modelTypes/IAbstract'
|
||||
|
||||
export interface ITaskPosition extends IAbstract {
|
||||
position: number
|
||||
projectViewId: IProjectView['id']
|
||||
taskId: number
|
||||
}
|
|
@ -7,6 +7,7 @@ import type {IProject} from '@/modelTypes/IProject'
|
|||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {ISubscription} from '@/modelTypes/ISubscription'
|
||||
import ProjectViewModel from '@/models/projectView'
|
||||
|
||||
export default class ProjectModel extends AbstractModel<IProject> implements IProject {
|
||||
id = 0
|
||||
|
@ -25,6 +26,7 @@ export default class ProjectModel extends AbstractModel<IProject> implements IPr
|
|||
parentProjectId = 0
|
||||
doneBucketId = 0
|
||||
defaultBucketId = 0
|
||||
views = []
|
||||
|
||||
created: Date = null
|
||||
updated: Date = null
|
||||
|
@ -48,6 +50,8 @@ export default class ProjectModel extends AbstractModel<IProject> implements IPr
|
|||
this.subscription = new SubscriptionModel(this.subscription)
|
||||
}
|
||||
|
||||
this.views = this.views.map(v => new ProjectViewModel(v))
|
||||
|
||||
this.created = new Date(this.created)
|
||||
this.updated = new Date(this.updated)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import type {IProjectView, ProjectViewBucketConfigurationMode, ProjectViewKind} from '@/modelTypes/IProjectView'
|
||||
import AbstractModel from '@/models/abstractModel'
|
||||
|
||||
export default class ProjectViewModel extends AbstractModel<IProjectView> implements IProjectView {
|
||||
id = 0
|
||||
title = ''
|
||||
projectId = 0
|
||||
viewKind: ProjectViewKind = 'list'
|
||||
|
||||
filter = ''
|
||||
position = 0
|
||||
|
||||
bucketConfiguration = []
|
||||
bucketConfigurationMode: ProjectViewBucketConfigurationMode = 'manual'
|
||||
defaultBucketId = 0
|
||||
doneBucketId = 0
|
||||
|
||||
created: Date = new Date()
|
||||
updated: Date = new Date()
|
||||
|
||||
constructor(data: Partial<IProjectView>) {
|
||||
super()
|
||||
this.assignData(data)
|
||||
|
||||
if (!this.bucketConfiguration) {
|
||||
this.bucketConfiguration = []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import AbstractModel from '@/models/abstractModel'
|
||||
import type {ITaskPosition} from '@/modelTypes/ITaskPosition'
|
||||
|
||||
export default class TaskPositionModel extends AbstractModel<ITaskPosition> implements ITaskPosition {
|
||||
position = 0
|
||||
projectViewId = 0
|
||||
taskId = 0
|
||||
|
||||
constructor(data: Partial<ITaskPosition>) {
|
||||
super()
|
||||
this.assignData(data)
|
||||
}
|
||||
}
|
|
@ -2,13 +2,11 @@ import { createRouter, createWebHistory } from 'vue-router'
|
|||
import type { RouteLocation } from 'vue-router'
|
||||
import {saveLastVisited} from '@/helpers/saveLastVisited'
|
||||
|
||||
import {saveProjectView, getProjectView} from '@/helpers/projectView'
|
||||
import {saveProjectView, getProjectViewId} from '@/helpers/projectView'
|
||||
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
||||
import {getNextWeekDate} from '@/helpers/time/getNextWeekDate'
|
||||
import {setTitle} from '@/helpers/setTitle'
|
||||
import {LINK_SHARE_HASH_PREFIX} from '@/constants/linkShareHash'
|
||||
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
||||
|
@ -33,15 +31,8 @@ const NewLabelComponent = () => import('@/views/labels/NewLabel.vue')
|
|||
// Migration
|
||||
const MigrationComponent = () => import('@/views/migrate/Migration.vue')
|
||||
const MigrationHandlerComponent = () => import('@/views/migrate/MigrationHandler.vue')
|
||||
// Project Views
|
||||
const ProjectList = () => import('@/views/project/ProjectList.vue')
|
||||
const ProjectGantt = () => import('@/views/project/ProjectGantt.vue')
|
||||
const ProjectTable = () => import('@/views/project/ProjectTable.vue')
|
||||
// If we load the component async, using it as a backdrop view will not work. Instead, everything explodes
|
||||
// with an error from the core saying "Cannot read properties of undefined (reading 'parentNode')"
|
||||
// Of course, with no clear indicator of where the problem comes from.
|
||||
// const ProjectKanban = () => import('@/views/project/ProjectKanban.vue')
|
||||
import ProjectKanban from '@/views/project/ProjectKanban.vue'
|
||||
// Project View
|
||||
import ProjectView from '@/views/project/ProjectView.vue'
|
||||
const ProjectInfo = () => import('@/views/project/ProjectInfo.vue')
|
||||
|
||||
// Project Settings
|
||||
|
@ -53,6 +44,7 @@ const ProjectSettingShare = () => import('@/views/project/settings/share.vue')
|
|||
const ProjectSettingWebhooks = () => import('@/views/project/settings/webhooks.vue')
|
||||
const ProjectSettingDelete = () => import('@/views/project/settings/delete.vue')
|
||||
const ProjectSettingArchive = () => import('@/views/project/settings/archive.vue')
|
||||
const ProjectSettingViews = () => import('@/views/project/settings/views.vue')
|
||||
|
||||
// Saved Filters
|
||||
const FilterNew = () => import('@/views/filters/FilterNew.vue')
|
||||
|
@ -315,6 +307,15 @@ const router = createRouter({
|
|||
showAsModal: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/settings/views',
|
||||
name: 'project.settings.views',
|
||||
component: ProjectSettingViews,
|
||||
meta: {
|
||||
showAsModal: true,
|
||||
},
|
||||
props: route => ({ projectId: Number(route.params.projectId as string) }),
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/settings/edit',
|
||||
name: 'filter.settings.edit',
|
||||
|
@ -346,55 +347,30 @@ const router = createRouter({
|
|||
path: '/projects/:projectId',
|
||||
name: 'project.index',
|
||||
redirect(to) {
|
||||
// Redirect the user to list view by default
|
||||
const savedProjectView = getProjectView(Number(to.params.projectId as string))
|
||||
const viewId = getProjectViewId(Number(to.params.projectId as string))
|
||||
|
||||
if (savedProjectView) {
|
||||
console.log('Replaced list view with', savedProjectView)
|
||||
if (viewId) {
|
||||
console.debug('Replaced list view with', viewId)
|
||||
}
|
||||
|
||||
return {
|
||||
name: savedProjectView || 'project.list',
|
||||
params: {projectId: to.params.projectId},
|
||||
name: 'project.view',
|
||||
params: {
|
||||
projectId: parseInt(to.params.projectId as string),
|
||||
viewId: viewId ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/list',
|
||||
name: 'project.list',
|
||||
component: ProjectList,
|
||||
beforeEnter: (to) => saveProjectView(to.params.projectId, to.name),
|
||||
props: route => ({ projectId: Number(route.params.projectId as string) }),
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/gantt',
|
||||
name: 'project.gantt',
|
||||
component: ProjectGantt,
|
||||
beforeEnter: (to) => saveProjectView(to.params.projectId, to.name),
|
||||
// FIXME: test if `useRoute` would be the same. If it would use it instead.
|
||||
props: route => ({route}),
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/table',
|
||||
name: 'project.table',
|
||||
component: ProjectTable,
|
||||
beforeEnter: (to) => saveProjectView(to.params.projectId, to.name),
|
||||
props: route => ({ projectId: Number(route.params.projectId as string) }),
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/kanban',
|
||||
name: 'project.kanban',
|
||||
component: ProjectKanban,
|
||||
beforeEnter: (to) => {
|
||||
saveProjectView(to.params.projectId, to.name)
|
||||
// Properly set the page title when a task popup is closed
|
||||
const projectStore = useProjectStore()
|
||||
const projectFromStore = projectStore.projects[Number(to.params.projectId)]
|
||||
if(projectFromStore) {
|
||||
setTitle(projectFromStore.title)
|
||||
}
|
||||
},
|
||||
props: route => ({ projectId: Number(route.params.projectId as string) }),
|
||||
path: '/projects/:projectId/:viewId',
|
||||
name: 'project.view',
|
||||
component: ProjectView,
|
||||
beforeEnter: (to) => saveProjectView(parseInt(to.params.projectId as string), parseInt(to.params.viewId as string)),
|
||||
props: route => ({
|
||||
projectId: parseInt(route.params.projectId as string),
|
||||
viewId: route.params.viewId ? parseInt(route.params.viewId as string): undefined,
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: '/teams',
|
||||
|
|
|
@ -6,10 +6,10 @@ import type { IBucket } from '@/modelTypes/IBucket'
|
|||
export default class BucketService extends AbstractService<IBucket> {
|
||||
constructor() {
|
||||
super({
|
||||
getAll: '/projects/{projectId}/buckets',
|
||||
create: '/projects/{projectId}/buckets',
|
||||
update: '/projects/{projectId}/buckets/{id}',
|
||||
delete: '/projects/{projectId}/buckets/{id}',
|
||||
getAll: '/projects/{projectId}/views/{projectViewId}/buckets',
|
||||
create: '/projects/{projectId}/views/{projectViewId}/buckets',
|
||||
update: '/projects/{projectId}/views/{projectViewId}/buckets/{id}',
|
||||
delete: '/projects/{projectId}/views/{projectViewId}/buckets/{id}',
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import AbstractService from '@/services/abstractService'
|
||||
import type {IAbstract} from '@/modelTypes/IAbstract'
|
||||
import ProjectViewModel from '@/models/projectView'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
export default class ProjectViewService extends AbstractService<IProjectView> {
|
||||
constructor() {
|
||||
super({
|
||||
get: '/projects/{projectId}/views/{id}',
|
||||
getAll: '/projects/{projectId}/views',
|
||||
create: '/projects/{projectId}/views',
|
||||
update: '/projects/{projectId}/views/{id}',
|
||||
delete: '/projects/{projectId}/views/{id}',
|
||||
})
|
||||
}
|
||||
|
||||
modelFactory(data: Partial<IAbstract>): ProjectViewModel {
|
||||
return new ProjectViewModel(data)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import AbstractService from '@/services/abstractService'
|
|||
import TaskModel from '@/models/task'
|
||||
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import BucketModel from '@/models/bucket'
|
||||
|
||||
export interface TaskFilterParams {
|
||||
sort_by: ('start_date' | 'end_date' | 'due_date' | 'done' | 'id' | 'position' | 'kanban_position')[],
|
||||
|
@ -27,11 +28,15 @@ export function getDefaultTaskFilterParams(): TaskFilterParams {
|
|||
export default class TaskCollectionService extends AbstractService<ITask> {
|
||||
constructor() {
|
||||
super({
|
||||
getAll: '/projects/{projectId}/tasks',
|
||||
getAll: '/projects/{projectId}/views/{viewId}/tasks',
|
||||
})
|
||||
}
|
||||
|
||||
modelFactory(data) {
|
||||
// FIXME: There must be a better way for this…
|
||||
if (typeof data.project_view_id !== 'undefined') {
|
||||
return new BucketModel(data)
|
||||
}
|
||||
return new TaskModel(data)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import AbstractService from '@/services/abstractService'
|
||||
import type {ITaskPosition} from '@/modelTypes/ITaskPosition'
|
||||
import TaskPositionModel from '@/models/taskPosition'
|
||||
|
||||
export default class TaskPositionService extends AbstractService<ITaskPosition> {
|
||||
constructor() {
|
||||
super({
|
||||
update: '/tasks/{taskId}/position',
|
||||
})
|
||||
}
|
||||
|
||||
modelFactory(data: Partial<ITaskPosition>) {
|
||||
return new TaskPositionModel(data)
|
||||
}
|
||||
}
|
|
@ -3,8 +3,6 @@ import {acceptHMRUpdate, defineStore} from 'pinia'
|
|||
import {klona} from 'klona/lite'
|
||||
|
||||
import {findById, findIndexById} from '@/helpers/utils'
|
||||
import {i18n} from '@/i18n'
|
||||
import {success} from '@/message'
|
||||
|
||||
import BucketService from '@/services/bucket'
|
||||
import TaskCollectionService, {type TaskFilterParams} from '@/services/taskCollection'
|
||||
|
@ -15,6 +13,7 @@ import type {ITask} from '@/modelTypes/ITask'
|
|||
import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {IBucket} from '@/modelTypes/IBucket'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
const TASKS_PER_BUCKET = 25
|
||||
|
||||
|
@ -31,15 +30,6 @@ function getTaskIndicesById(buckets: IBucket[], taskId: ITask['id']) {
|
|||
}
|
||||
}
|
||||
|
||||
const addTaskToBucketAndSort = (buckets: IBucket[], task: ITask) => {
|
||||
const bucketIndex = findIndexById(buckets, task.bucketId)
|
||||
if (typeof buckets[bucketIndex] === 'undefined') {
|
||||
return
|
||||
}
|
||||
buckets[bucketIndex].tasks.push(task)
|
||||
buckets[bucketIndex].tasks.sort((a, b) => a.kanbanPosition > b.kanbanPosition ? 1 : -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* This store is intended to hold the currently active kanban view.
|
||||
* It should hold only the current buckets.
|
||||
|
@ -132,11 +122,6 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
const bucket = buckets.value[b]
|
||||
bucket.tasks[t] = task
|
||||
|
||||
if (bucket.id !== task.bucketId) {
|
||||
bucket.tasks.splice(t, 1)
|
||||
addTaskToBucketAndSort(buckets.value, task)
|
||||
}
|
||||
|
||||
buckets.value[b] = bucket
|
||||
|
||||
found = true
|
||||
|
@ -145,15 +130,6 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
}
|
||||
}
|
||||
|
||||
for (const b in buckets.value) {
|
||||
if (buckets.value[b].id === task.bucketId) {
|
||||
findAndUpdate(b)
|
||||
if (found) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const b in buckets.value) {
|
||||
findAndUpdate(b)
|
||||
if (found) {
|
||||
|
@ -176,10 +152,7 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
buckets.value[bucketIndex] = newBucket
|
||||
}
|
||||
|
||||
function addTasksToBucket({tasks, bucketId}: {
|
||||
tasks: ITask[];
|
||||
bucketId: IBucket['id'];
|
||||
}) {
|
||||
function addTasksToBucket(tasks: ITask[], bucketId: IBucket['id']) {
|
||||
const bucketIndex = findIndexById(buckets.value, bucketId)
|
||||
const oldBucket = buckets.value[bucketIndex]
|
||||
const newBucket = {
|
||||
|
@ -202,7 +175,6 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
|
||||
if (
|
||||
bucketIndex === null ||
|
||||
buckets.value[bucketIndex]?.id !== task.bucketId ||
|
||||
taskIndex === null ||
|
||||
(buckets.value[bucketIndex]?.tasks[taskIndex]?.id !== task.id)
|
||||
) {
|
||||
|
@ -225,15 +197,15 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
allTasksLoadedForBucket.value[bucketId] = true
|
||||
}
|
||||
|
||||
async function loadBucketsForProject({projectId, params}: { projectId: IProject['id'], params }) {
|
||||
async function loadBucketsForProject(projectId: IProject['id'], viewId: IProjectView['id'], params) {
|
||||
const cancel = setModuleLoading(setIsLoading)
|
||||
|
||||
// Clear everything to prevent having old buckets in the project if loading the buckets from this project takes a few moments
|
||||
setBuckets([])
|
||||
|
||||
const bucketService = new BucketService()
|
||||
const taskCollectionService = new TaskCollectionService()
|
||||
try {
|
||||
const newBuckets = await bucketService.getAll({projectId}, {
|
||||
const newBuckets = await taskCollectionService.getAll({projectId, viewId}, {
|
||||
...params,
|
||||
per_page: TASKS_PER_BUCKET,
|
||||
})
|
||||
|
@ -247,6 +219,7 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
|
||||
async function loadNextTasksForBucket(
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
ps: TaskFilterParams,
|
||||
bucketId: IBucket['id'],
|
||||
) {
|
||||
|
@ -267,7 +240,7 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
|
||||
const params: TaskFilterParams = JSON.parse(JSON.stringify(ps))
|
||||
|
||||
params.sort_by = ['kanban_position']
|
||||
params.sort_by = ['position']
|
||||
params.order_by = ['asc']
|
||||
params.filter = `${params.filter === '' ? '' : params.filter + ' && '}bucket_id = ${bucketId}`
|
||||
params.filter_timezone = authStore.settings.timezone
|
||||
|
@ -275,8 +248,8 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
|
||||
const taskService = new TaskCollectionService()
|
||||
try {
|
||||
const tasks = await taskService.getAll({projectId}, params, page)
|
||||
addTasksToBucket({tasks, bucketId: bucketId})
|
||||
const tasks = await taskService.getAll({projectId, viewId}, params, page)
|
||||
addTasksToBucket(tasks, bucketId)
|
||||
setTasksLoadedForBucketPage({bucketId, page})
|
||||
if (taskService.totalPages <= page) {
|
||||
setAllTasksLoadedForBucket(bucketId)
|
||||
|
@ -309,7 +282,7 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
const response = await bucketService.delete(bucket)
|
||||
removeBucket(bucket)
|
||||
// We reload all buckets because tasks are being moved from the deleted bucket
|
||||
loadBucketsForProject({projectId: bucket.projectId, params})
|
||||
loadBucketsForProject(bucket.projectId, bucket.projectViewId, params)
|
||||
return response
|
||||
} finally {
|
||||
cancel()
|
||||
|
@ -344,18 +317,6 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function updateBucketTitle({id, title}: { id: IBucket['id'], title: IBucket['title'] }) {
|
||||
const bucket = findById(buckets.value, id)
|
||||
|
||||
if (bucket?.title === title) {
|
||||
// bucket title has not changed
|
||||
return
|
||||
}
|
||||
|
||||
await updateBucket({id, title})
|
||||
success({message: i18n.global.t('project.kanban.bucketTitleSavedSuccess')})
|
||||
}
|
||||
|
||||
return {
|
||||
buckets,
|
||||
isLoading: readonly(isLoading),
|
||||
|
@ -374,7 +335,6 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||
createBucket,
|
||||
deleteBucket,
|
||||
updateBucket,
|
||||
updateBucketTitle,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import ProjectModel from '@/models/project'
|
|||
import {success} from '@/message'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {getSavedFilterIdFromProjectId} from '@/services/savedFilter'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
const {add, remove, search, update} = createNewIndexer('projects', ['title', 'description'])
|
||||
|
||||
|
@ -210,7 +211,27 @@ export const useProjectStore = defineStore('project', () => {
|
|||
project,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
function setProjectView(view: IProjectView) {
|
||||
const viewPos = projects.value[view.projectId].views.findIndex(v => v.id === view.id)
|
||||
if (viewPos !== -1) {
|
||||
projects.value[view.projectId].views[viewPos] = view
|
||||
setProject(projects.value[view.projectId])
|
||||
return
|
||||
}
|
||||
|
||||
projects.value[view.projectId].views.push(view)
|
||||
|
||||
setProject(projects.value[view.projectId])
|
||||
}
|
||||
|
||||
function removeProjectView(projectId: IProject['id'], viewId: IProjectView['id']) {
|
||||
const viewPos = projects.value[projectId].views.findIndex(v => v.id === viewId)
|
||||
if (viewPos !== -1) {
|
||||
projects.value[projectId].views.splice(viewPos, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isLoading: readonly(isLoading),
|
||||
projects: readonly(projects),
|
||||
|
@ -235,6 +256,8 @@ export const useProjectStore = defineStore('project', () => {
|
|||
updateProject,
|
||||
deleteProject,
|
||||
getAncestors,
|
||||
setProjectView,
|
||||
removeProjectView,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import {useKanbanStore} from '@/stores/kanban'
|
|||
import {useBaseStore} from '@/stores/base'
|
||||
import ProjectUserService from '@/services/projectUsers'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import TaskCollectionService, {type TaskFilterParams} from '@/services/taskCollection'
|
||||
import {type TaskFilterParams} from '@/services/taskCollection'
|
||||
import {getRandomColorHex} from '@/helpers/color/randomColor'
|
||||
|
||||
interface MatchedAssignee extends IUser {
|
||||
|
@ -124,21 +124,23 @@ export const useTaskStore = defineStore('task', () => {
|
|||
})
|
||||
}
|
||||
|
||||
async function loadTasks(params: TaskFilterParams, projectId: IProject['id'] | null = null) {
|
||||
async function loadTasks(
|
||||
params: TaskFilterParams,
|
||||
projectId: IProject['id'] | null = null,
|
||||
) {
|
||||
|
||||
if (!params.filter_timezone || params.filter_timezone === '') {
|
||||
params.filter_timezone = authStore.settings.timezone
|
||||
}
|
||||
|
||||
if (projectId !== null) {
|
||||
params.filter = 'project = '+projectId+' && (' + params.filter +')'
|
||||
}
|
||||
|
||||
const cancel = setModuleLoading(setIsLoading)
|
||||
try {
|
||||
if (projectId === null) {
|
||||
const taskService = new TaskService()
|
||||
tasks.value = await taskService.getAll({}, params)
|
||||
} else {
|
||||
const taskCollectionService = new TaskCollectionService()
|
||||
tasks.value = await taskCollectionService.getAll({projectId}, params)
|
||||
}
|
||||
const taskService = new TaskService()
|
||||
tasks.value = await taskService.getAll({}, params)
|
||||
baseStore.setHasTasks(tasks.value.length > 0)
|
||||
return tasks.value
|
||||
} finally {
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export const PROJECT_VIEWS = {
|
||||
LIST: 'list',
|
||||
GANTT: 'gantt',
|
||||
TABLE: 'table',
|
||||
KANBAN: 'kanban',
|
||||
} as const
|
||||
|
||||
export type ProjectView = typeof PROJECT_VIEWS[keyof typeof PROJECT_VIEWS]
|
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import {computed, watch} from 'vue'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
|
||||
import ProjectList from '@/components/project/views/ProjectList.vue'
|
||||
import ProjectGantt from '@/components/project/views/ProjectGantt.vue'
|
||||
import ProjectTable from '@/components/project/views/ProjectTable.vue'
|
||||
import ProjectKanban from '@/components/project/views/ProjectKanban.vue'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
projectId: number,
|
||||
viewId: number,
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const projectStore = useProjectStore()
|
||||
|
||||
const currentView = computed(() => {
|
||||
const project = projectStore.projects[projectId]
|
||||
|
||||
return project?.views.find(v => v.id === viewId)
|
||||
})
|
||||
|
||||
function redirectToFirstViewIfNecessary() {
|
||||
if (viewId === 0) {
|
||||
// Ideally, we would do that in the router redirect, but the projects (and therefore, the views)
|
||||
// are not always loaded then.
|
||||
const firstViewId = projectStore.projects[projectId]?.views[0].id
|
||||
if (firstViewId) {
|
||||
router.replace({
|
||||
name: 'project.view',
|
||||
params: {
|
||||
projectId,
|
||||
viewId: firstViewId,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => viewId,
|
||||
redirectToFirstViewIfNecessary,
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => projectStore.projects[projectId],
|
||||
redirectToFirstViewIfNecessary,
|
||||
)
|
||||
|
||||
const route = useRoute()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProjectList
|
||||
v-if="currentView?.viewKind === 'list'"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
/>
|
||||
<ProjectGantt
|
||||
v-if="currentView?.viewKind === 'gantt'"
|
||||
:route
|
||||
:view-id
|
||||
/>
|
||||
<ProjectTable
|
||||
v-if="currentView?.viewKind === 'table'"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
/>
|
||||
<ProjectKanban
|
||||
v-if="currentView?.viewKind === 'kanban'"
|
||||
:project-id="projectId"
|
||||
:view-id
|
||||
/>
|
||||
</template>
|
|
@ -12,10 +12,12 @@ import type {TaskFilterParams} from '@/services/taskCollection'
|
|||
|
||||
import type {DateISO} from '@/types/DateISO'
|
||||
import type {DateKebab} from '@/types/DateKebab'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
// convenient internal filter object
|
||||
export interface GanttFilters {
|
||||
projectId: IProject['id']
|
||||
viewId: IProjectView['id'],
|
||||
dateFrom: DateISO
|
||||
dateTo: DateISO
|
||||
showTasksWithoutDates: boolean
|
||||
|
@ -41,6 +43,7 @@ function ganttRouteToFilters(route: Partial<RouteLocationNormalized>): GanttFilt
|
|||
const ganttRoute = route
|
||||
return {
|
||||
projectId: Number(ganttRoute.params?.projectId),
|
||||
viewId: Number(ganttRoute.params?.viewId),
|
||||
dateFrom: parseDateProp(ganttRoute.query?.dateFrom as DateKebab) || getDefaultDateFrom(),
|
||||
dateTo: parseDateProp(ganttRoute.query?.dateTo as DateKebab) || getDefaultDateTo(),
|
||||
showTasksWithoutDates: parseBooleanProp(ganttRoute.query?.showTasksWithoutDates as string) || DEFAULT_SHOW_TASKS_WITHOUT_DATES,
|
||||
|
@ -69,8 +72,11 @@ function ganttFiltersToRoute(filters: GanttFilters): RouteLocationRaw {
|
|||
}
|
||||
|
||||
return {
|
||||
name: 'project.gantt',
|
||||
params: {projectId: filters.projectId},
|
||||
name: 'project.view',
|
||||
params: {
|
||||
projectId: filters.projectId,
|
||||
viewId: filters.viewId,
|
||||
},
|
||||
query,
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +94,7 @@ export type UseGanttFiltersReturn =
|
|||
ReturnType<typeof useRouteFilters<GanttFilters>> &
|
||||
ReturnType<typeof useGanttTaskList<GanttFilters>>
|
||||
|
||||
export function useGanttFilters(route: Ref<RouteLocationNormalized>): UseGanttFiltersReturn {
|
||||
export function useGanttFilters(route: Ref<RouteLocationNormalized>, viewId: IProjectView['id']): UseGanttFiltersReturn {
|
||||
const {
|
||||
filters,
|
||||
hasDefaultFilters,
|
||||
|
@ -98,7 +104,7 @@ export function useGanttFilters(route: Ref<RouteLocationNormalized>): UseGanttFi
|
|||
ganttGetDefaultFilters,
|
||||
ganttRouteToFilters,
|
||||
ganttFiltersToRoute,
|
||||
['project.gantt'],
|
||||
['project.view'],
|
||||
)
|
||||
|
||||
const {
|
||||
|
@ -108,7 +114,7 @@ export function useGanttFilters(route: Ref<RouteLocationNormalized>): UseGanttFi
|
|||
isLoading,
|
||||
addTask,
|
||||
updateTask,
|
||||
} = useGanttTaskList<GanttFilters>(filters, ganttFiltersToApiParams)
|
||||
} = useGanttTaskList<GanttFilters>(filters, ganttFiltersToApiParams, viewId)
|
||||
|
||||
return {
|
||||
filters,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {computed, ref, shallowReactive, watch, type Ref} from 'vue'
|
||||
import {computed, ref, type Ref, shallowReactive, watch} from 'vue'
|
||||
import {klona} from 'klona/lite'
|
||||
|
||||
import type {Filters} from '@/composables/useRouteFilters'
|
||||
|
@ -10,16 +10,15 @@ import TaskService from '@/services/task'
|
|||
import TaskModel from '@/models/task'
|
||||
import {error, success} from '@/message'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
// FIXME: unify with general `useTaskList`
|
||||
export function useGanttTaskList<F extends Filters>(
|
||||
filters: Ref<F>,
|
||||
filterToApiParams: (filters: F) => TaskFilterParams,
|
||||
options: {
|
||||
loadAll?: boolean,
|
||||
} = {
|
||||
loadAll: true,
|
||||
}) {
|
||||
viewId: IProjectView['id'],
|
||||
loadAll: boolean = true,
|
||||
) {
|
||||
const taskCollectionService = shallowReactive(new TaskCollectionService())
|
||||
const taskService = shallowReactive(new TaskService())
|
||||
const authStore = useAuthStore()
|
||||
|
@ -29,13 +28,13 @@ export function useGanttTaskList<F extends Filters>(
|
|||
const tasks = ref<Map<ITask['id'], ITask>>(new Map())
|
||||
|
||||
async function fetchTasks(params: TaskFilterParams, page = 1): Promise<ITask[]> {
|
||||
|
||||
if(params.filter_timezone === '') {
|
||||
|
||||
if (params.filter_timezone === '') {
|
||||
params.filter_timezone = authStore.settings.timezone
|
||||
}
|
||||
|
||||
const tasks = await taskCollectionService.getAll({projectId: filters.value.projectId}, params, page) as ITask[]
|
||||
if (options.loadAll && page < taskCollectionService.totalPages) {
|
||||
const tasks = await taskCollectionService.getAll({projectId: filters.value.projectId, viewId}, params, page) as ITask[]
|
||||
if (loadAll && page < taskCollectionService.totalPages) {
|
||||
const nextTasks = await fetchTasks(params, page + 1)
|
||||
return tasks.concat(nextTasks)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
<script setup lang="ts">
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import {computed, ref} from 'vue'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import ProjectViewModel from '@/models/projectView'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import ViewEditForm from '@/components/project/views/viewEditForm.vue'
|
||||
import ProjectViewService from '@/services/projectViews'
|
||||
import XButton from '@/components/input/button.vue'
|
||||
import {error, success} from '@/message'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
} = defineProps<{
|
||||
projectId: number
|
||||
}>()
|
||||
|
||||
const projectStore = useProjectStore()
|
||||
const {t} = useI18n()
|
||||
|
||||
const views = computed(() => projectStore.projects[projectId]?.views)
|
||||
const showCreateForm = ref(false)
|
||||
|
||||
const projectViewService = ref(new ProjectViewService())
|
||||
const newView = ref<IProjectView>(new ProjectViewModel({}))
|
||||
const viewIdToDelete = ref<number | null>(null)
|
||||
const showDeleteModal = ref(false)
|
||||
const viewToEdit = ref<IProjectView | null>(null)
|
||||
|
||||
async function createView() {
|
||||
if (!showCreateForm.value) {
|
||||
showCreateForm.value = true
|
||||
return
|
||||
}
|
||||
|
||||
if (newView.value.title === '') {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
newView.value.bucketConfigurationMode = newView.value.viewKind === 'kanban'
|
||||
? newView.value.bucketConfigurationMode
|
||||
: 'none'
|
||||
newView.value.projectId = projectId
|
||||
|
||||
const result: IProjectView = await projectViewService.value.create(newView.value)
|
||||
success({message: t('project.views.createSuccess')})
|
||||
showCreateForm.value = false
|
||||
projectStore.setProjectView(result)
|
||||
newView.value = new ProjectViewModel({})
|
||||
} catch (e) {
|
||||
error(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteView() {
|
||||
if (!viewIdToDelete.value) {
|
||||
return
|
||||
}
|
||||
|
||||
await projectViewService.value.delete(new ProjectViewModel({
|
||||
id: viewIdToDelete.value,
|
||||
projectId,
|
||||
}))
|
||||
|
||||
projectStore.removeProjectView(projectId, viewIdToDelete.value)
|
||||
|
||||
showDeleteModal.value = false
|
||||
}
|
||||
|
||||
async function saveView() {
|
||||
if (viewToEdit.value?.viewKind !== 'kanban') {
|
||||
viewToEdit.value.bucketConfigurationMode = 'none'
|
||||
}
|
||||
const result = await projectViewService.value.update(viewToEdit.value)
|
||||
projectStore.setProjectView(result)
|
||||
viewToEdit.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CreateEdit
|
||||
:title="$t('project.views.header')"
|
||||
:primary-label="$t('misc.save')"
|
||||
>
|
||||
<ViewEditForm
|
||||
v-if="showCreateForm"
|
||||
v-model="newView"
|
||||
class="mb-4"
|
||||
/>
|
||||
<div class="is-flex is-justify-content-end">
|
||||
<XButton
|
||||
:loading="projectViewService.loading"
|
||||
@click="createView"
|
||||
>
|
||||
{{ $t('project.views.create') }}
|
||||
</XButton>
|
||||
</div>
|
||||
|
||||
<table
|
||||
v-if="views?.length > 0"
|
||||
class="table has-actions is-striped is-hoverable is-fullwidth"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('project.views.title') }}</th>
|
||||
<th>{{ $t('project.views.kind') }}</th>
|
||||
<th class="has-text-right">
|
||||
{{ $t('project.views.actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="v in views"
|
||||
:key="v.id"
|
||||
>
|
||||
<template v-if="viewToEdit !== null && viewToEdit.id === v.id">
|
||||
<td colspan="3">
|
||||
<ViewEditForm
|
||||
v-model="viewToEdit"
|
||||
class="mb-4"
|
||||
/>
|
||||
<div class="is-flex is-justify-content-end">
|
||||
<XButton
|
||||
variant="tertiary"
|
||||
class="mr-2"
|
||||
@click="viewToEdit = null"
|
||||
>
|
||||
{{ $t('misc.cancel') }}
|
||||
</XButton>
|
||||
<XButton
|
||||
:loading="projectViewService.loading"
|
||||
@click="saveView"
|
||||
>
|
||||
{{ $t('misc.save') }}
|
||||
</XButton>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td>{{ v.title }}</td>
|
||||
<td>{{ v.viewKind }}</td>
|
||||
<td class="has-text-right">
|
||||
<XButton
|
||||
class="is-danger mr-2"
|
||||
icon="trash-alt"
|
||||
@click="() => {
|
||||
viewIdToDelete = v.id
|
||||
showDeleteModal = true
|
||||
}"
|
||||
/>
|
||||
<XButton
|
||||
icon="pen"
|
||||
@click="viewToEdit = {...v}"
|
||||
/>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</CreateEdit>
|
||||
|
||||
<modal
|
||||
:enabled="showDeleteModal"
|
||||
@close="showDeleteModal = false"
|
||||
@submit="deleteView"
|
||||
>
|
||||
<template #header>
|
||||
<span>{{ $t('project.views.delete') }}</span>
|
||||
</template>
|
||||
|
||||
<template #text>
|
||||
<p>{{ $t('project.views.deleteText') }}</p>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
|
@ -49,7 +49,6 @@ import {useI18n} from 'vue-i18n'
|
|||
import {useTitle} from '@vueuse/core'
|
||||
|
||||
import Message from '@/components/misc/message.vue'
|
||||
import {PROJECT_VIEWS, type ProjectView} from '@/types/ProjectView'
|
||||
import {LINK_SHARE_HASH_PREFIX} from '@/constants/linkShareHash'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
@ -96,10 +95,6 @@ function useAuth() {
|
|||
: true
|
||||
baseStore.setLogoVisible(logoVisible)
|
||||
|
||||
const view = route.query.view && Object.values(PROJECT_VIEWS).includes(route.query.view as ProjectView)
|
||||
? route.query.view
|
||||
: 'list'
|
||||
|
||||
const hash = LINK_SHARE_HASH_PREFIX + route.params.share
|
||||
|
||||
const last = getLastVisitedRoute()
|
||||
|
@ -111,8 +106,10 @@ function useAuth() {
|
|||
}
|
||||
|
||||
return router.push({
|
||||
name: `project.${view}`,
|
||||
params: {projectId},
|
||||
name: 'project.index',
|
||||
params: {
|
||||
projectId,
|
||||
},
|
||||
hash,
|
||||
})
|
||||
} catch (e) {
|
||||
|
|
9
go.mod
9
go.mod
|
@ -20,12 +20,12 @@ require (
|
|||
code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3
|
||||
dario.cat/mergo v1.0.0
|
||||
github.com/ThreeDotsLabs/watermill v1.3.5
|
||||
github.com/adlio/trello v1.10.0
|
||||
github.com/adlio/trello v1.11.0
|
||||
github.com/arran4/golang-ical v0.2.7
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/bbrks/go-blurhash v1.1.1
|
||||
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
|
||||
github.com/coreos/go-oidc/v3 v3.9.0
|
||||
github.com/coreos/go-oidc/v3 v3.10.0
|
||||
github.com/cweill/gotests v1.6.0
|
||||
github.com/d4l3k/messagediff v1.2.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
|
@ -80,7 +80,7 @@ require (
|
|||
src.techknowlogick.com/xgo v1.7.1-0.20240305180710-770b8eae9cec
|
||||
src.techknowlogick.com/xormigrate v1.7.1
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.8
|
||||
xorm.io/xorm v1.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -109,6 +109,7 @@ require (
|
|||
github.com/go-faster/city v1.0.1 // indirect
|
||||
github.com/go-faster/errors v0.6.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.3 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
|
@ -190,5 +191,3 @@ replace github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20
|
|||
go 1.21
|
||||
|
||||
toolchain go1.21.2
|
||||
|
||||
replace github.com/adlio/trello => github.com/kolaente/trello v1.8.1-0.20240310152004-14ccae2ddc51
|
||||
|
|
10
go.sum
10
go.sum
|
@ -24,6 +24,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
|||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/ThreeDotsLabs/watermill v1.3.5 h1:50JEPEhMGZQMh08ct0tfO1PsgMOAOhV3zxK2WofkbXg=
|
||||
github.com/ThreeDotsLabs/watermill v1.3.5/go.mod h1:O/u/Ptyrk5MPTxSeWM5vzTtZcZfxXfO9PK9eXTYiFZY=
|
||||
github.com/adlio/trello v1.11.0 h1:PGpwpRZcRiVhsG7VEHb2GWKw4R2ZxB9nc6cMI/7mLD8=
|
||||
github.com/adlio/trello v1.11.0/go.mod h1:I4Lti4jf2KxjTNgTqs5W3lLuE78QZZdYbbPnQQGwjOo=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
|
@ -68,6 +70,8 @@ github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMn
|
|||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
|
||||
github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
|
||||
github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU=
|
||||
github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
|
@ -125,6 +129,8 @@ github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06F
|
|||
github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
|
@ -299,8 +305,6 @@ github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
|
|||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kolaente/caldav-go v3.0.1-0.20190610114120-2a4eb8b5dcc9+incompatible h1:q7DbyV+sFjEoTuuUdRDNl2nlyfztkZgxVVCV7JhzIkY=
|
||||
github.com/kolaente/caldav-go v3.0.1-0.20190610114120-2a4eb8b5dcc9+incompatible/go.mod h1:y1UhTNI4g0hVymJrI6yJ5/ohy09hNBeU8iJEZjgdDOw=
|
||||
github.com/kolaente/trello v1.8.1-0.20240310152004-14ccae2ddc51 h1:R8xiJ/zSWOndiUjG03GmkkIm1O8MDKt2av0SeaIZy/c=
|
||||
github.com/kolaente/trello v1.8.1-0.20240310152004-14ccae2ddc51/go.mod h1:I4Lti4jf2KxjTNgTqs5W3lLuE78QZZdYbbPnQQGwjOo=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
|
@ -792,3 +796,5 @@ xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
|||
xorm.io/xorm v1.3.3/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo=
|
||||
xorm.io/xorm v1.3.8 h1:CJmplmWqfSRpLWSPMmqz+so8toBp3m7ehuRehIWedZo=
|
||||
xorm.io/xorm v1.3.8/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
|
||||
xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU=
|
||||
xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- id: 1
|
||||
title: testbucket1
|
||||
project_id: 1
|
||||
project_view_id: 4
|
||||
created_by_id: 1
|
||||
limit: 9999999 # This bucket has a limit we will never exceed in the tests to make sure the logic allows for buckets with limits
|
||||
position: 1
|
||||
|
@ -8,7 +8,7 @@
|
|||
updated: 2020-04-18 21:13:52
|
||||
- id: 2
|
||||
title: testbucket2
|
||||
project_id: 1
|
||||
project_view_id: 4
|
||||
created_by_id: 1
|
||||
limit: 3
|
||||
position: 2
|
||||
|
@ -16,14 +16,14 @@
|
|||
updated: 2020-04-18 21:13:52
|
||||
- id: 3
|
||||
title: testbucket3
|
||||
project_id: 1
|
||||
project_view_id: 4
|
||||
created_by_id: 1
|
||||
position: 3
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 4
|
||||
title: testbucket4 - other project
|
||||
project_id: 2
|
||||
project_view_id: 8
|
||||
created_by_id: 1
|
||||
position: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
|
@ -31,221 +31,221 @@
|
|||
# The following are not or only partly owned by user 1
|
||||
- id: 5
|
||||
title: testbucket5
|
||||
project_id: 20
|
||||
project_view_id: 80
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 6
|
||||
title: testbucket6
|
||||
project_id: 6
|
||||
project_view_id: 24
|
||||
created_by_id: 1
|
||||
position: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 7
|
||||
title: testbucket7
|
||||
project_id: 7
|
||||
project_view_id: 28
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 8
|
||||
title: testbucket8
|
||||
project_id: 8
|
||||
project_view_id: 32
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 9
|
||||
title: testbucket9
|
||||
project_id: 9
|
||||
project_view_id: 36
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 10
|
||||
title: testbucket10
|
||||
project_id: 10
|
||||
project_view_id: 40
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 11
|
||||
title: testbucket11
|
||||
project_id: 11
|
||||
project_view_id: 44
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 12
|
||||
title: testbucket13
|
||||
project_id: 12
|
||||
project_view_id: 48
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 13
|
||||
title: testbucket13
|
||||
project_id: 13
|
||||
project_view_id: 52
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 14
|
||||
title: testbucket14
|
||||
project_id: 14
|
||||
project_view_id: 56
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 15
|
||||
title: testbucket15
|
||||
project_id: 15
|
||||
project_view_id: 60
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 16
|
||||
title: testbucket16
|
||||
project_id: 16
|
||||
project_view_id: 64
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 17
|
||||
title: testbucket17
|
||||
project_id: 17
|
||||
project_view_id: 68
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 18
|
||||
title: testbucket18
|
||||
project_id: 5
|
||||
project_view_id: 20
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 19
|
||||
title: testbucket19
|
||||
project_id: 21
|
||||
project_view_id: 84
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 20
|
||||
title: testbucket20
|
||||
project_id: 22
|
||||
project_view_id: 88
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 21
|
||||
title: testbucket21
|
||||
project_id: 3
|
||||
project_view_id: 12
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
# Duplicate buckets to make deletion of one of them possible
|
||||
- id: 22
|
||||
title: testbucket22
|
||||
project_id: 6
|
||||
project_view_id: 24
|
||||
created_by_id: 1
|
||||
position: 2
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 23
|
||||
title: testbucket23
|
||||
project_id: 7
|
||||
project_view_id: 28
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 24
|
||||
title: testbucket24
|
||||
project_id: 8
|
||||
project_view_id: 32
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 25
|
||||
title: testbucket25
|
||||
project_id: 9
|
||||
project_view_id: 36
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 26
|
||||
title: testbucket26
|
||||
project_id: 10
|
||||
project_view_id: 40
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 27
|
||||
title: testbucket27
|
||||
project_id: 11
|
||||
project_view_id: 44
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 28
|
||||
title: testbucket28
|
||||
project_id: 12
|
||||
project_view_id: 48
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 29
|
||||
title: testbucket29
|
||||
project_id: 13
|
||||
project_view_id: 52
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 30
|
||||
title: testbucket30
|
||||
project_id: 14
|
||||
project_view_id: 56
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 31
|
||||
title: testbucket31
|
||||
project_id: 15
|
||||
project_view_id: 60
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 32
|
||||
title: testbucket32
|
||||
project_id: 16
|
||||
project_view_id: 64
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 33
|
||||
title: testbucket33
|
||||
project_id: 17
|
||||
project_view_id: 68
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
# This bucket is the last one in its project
|
||||
- id: 34
|
||||
title: testbucket34
|
||||
project_id: 18
|
||||
project_view_id: 72
|
||||
created_by_id: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 35
|
||||
title: testbucket35
|
||||
project_id: 23
|
||||
project_view_id: 92
|
||||
created_by_id: -2
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 36
|
||||
title: testbucket36
|
||||
project_id: 33
|
||||
project_view_id: 132
|
||||
created_by_id: 6
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 37
|
||||
title: testbucket37
|
||||
project_id: 34
|
||||
project_view_id: 136
|
||||
created_by_id: 6
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 38
|
||||
title: testbucket36
|
||||
project_id: 36
|
||||
project_view_id: 144
|
||||
created_by_id: 15
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 39
|
||||
title: testbucket38
|
||||
project_id: 38
|
||||
project_view_id: 152
|
||||
created_by_id: 15
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 40
|
||||
title: testbucket40
|
||||
project_id: 2
|
||||
project_view_id: 8
|
||||
created_by_id: 1
|
||||
position: 10
|
||||
created: 2020-04-18 21:13:52
|
||||
|
|
|
@ -0,0 +1,954 @@
|
|||
- id: 1
|
||||
title: List
|
||||
project_id: 1
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 2
|
||||
title: Gantt
|
||||
project_id: 1
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 3
|
||||
title: Table
|
||||
project_id: 1
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 4
|
||||
title: Kanban
|
||||
project_id: 1
|
||||
view_kind: 3
|
||||
done_bucket_id: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 5
|
||||
title: List
|
||||
project_id: 2
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 6
|
||||
title: Gantt
|
||||
project_id: 2
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 7
|
||||
title: Table
|
||||
project_id: 2
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 8
|
||||
title: Kanban
|
||||
project_id: 2
|
||||
view_kind: 3
|
||||
done_bucket_id: 4
|
||||
default_bucket_id: 40
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 9
|
||||
title: List
|
||||
project_id: 3
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 10
|
||||
title: Gantt
|
||||
project_id: 3
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 11
|
||||
title: Table
|
||||
project_id: 3
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 12
|
||||
title: Kanban
|
||||
project_id: 3
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 13
|
||||
title: List
|
||||
project_id: 4
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 14
|
||||
title: Gantt
|
||||
project_id: 4
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 15
|
||||
title: Table
|
||||
project_id: 4
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 16
|
||||
title: Kanban
|
||||
project_id: 4
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 17
|
||||
title: List
|
||||
project_id: 5
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 18
|
||||
title: Gantt
|
||||
project_id: 5
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 19
|
||||
title: Table
|
||||
project_id: 5
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 20
|
||||
title: Kanban
|
||||
project_id: 5
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 21
|
||||
title: List
|
||||
project_id: 6
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 22
|
||||
title: Gantt
|
||||
project_id: 6
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 23
|
||||
title: Table
|
||||
project_id: 6
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 24
|
||||
title: Kanban
|
||||
project_id: 6
|
||||
view_kind: 3
|
||||
default_bucket_id: 22
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 25
|
||||
title: List
|
||||
project_id: 7
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 26
|
||||
title: Gantt
|
||||
project_id: 7
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 27
|
||||
title: Table
|
||||
project_id: 7
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 28
|
||||
title: Kanban
|
||||
project_id: 7
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 29
|
||||
title: List
|
||||
project_id: 8
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 30
|
||||
title: Gantt
|
||||
project_id: 8
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 31
|
||||
title: Table
|
||||
project_id: 8
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 32
|
||||
title: Kanban
|
||||
project_id: 8
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 33
|
||||
title: List
|
||||
project_id: 9
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 34
|
||||
title: Gantt
|
||||
project_id: 9
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 35
|
||||
title: Table
|
||||
project_id: 9
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 36
|
||||
title: Kanban
|
||||
project_id: 9
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 37
|
||||
title: List
|
||||
project_id: 10
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 38
|
||||
title: Gantt
|
||||
project_id: 10
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 39
|
||||
title: Table
|
||||
project_id: 10
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 40
|
||||
title: Kanban
|
||||
project_id: 10
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 41
|
||||
title: List
|
||||
project_id: 11
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 42
|
||||
title: Gantt
|
||||
project_id: 11
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 43
|
||||
title: Table
|
||||
project_id: 11
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 44
|
||||
title: Kanban
|
||||
project_id: 11
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 45
|
||||
title: List
|
||||
project_id: 12
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 46
|
||||
title: Gantt
|
||||
project_id: 12
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 47
|
||||
title: Table
|
||||
project_id: 12
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 48
|
||||
title: Kanban
|
||||
project_id: 12
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 49
|
||||
title: List
|
||||
project_id: 13
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 50
|
||||
title: Gantt
|
||||
project_id: 13
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 51
|
||||
title: Table
|
||||
project_id: 13
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 52
|
||||
title: Kanban
|
||||
project_id: 13
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 53
|
||||
title: List
|
||||
project_id: 14
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 54
|
||||
title: Gantt
|
||||
project_id: 14
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 55
|
||||
title: Table
|
||||
project_id: 14
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 56
|
||||
title: Kanban
|
||||
project_id: 14
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 57
|
||||
title: List
|
||||
project_id: 15
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 58
|
||||
title: Gantt
|
||||
project_id: 15
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 59
|
||||
title: Table
|
||||
project_id: 15
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 60
|
||||
title: Kanban
|
||||
project_id: 15
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 61
|
||||
title: List
|
||||
project_id: 16
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 62
|
||||
title: Gantt
|
||||
project_id: 16
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 63
|
||||
title: Table
|
||||
project_id: 16
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 64
|
||||
title: Kanban
|
||||
project_id: 16
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 65
|
||||
title: List
|
||||
project_id: 17
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 66
|
||||
title: Gantt
|
||||
project_id: 17
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 67
|
||||
title: Table
|
||||
project_id: 17
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 68
|
||||
title: Kanban
|
||||
project_id: 17
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 69
|
||||
title: List
|
||||
project_id: 18
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 70
|
||||
title: Gantt
|
||||
project_id: 18
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 71
|
||||
title: Table
|
||||
project_id: 18
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 72
|
||||
title: Kanban
|
||||
project_id: 18
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 73
|
||||
title: List
|
||||
project_id: 19
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 74
|
||||
title: Gantt
|
||||
project_id: 19
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 75
|
||||
title: Table
|
||||
project_id: 19
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 76
|
||||
title: Kanban
|
||||
project_id: 19
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 77
|
||||
title: List
|
||||
project_id: 20
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 78
|
||||
title: Gantt
|
||||
project_id: 20
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 79
|
||||
title: Table
|
||||
project_id: 20
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 80
|
||||
title: Kanban
|
||||
project_id: 20
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 81
|
||||
title: List
|
||||
project_id: 21
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 82
|
||||
title: Gantt
|
||||
project_id: 21
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 83
|
||||
title: Table
|
||||
project_id: 21
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 84
|
||||
title: Kanban
|
||||
project_id: 21
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 85
|
||||
title: List
|
||||
project_id: 22
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 86
|
||||
title: Gantt
|
||||
project_id: 22
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 87
|
||||
title: Table
|
||||
project_id: 22
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 88
|
||||
title: Kanban
|
||||
project_id: 22
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 89
|
||||
title: List
|
||||
project_id: 23
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 90
|
||||
title: Gantt
|
||||
project_id: 23
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 91
|
||||
title: Table
|
||||
project_id: 23
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 92
|
||||
title: Kanban
|
||||
project_id: 23
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 93
|
||||
title: List
|
||||
project_id: 24
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 94
|
||||
title: Gantt
|
||||
project_id: 24
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 95
|
||||
title: Table
|
||||
project_id: 24
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 96
|
||||
title: Kanban
|
||||
project_id: 24
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 97
|
||||
title: List
|
||||
project_id: 25
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 98
|
||||
title: Gantt
|
||||
project_id: 25
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 99
|
||||
title: Table
|
||||
project_id: 25
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 100
|
||||
title: Kanban
|
||||
project_id: 25
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 101
|
||||
title: List
|
||||
project_id: 26
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 102
|
||||
title: Gantt
|
||||
project_id: 26
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 103
|
||||
title: Table
|
||||
project_id: 26
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 104
|
||||
title: Kanban
|
||||
project_id: 26
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 105
|
||||
title: List
|
||||
project_id: 27
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 106
|
||||
title: Gantt
|
||||
project_id: 27
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 107
|
||||
title: Table
|
||||
project_id: 27
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 108
|
||||
title: Kanban
|
||||
project_id: 27
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 109
|
||||
title: List
|
||||
project_id: 28
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 110
|
||||
title: Gantt
|
||||
project_id: 28
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 111
|
||||
title: Table
|
||||
project_id: 28
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 112
|
||||
title: Kanban
|
||||
project_id: 28
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 113
|
||||
title: List
|
||||
project_id: 29
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 114
|
||||
title: Gantt
|
||||
project_id: 29
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 115
|
||||
title: Table
|
||||
project_id: 29
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 116
|
||||
title: Kanban
|
||||
project_id: 29
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 117
|
||||
title: List
|
||||
project_id: 30
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 118
|
||||
title: Gantt
|
||||
project_id: 30
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 119
|
||||
title: Table
|
||||
project_id: 30
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 120
|
||||
title: Kanban
|
||||
project_id: 30
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 121
|
||||
title: List
|
||||
project_id: 31
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 122
|
||||
title: Gantt
|
||||
project_id: 31
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 123
|
||||
title: Table
|
||||
project_id: 31
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 124
|
||||
title: Kanban
|
||||
project_id: 31
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 125
|
||||
title: List
|
||||
project_id: 32
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 126
|
||||
title: Gantt
|
||||
project_id: 32
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 127
|
||||
title: Table
|
||||
project_id: 32
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 128
|
||||
title: Kanban
|
||||
project_id: 32
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 129
|
||||
title: List
|
||||
project_id: 33
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 130
|
||||
title: Gantt
|
||||
project_id: 33
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 131
|
||||
title: Table
|
||||
project_id: 33
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 132
|
||||
title: Kanban
|
||||
project_id: 33
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 133
|
||||
title: List
|
||||
project_id: 34
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 134
|
||||
title: Gantt
|
||||
project_id: 34
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 135
|
||||
title: Table
|
||||
project_id: 34
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 136
|
||||
title: Kanban
|
||||
project_id: 34
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 137
|
||||
title: List
|
||||
project_id: 35
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 138
|
||||
title: Gantt
|
||||
project_id: 35
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 139
|
||||
title: Table
|
||||
project_id: 35
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 140
|
||||
title: Kanban
|
||||
project_id: 35
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 141
|
||||
title: List
|
||||
project_id: 36
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 142
|
||||
title: Gantt
|
||||
project_id: 36
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 143
|
||||
title: Table
|
||||
project_id: 36
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 144
|
||||
title: Kanban
|
||||
project_id: 36
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 145
|
||||
title: List
|
||||
project_id: 37
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 146
|
||||
title: Gantt
|
||||
project_id: 37
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 147
|
||||
title: Table
|
||||
project_id: 37
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 148
|
||||
title: Kanban
|
||||
project_id: 37
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
||||
- id: 149
|
||||
title: List
|
||||
project_id: 38
|
||||
view_kind: 0
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 150
|
||||
title: Gantt
|
||||
project_id: 38
|
||||
view_kind: 1
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 151
|
||||
title: Table
|
||||
project_id: 38
|
||||
view_kind: 2
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
- id: 152
|
||||
title: Kanban
|
||||
project_id: 38
|
||||
view_kind: 3
|
||||
updated: '2024-03-18 15:14:13'
|
||||
created: '2018-03-18 15:14:13'
|
||||
bucket_configuration_mode: 1
|
|
@ -5,7 +5,6 @@
|
|||
identifier: test1
|
||||
owner_id: 1
|
||||
position: 3
|
||||
done_bucket_id: 3
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
|
@ -15,8 +14,6 @@
|
|||
identifier: test2
|
||||
owner_id: 3
|
||||
position: 2
|
||||
done_bucket_id: 4
|
||||
default_bucket_id: 40
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
|
@ -53,7 +50,6 @@
|
|||
identifier: test6
|
||||
owner_id: 6
|
||||
position: 6
|
||||
default_bucket_id: 22
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
- task_id: 1
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 2
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 3
|
||||
project_view_id: 4
|
||||
bucket_id: 2
|
||||
- task_id: 4
|
||||
project_view_id: 4
|
||||
bucket_id: 2
|
||||
- task_id: 5
|
||||
project_view_id: 4
|
||||
bucket_id: 2
|
||||
- task_id: 6
|
||||
project_view_id: 4
|
||||
bucket_id: 3
|
||||
- task_id: 7
|
||||
project_view_id: 4
|
||||
bucket_id: 3
|
||||
- task_id: 8
|
||||
project_view_id: 4
|
||||
bucket_id: 3
|
||||
- task_id: 9
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 10
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 11
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 12
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 13
|
||||
project_view_id: 8
|
||||
bucket_id: 4
|
||||
- task_id: 14
|
||||
project_view_id: 20
|
||||
bucket_id: 18
|
||||
- task_id: 15
|
||||
project_view_id: 24
|
||||
bucket_id: 6
|
||||
- task_id: 16
|
||||
project_view_id: 28
|
||||
bucket_id: 7
|
||||
- task_id: 17
|
||||
project_view_id: 32
|
||||
bucket_id: 8
|
||||
- task_id: 18
|
||||
project_view_id: 36
|
||||
bucket_id: 9
|
||||
- task_id: 19
|
||||
project_view_id: 40
|
||||
bucket_id: 10
|
||||
- task_id: 20
|
||||
project_view_id: 44
|
||||
bucket_id: 11
|
||||
- task_id: 21
|
||||
project_view_id: 128
|
||||
bucket_id: 12
|
||||
- task_id: 22
|
||||
project_view_id: 132
|
||||
bucket_id: 36
|
||||
- task_id: 23
|
||||
project_view_id: 136
|
||||
bucket_id: 37
|
||||
- task_id: 24
|
||||
project_view_id: 60
|
||||
bucket_id: 15
|
||||
- task_id: 25
|
||||
project_view_id: 64
|
||||
bucket_id: 16
|
||||
- task_id: 26
|
||||
project_view_id: 68
|
||||
bucket_id: 17
|
||||
- task_id: 27
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 28
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 29
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 30
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 31
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 32
|
||||
project_view_id: 12
|
||||
bucket_id: 21
|
||||
- task_id: 33
|
||||
project_view_id: 4
|
||||
bucket_id: 1
|
||||
- task_id: 34
|
||||
project_view_id: 80
|
||||
bucket_id: 5
|
||||
- task_id: 35
|
||||
project_view_id: 84
|
||||
bucket_id: 19
|
||||
- task_id: 36
|
||||
project_view_id: 88
|
||||
bucket_id: 20
|
||||
#- task_id: 37
|
||||
# project_view_id: 8
|
||||
# bucket_id: null
|
||||
#- task_id: 38
|
||||
# project_view_id: 88
|
||||
# bucket_id: null
|
||||
#- task_id: 39
|
||||
# project_view_id: 100
|
||||
# bucket_id: null
|
||||
- task_id: 40
|
||||
project_view_id: 144
|
||||
bucket_id: 38
|
||||
- task_id: 41
|
||||
project_view_id: 144
|
||||
bucket_id: 38
|
||||
- task_id: 42
|
||||
project_view_id: 144
|
||||
bucket_id: 38
|
||||
- task_id: 43
|
||||
project_view_id: 144
|
||||
bucket_id: 38
|
||||
- task_id: 44
|
||||
project_view_id: 152
|
||||
bucket_id: 38
|
||||
- task_id: 45
|
||||
project_view_id: 144
|
||||
bucket_id: 38
|
||||
- task_id: 46
|
||||
project_view_id: 152
|
||||
bucket_id: 38
|
|
@ -0,0 +1,138 @@
|
|||
- task_id: 1
|
||||
project_view_id: 1
|
||||
position: 2
|
||||
- task_id: 2
|
||||
project_view_id: 1
|
||||
position: 4
|
||||
#- task_id: 3
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 4
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 5
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 6
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 7
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 8
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 9
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 10
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 11
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 12
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 13
|
||||
# project_view_id: 2
|
||||
# position: null
|
||||
#- task_id: 14
|
||||
# project_view_id: 5
|
||||
# position: null
|
||||
#- task_id: 15
|
||||
# project_view_id: 6
|
||||
# position: null
|
||||
#- task_id: 16
|
||||
# project_view_id: 7
|
||||
# position: null
|
||||
#- task_id: 17
|
||||
# project_view_id: 8
|
||||
# position: null
|
||||
#- task_id: 18
|
||||
# project_view_id: 9
|
||||
# position: null
|
||||
#- task_id: 19
|
||||
# project_view_id: 10
|
||||
# position: null
|
||||
#- task_id: 20
|
||||
# project_view_id: 11
|
||||
# position: null
|
||||
#- task_id: 21
|
||||
# project_view_id: 32
|
||||
# position: null
|
||||
#- task_id: 22
|
||||
# project_view_id: 33
|
||||
# position: null
|
||||
#- task_id: 23
|
||||
# project_view_id: 34
|
||||
# position: null
|
||||
#- task_id: 24
|
||||
# project_view_id: 15
|
||||
# position: null
|
||||
#- task_id: 25
|
||||
# project_view_id: 16
|
||||
# position: null
|
||||
#- task_id: 26
|
||||
# project_view_id: 17
|
||||
# position: null
|
||||
#- task_id: 27
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 28
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 29
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 30
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 31
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 32
|
||||
# project_view_id: 3
|
||||
# position: null
|
||||
#- task_id: 33
|
||||
# project_view_id: 1
|
||||
# position: null
|
||||
#- task_id: 34
|
||||
# project_view_id: 20
|
||||
# position: null
|
||||
- task_id: 35
|
||||
project_view_id: 21
|
||||
position: 0
|
||||
#- task_id: 36
|
||||
# project_view_id: 22
|
||||
# position: null
|
||||
#- task_id: 37
|
||||
# project_view_id: 2
|
||||
# position: null
|
||||
#- task_id: 38
|
||||
# project_view_id: 22
|
||||
# position: null
|
||||
- task_id: 39
|
||||
project_view_id: 25
|
||||
position: 0
|
||||
- task_id: 40
|
||||
project_view_id: 36
|
||||
position: 39
|
||||
- task_id: 41
|
||||
project_view_id: 36
|
||||
position: 40
|
||||
- task_id: 42
|
||||
project_view_id: 36
|
||||
position: 41
|
||||
- task_id: 43
|
||||
project_view_id: 36
|
||||
position: 42
|
||||
- task_id: 44
|
||||
project_view_id: 38
|
||||
position: 43
|
||||
- task_id: 45
|
||||
project_view_id: 36
|
||||
position: 44
|
||||
- task_id: 46
|
||||
project_view_id: 38
|
||||
position: 45
|
|
@ -7,8 +7,6 @@
|
|||
index: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 1
|
||||
position: 2
|
||||
- id: 2
|
||||
title: 'task #2 done'
|
||||
done: true
|
||||
|
@ -17,8 +15,6 @@
|
|||
index: 2
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 1
|
||||
position: 4
|
||||
- id: 3
|
||||
title: 'task #3 high prio'
|
||||
done: false
|
||||
|
@ -28,7 +24,6 @@
|
|||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
priority: 100
|
||||
bucket_id: 2
|
||||
- id: 4
|
||||
title: 'task #4 low prio'
|
||||
done: false
|
||||
|
@ -38,7 +33,6 @@
|
|||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
priority: 1
|
||||
bucket_id: 2
|
||||
- id: 5
|
||||
title: 'task #5 higher due date'
|
||||
done: false
|
||||
|
@ -48,7 +42,6 @@
|
|||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
due_date: 2018-12-01 03:58:44
|
||||
bucket_id: 2
|
||||
- id: 6
|
||||
title: 'task #6 lower due date'
|
||||
done: false
|
||||
|
@ -58,7 +51,6 @@
|
|||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
due_date: 2018-11-30 22:25:24
|
||||
bucket_id: 3
|
||||
- id: 7
|
||||
title: 'task #7 with start date'
|
||||
done: false
|
||||
|
@ -68,7 +60,6 @@
|
|||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
start_date: 2018-12-12 07:33:20
|
||||
bucket_id: 3
|
||||
- id: 8
|
||||
title: 'task #8 with end date'
|
||||
done: false
|
||||
|
@ -78,7 +69,6 @@
|
|||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
end_date: 2018-12-13 11:20:00
|
||||
bucket_id: 3
|
||||
- id: 9
|
||||
title: 'task #9 with start and end date'
|
||||
done: false
|
||||
|
@ -89,14 +79,12 @@
|
|||
updated: 2018-12-01 01:12:04
|
||||
start_date: 2018-12-12 07:33:20
|
||||
end_date: 2018-12-13 11:20:00
|
||||
bucket_id: 1
|
||||
- id: 10
|
||||
title: 'task #10 basic'
|
||||
done: false
|
||||
created_by_id: 1
|
||||
project_id: 1
|
||||
index: 10
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 11
|
||||
|
@ -105,7 +93,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 1
|
||||
index: 11
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 12
|
||||
|
@ -114,7 +101,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 1
|
||||
index: 12
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 13
|
||||
|
@ -123,7 +109,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 2
|
||||
index: 1
|
||||
bucket_id: 4
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 14
|
||||
|
@ -132,7 +117,6 @@
|
|||
created_by_id: 5
|
||||
project_id: 5
|
||||
index: 1
|
||||
bucket_id: 18
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 15
|
||||
|
@ -141,7 +125,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 6
|
||||
index: 1
|
||||
bucket_id: 6
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 16
|
||||
|
@ -150,7 +133,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 7
|
||||
index: 1
|
||||
bucket_id: 7
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 17
|
||||
|
@ -159,7 +141,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 8
|
||||
index: 1
|
||||
bucket_id: 8
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 18
|
||||
|
@ -168,7 +149,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 9
|
||||
index: 1
|
||||
bucket_id: 9
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 19
|
||||
|
@ -177,7 +157,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 10
|
||||
index: 1
|
||||
bucket_id: 10
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 20
|
||||
|
@ -186,7 +165,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 11
|
||||
index: 1
|
||||
bucket_id: 11
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 21
|
||||
|
@ -195,7 +173,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 32
|
||||
index: 1
|
||||
bucket_id: 12
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 22
|
||||
|
@ -204,7 +181,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 33
|
||||
index: 1
|
||||
bucket_id: 36
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 23
|
||||
|
@ -213,7 +189,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 34
|
||||
index: 1
|
||||
bucket_id: 37
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 24
|
||||
|
@ -222,7 +197,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 15
|
||||
index: 1
|
||||
bucket_id: 15
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 25
|
||||
|
@ -231,7 +205,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 16
|
||||
index: 1
|
||||
bucket_id: 16
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 26
|
||||
|
@ -240,7 +213,6 @@
|
|||
created_by_id: 6
|
||||
project_id: 17
|
||||
index: 1
|
||||
bucket_id: 17
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 27
|
||||
|
@ -249,7 +221,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 1
|
||||
index: 12
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
start_date: 2018-11-30 22:25:24
|
||||
|
@ -260,7 +231,6 @@
|
|||
repeat_after: 3600
|
||||
project_id: 1
|
||||
index: 13
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 29
|
||||
|
@ -269,7 +239,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 1
|
||||
index: 14
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 30
|
||||
|
@ -278,7 +247,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 1
|
||||
index: 15
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 31
|
||||
|
@ -288,7 +256,6 @@
|
|||
project_id: 1
|
||||
index: 16
|
||||
hex_color: f0f0f0
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 32
|
||||
|
@ -297,7 +264,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 3
|
||||
index: 1
|
||||
bucket_id: 21
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 33
|
||||
|
@ -307,7 +273,6 @@
|
|||
project_id: 1
|
||||
index: 17
|
||||
percent_done: 0.5
|
||||
bucket_id: 1
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
# This task is forbidden for user1
|
||||
|
@ -317,7 +282,6 @@
|
|||
created_by_id: 13
|
||||
project_id: 20
|
||||
index: 20
|
||||
bucket_id: 5
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 35
|
||||
|
@ -326,7 +290,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 21
|
||||
index: 1
|
||||
bucket_id: 19
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
- id: 36
|
||||
|
@ -335,7 +298,6 @@
|
|||
created_by_id: 1
|
||||
project_id: 22
|
||||
index: 1
|
||||
bucket_id: 20
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
due_date: 2018-10-30 22:25:24
|
||||
|
@ -374,8 +336,6 @@
|
|||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 39
|
||||
- id: 41
|
||||
uid: 'uid-caldav-test-parent-task'
|
||||
title: 'Parent task for Caldav Test'
|
||||
|
@ -388,8 +348,6 @@
|
|||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 40
|
||||
- id: 42
|
||||
uid: 'uid-caldav-test-parent-task-2'
|
||||
title: 'Parent task for Caldav Test 2'
|
||||
|
@ -402,8 +360,6 @@
|
|||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 41
|
||||
- id: 43
|
||||
uid: 'uid-caldav-test-child-task'
|
||||
title: 'Child task for Caldav Test'
|
||||
|
@ -416,8 +372,6 @@
|
|||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 42
|
||||
- id: 44
|
||||
uid: 'uid-caldav-test-child-task-2'
|
||||
title: 'Child task for Caldav Test '
|
||||
|
@ -430,8 +384,6 @@
|
|||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 43
|
||||
- id: 45
|
||||
uid: 'uid-caldav-test-parent-task-another-list'
|
||||
title: 'Parent task for Caldav Test'
|
||||
|
@ -444,8 +396,6 @@
|
|||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 44
|
||||
- id: 46
|
||||
uid: 'uid-caldav-test-child-task-another-list'
|
||||
title: 'Child task for Caldav Test '
|
||||
|
@ -457,6 +407,4 @@
|
|||
index: 45
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 45
|
||||
updated: 2018-12-01 01:12:04
|
|
@ -52,7 +52,10 @@ func TestBucket(t *testing.T) {
|
|||
}
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(nil, map[string]string{"project": "1"})
|
||||
rec, err := testHandler.testReadAllWithUser(nil, map[string]string{
|
||||
"project": "1",
|
||||
"view": "4",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket1`)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket2`)
|
||||
|
@ -63,87 +66,151 @@ func TestBucket(t *testing.T) {
|
|||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
// Check the project was loaded successfully afterwards, see testReadOneWithUser
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "1",
|
||||
"project": "1",
|
||||
"view": "4",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Nonexisting Bucket", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "9999"}, `{"title":"TestLoremIpsum"}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "9999",
|
||||
"project": "1",
|
||||
"view": "4",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
t.Run("Empty title", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "1"}, `{"title":""}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "1",
|
||||
"project": "1",
|
||||
"view": "4",
|
||||
}, `{"title":""}`)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "5"}, `{"title":"TestLoremIpsum"}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "5",
|
||||
"project": "20",
|
||||
"view": "80",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "6"}, `{"title":"TestLoremIpsum"}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "6",
|
||||
"project": "6",
|
||||
"view": "24",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "7"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "7",
|
||||
"project": "7",
|
||||
"view": "28",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "8"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "8",
|
||||
"project": "8",
|
||||
"view": "32",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "9"}, `{"title":"TestLoremIpsum"}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "9",
|
||||
"project": "9",
|
||||
"view": "36",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "10"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "10",
|
||||
"project": "10",
|
||||
"view": "40",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "11"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "11",
|
||||
"project": "11",
|
||||
"view": "44",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "12"}, `{"title":"TestLoremIpsum"}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "12",
|
||||
"project": "12",
|
||||
"view": "48",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "13"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "13",
|
||||
"project": "13",
|
||||
"view": "52",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "14"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "14",
|
||||
"project": "14",
|
||||
"view": "56",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "15"}, `{"title":"TestLoremIpsum"}`)
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "15",
|
||||
"project": "15",
|
||||
"view": "60",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "16"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "16",
|
||||
"project": "16",
|
||||
"view": "64",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "17"}, `{"title":"TestLoremIpsum"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{
|
||||
"bucket": "17",
|
||||
"project": "17",
|
||||
"view": "68",
|
||||
}, `{"title":"TestLoremIpsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
@ -151,7 +218,11 @@ func TestBucket(t *testing.T) {
|
|||
})
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "1", "bucket": "1"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "1",
|
||||
"bucket": "1",
|
||||
"view": "4",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
@ -173,60 +244,104 @@ func TestBucket(t *testing.T) {
|
|||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "7", "bucket": "7"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "7",
|
||||
"bucket": "7",
|
||||
"view": "28",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "8", "bucket": "8"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "8",
|
||||
"bucket": "8",
|
||||
"view": "32",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "9", "bucket": "9"})
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "9",
|
||||
"bucket": "9",
|
||||
"view": "36",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "10", "bucket": "10"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "10",
|
||||
"bucket": "10",
|
||||
"view": "40",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "11", "bucket": "11"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "11",
|
||||
"bucket": "11",
|
||||
"view": "44",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "12", "bucket": "12"})
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "12",
|
||||
"bucket": "12",
|
||||
"view": "48",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "13", "bucket": "13"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "13",
|
||||
"bucket": "13",
|
||||
"view": "52",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "14", "bucket": "14"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "14",
|
||||
"bucket": "14",
|
||||
"view": "56",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "15", "bucket": "15"})
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "15",
|
||||
"bucket": "15",
|
||||
"view": "60",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "16", "bucket": "16"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "16",
|
||||
"bucket": "16",
|
||||
"view": "64",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "17", "bucket": "17"})
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{
|
||||
"project": "17",
|
||||
"bucket": "17",
|
||||
"view": "68",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
@ -315,13 +430,16 @@ func TestBucket(t *testing.T) {
|
|||
})
|
||||
})
|
||||
t.Run("Link Share", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{"title":"Lorem Ipsum"}`)
|
||||
rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{
|
||||
"project": "2",
|
||||
"view": "8",
|
||||
}, `{"title":"Lorem Ipsum"}`)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
db.AssertExists(t, "buckets", map[string]interface{}{
|
||||
"project_id": 2,
|
||||
"created_by_id": -2,
|
||||
"title": "Lorem Ipsum",
|
||||
"project_view_id": 8,
|
||||
"created_by_id": -2,
|
||||
"title": "Lorem Ipsum",
|
||||
}, false)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -115,49 +115,49 @@ func TestTaskCollection(t *testing.T) {
|
|||
t.Run("by priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
t.Run("by priority desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
})
|
||||
t.Run("by priority asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
// should equal duedate asc
|
||||
t.Run("by due_date", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by duedate desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
})
|
||||
// Due date without unix suffix
|
||||
t.Run("by duedate asc without suffix", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by due_date without suffix", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by duedate desc without suffix", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
})
|
||||
t.Run("by duedate asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("invalid sort parameter", func(t *testing.T) {
|
||||
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
||||
|
@ -358,33 +358,33 @@ func TestTaskCollection(t *testing.T) {
|
|||
t.Run("by priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
t.Run("by priority desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
})
|
||||
t.Run("by priority asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
// should equal duedate asc
|
||||
t.Run("by due_date", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by duedate desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
})
|
||||
t.Run("by duedate asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"reactions":null,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("invalid parameter", func(t *testing.T) {
|
||||
// Invalid parameter should not sort at all
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue