Compare commits
256 Commits
v1.15.2
...
vue3-remov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981d1cbd38 | ||
|
|
85af54cd6f | ||
|
|
f3a8de2c48 | ||
|
|
1d7bf30e8f | ||
|
|
ee10097c24 | ||
|
|
03d2bd945c | ||
|
|
1110a4f125 | ||
|
|
f4acf38035 | ||
|
|
30e74a7919 | ||
|
|
b59b2edb37 | ||
|
|
8e805dbec3 | ||
|
|
0f1d1181e3 | ||
|
|
c32115dea6 | ||
|
|
452dc5f230 | ||
|
|
81e2c4768b | ||
|
|
3c917f3b2d | ||
|
|
6fd24ffa45 | ||
|
|
a9235e00c7 | ||
|
|
f8ad05b55b | ||
|
|
be497a741c | ||
|
|
62a9ce3131 | ||
|
|
7799854df4 | ||
|
|
bf961c3de4 | ||
|
|
5f73d97e4e | ||
|
|
32f0852a23 | ||
|
|
d80c9b3eec | ||
|
|
002bac60b1 | ||
|
|
08756b6c5d | ||
|
|
71bb838964 | ||
|
|
6c55e3cd70 | ||
|
|
8391d843d5 | ||
|
|
7cb9f8b966 | ||
|
|
1dfbec4278 | ||
|
|
6841d4c068 | ||
|
|
4e70286629 | ||
|
|
54a5a1ecb2 | ||
|
|
4139f8b94a | ||
|
|
5c21cd2cf5 | ||
|
|
af253270aa | ||
|
|
6a64b4fc75 | ||
|
|
4b260a047c | ||
|
|
8d41f9730f | ||
|
|
2adacaa39e | ||
|
|
69fcd9bfdf | ||
|
|
c0b3775232 | ||
|
|
df0e909be4 | ||
|
|
96905c891c | ||
|
|
58c8dd5221 | ||
|
|
e21c82b38e | ||
|
|
02afc218ff | ||
|
|
50d199dd08 | ||
|
|
ff74bfbf13 | ||
|
|
a64bec0ecd | ||
|
|
d66e6408b1 | ||
|
|
324eb05598 | ||
|
|
9938a6d57f | ||
|
|
c2e4165bf7 | ||
|
|
7de0d6f1c4 | ||
|
|
5631741b6b | ||
|
|
724ad74c47 | ||
|
|
c8897477a7 | ||
|
|
7ac93d311c | ||
|
|
be8806102a | ||
|
|
f6a5877b85 | ||
|
|
9360a49d50 | ||
|
|
3aee848221 | ||
|
|
2f53f8a1d6 | ||
|
|
d63764fa78 | ||
|
|
d06ec0ad98 | ||
|
|
4bf086b55e | ||
|
|
99e2912d7c | ||
|
|
10aed3660c | ||
|
|
6628c42eca | ||
|
|
dbe1ced6b0 | ||
|
|
04b4da39c9 | ||
|
|
530a695e6f | ||
|
|
02d7dd469e | ||
|
|
71340b1217 | ||
|
|
153a9d2b7d | ||
|
|
e18f73a333 | ||
|
|
127f1b2d40 | ||
|
|
fe4c8df97e | ||
|
|
d5582535d6 | ||
|
|
7ec112ae1d | ||
|
|
aa747d7d62 | ||
|
|
d97f6c1a5f | ||
|
|
8e0b585662 | ||
|
|
c0f7e16463 | ||
|
|
71b4700a07 | ||
|
|
688649f5a8 | ||
|
|
03cdc47540 | ||
|
|
9f06a43d4b | ||
|
|
d31e31735d | ||
|
|
4a05762c0a | ||
|
|
a72f48f9ce | ||
|
|
7e6f23fa82 | ||
|
|
a72f4e3aa3 | ||
|
|
ec8945bb54 | ||
|
|
760f771e11 | ||
|
|
f0a443cbb6 | ||
|
|
69b9c09a14 | ||
|
|
7c93108172 | ||
|
|
899d8a6531 | ||
|
|
60b34d190a | ||
|
|
438a149304 | ||
|
|
9a2fcf6925 | ||
|
|
e3c802c0f3 | ||
|
|
ea84743864 | ||
|
|
abd38e0c15 | ||
|
|
0ec1f7151c | ||
|
|
a27cbf8bad | ||
|
|
f4a1424e90 | ||
|
|
a92dc282a8 | ||
|
|
e3ffbf73c3 | ||
|
|
c6e075af92 | ||
|
|
144f293231 | ||
|
|
6d78bc70f3 | ||
|
|
bf15865957 | ||
|
|
d2e5c0c515 | ||
|
|
933f2515db | ||
|
|
17bf361767 | ||
|
|
1df731396f | ||
|
|
968ea30fd4 | ||
|
|
8f75c7f92d | ||
|
|
234be011cc | ||
|
|
fa2f5bcab8 | ||
|
|
685014ec48 | ||
|
|
1cd6c55d30 | ||
|
|
6dbcf5d17b | ||
|
|
f84d7c372b | ||
|
|
8be8647f6d | ||
|
|
eb878055ae | ||
|
|
00b334d3d9 | ||
|
|
5905154fe0 | ||
|
|
8280f0796f | ||
|
|
18ea2f9a7e | ||
|
|
3cbddae6bc | ||
|
|
aac11e96b5 | ||
|
|
2e62a9c82a | ||
|
|
ec18ec758b | ||
|
|
8f3e2ae1d9 | ||
|
|
7f00601007 | ||
|
|
175c10c146 | ||
|
|
8dc0933c47 | ||
|
|
8c52b9bad9 | ||
|
|
a1c9396946 | ||
|
|
b0fbb386b8 | ||
|
|
e77a2fcd79 | ||
|
|
89152325be | ||
|
|
03cb9e2bc5 | ||
|
|
3c6f8ec419 | ||
|
|
3cfb1f6bfe | ||
|
|
96ad191ce2 | ||
|
|
52d4039dc8 | ||
|
|
73b3517840 | ||
|
|
3d78802446 | ||
|
|
8a99e6d539 | ||
|
|
0e08ad16e1 | ||
|
|
d04ab25315 | ||
|
|
19d35c65a0 | ||
|
|
1783914d3a | ||
|
|
c837dd4db6 | ||
|
|
db22052729 | ||
|
|
116babcaaf | ||
|
|
94da732bfb | ||
|
|
86d42b7060 | ||
|
|
122387a195 | ||
|
|
028f26a969 | ||
|
|
13c13b8dde | ||
|
|
5df4a49ff5 | ||
|
|
97902d2f6a | ||
|
|
db6221d1c9 | ||
|
|
0223dd9a3a | ||
|
|
974a4c979a | ||
|
|
51ba51fe33 | ||
|
|
4d5c69f250 | ||
|
|
9bddab4928 | ||
|
|
6dcb1d4b8b | ||
|
|
9ed384fa20 | ||
|
|
7cdc79a9ef | ||
|
|
852770c574 | ||
|
|
74afeb85d0 | ||
|
|
12a494720b | ||
|
|
b3261e0b56 | ||
|
|
88dbb010d0 | ||
|
|
d8adaf5fe6 | ||
|
|
2a36b45072 | ||
|
|
4e6474fa99 | ||
|
|
dbe7536339 | ||
|
|
b5e08110ab | ||
|
|
a7f2558aab | ||
|
|
3c3e6fa7d2 | ||
|
|
6a2b092ad6 | ||
|
|
5f2c300b21 | ||
|
|
439e44128a | ||
|
|
55056e27f1 | ||
|
|
22c36a0d77 | ||
|
|
1ebaa0c41f | ||
|
|
a2dbb94179 | ||
|
|
6f6f54965c | ||
|
|
5a833ec367 | ||
|
|
f1161be49f | ||
|
|
c0731ce142 | ||
|
|
4534ad5a92 | ||
|
|
6788210c27 | ||
|
|
1051dea281 | ||
|
|
eb9fce2e5f | ||
|
|
955a16f1c5 | ||
|
|
f38773473b | ||
|
|
b15254f1ed | ||
|
|
6dd623f796 | ||
|
|
41e97d7281 | ||
|
|
1b2a352df1 | ||
|
|
7fcad2425d | ||
|
|
5fb43086b6 | ||
|
|
7b4586a43a | ||
|
|
aaa76f7bd0 | ||
|
|
8260296d64 | ||
|
|
b02bc530f1 | ||
|
|
1b4e859431 | ||
|
|
acc61ad584 | ||
|
|
cb1d5c49e0 | ||
|
|
0c7b1c4731 | ||
|
|
629ef85d64 | ||
|
|
914f1d76ac | ||
|
|
0923070c45 | ||
|
|
bbf51952e0 | ||
|
|
1d1c2df8f3 | ||
|
|
0780b10170 | ||
|
|
fd6ef9b6eb | ||
|
|
da4a54900c | ||
|
|
53bd32d6b6 | ||
|
|
f7fa419105 | ||
|
|
023ab01e93 | ||
|
|
615d5ef871 | ||
|
|
a52b7b9742 | ||
|
|
dcbc6bc604 | ||
|
|
ce13d89e07 | ||
|
|
7114b10871 | ||
|
|
100404d25f | ||
|
|
60bfc192d3 | ||
|
|
fd090c7749 | ||
|
|
d735308bd3 | ||
|
|
4ec068ca62 | ||
|
|
f5e3e5e0f8 | ||
|
|
94aee3e07a | ||
|
|
b1f7c623a1 | ||
|
|
afa95d3c50 | ||
|
|
ba8e77abfe | ||
|
|
8c09047203 | ||
|
|
baa85e8947 | ||
|
|
f1e5acc52e | ||
|
|
d76b7a4667 | ||
|
|
df32a3ee34 | ||
|
|
2fa74662db | ||
|
|
d5fd80d116 |
59
.github/dependabot.yml
vendored
59
.github/dependabot.yml
vendored
@@ -13,6 +13,23 @@ updates:
|
||||
- juliushaertl
|
||||
- luka-nextcloud
|
||||
|
||||
- package-ecosystem: npm
|
||||
target-branch: stable31
|
||||
versioning-strategy: lockfile-only
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:15"
|
||||
timezone: Europe/Paris
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
open-pull-requests-limit: 30
|
||||
labels:
|
||||
- 3. to review
|
||||
- dependencies
|
||||
|
||||
- package-ecosystem: npm
|
||||
target-branch: stable30
|
||||
versioning-strategy: lockfile-only
|
||||
@@ -20,41 +37,7 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:00"
|
||||
timezone: Europe/Paris
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
open-pull-requests-limit: 30
|
||||
labels:
|
||||
- 3. to review
|
||||
- dependencies
|
||||
|
||||
- package-ecosystem: npm
|
||||
target-branch: stable29
|
||||
versioning-strategy: lockfile-only
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:00"
|
||||
timezone: Europe/Paris
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
open-pull-requests-limit: 30
|
||||
labels:
|
||||
- 3. to review
|
||||
- dependencies
|
||||
|
||||
- package-ecosystem: npm
|
||||
target-branch: stable28
|
||||
versioning-strategy: lockfile-only
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:00"
|
||||
time: "03:30"
|
||||
timezone: Europe/Paris
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
@@ -69,7 +52,7 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:00"
|
||||
time: "03:45"
|
||||
timezone: Europe/Paris
|
||||
open-pull-requests-limit: 10
|
||||
reviewers:
|
||||
@@ -81,7 +64,7 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:00"
|
||||
time: "04:00"
|
||||
timezone: Europe/Paris
|
||||
open-pull-requests-limit: 10
|
||||
reviewers:
|
||||
@@ -93,7 +76,7 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: saturday
|
||||
time: "03:00"
|
||||
time: "04:15"
|
||||
timezone: Europe/Paris
|
||||
open-pull-requests-limit: 10
|
||||
reviewers:
|
||||
|
||||
4
.github/workflows/appbuild.yml
vendored
4
.github/workflows/appbuild.yml
vendored
@@ -18,13 +18,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Set up npm7
|
||||
run: npm i -g npm@7
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.34.1
|
||||
with:
|
||||
php-version: '7.4'
|
||||
tools: composer
|
||||
|
||||
4
.github/workflows/appstore-build-publish.yml
vendored
4
.github/workflows/appstore-build-publish.yml
vendored
@@ -71,7 +71,7 @@ jobs:
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
# Skip if no package.json
|
||||
if: ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.1.0
|
||||
with:
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
filename: ${{ env.APP_NAME }}/appinfo/info.xml
|
||||
|
||||
- name: Set up php ${{ steps.php-versions.outputs.php-min }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ steps.php-versions.outputs.php-min }}
|
||||
coverage: none
|
||||
|
||||
6
.github/workflows/cypress-e2e.yml
vendored
6
.github/workflows/cypress-e2e.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
node-version: [20.x]
|
||||
# containers: [1, 2, 3]
|
||||
php-versions: [ '8.2' ]
|
||||
server-versions: [ 'stable31' ]
|
||||
server-versions: [ 'master' ]
|
||||
|
||||
env:
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, apcu
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
restore-keys: ${{ steps.extcache.outputs.key }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.34.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: ${{ env.extensions }}
|
||||
|
||||
2
.github/workflows/integration.yml
vendored
2
.github/workflows/integration.yml
vendored
@@ -78,7 +78,7 @@ jobs:
|
||||
path: apps/activity
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@2.31.1
|
||||
uses: shivammathur/setup-php@2.34.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, mysql, pdo_mysql, pgsql, pdo_pgsql, apcu, gd
|
||||
|
||||
2
.github/workflows/lint-eslint.yml
vendored
2
.github/workflows/lint-eslint.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
fallbackNpm: '^10'
|
||||
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.1.0
|
||||
with:
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
|
||||
4
.github/workflows/lint-php-cs.yml
vendored
4
.github/workflows/lint-php-cs.yml
vendored
@@ -33,8 +33,8 @@ jobs:
|
||||
id: versions
|
||||
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
|
||||
|
||||
- name: Set up php${{ steps.versions.outputs.php-available }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
- name: Set up php${{ steps.versions.outputs.php-min }}
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ steps.versions.outputs.php-min }}
|
||||
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
|
||||
|
||||
2
.github/workflows/lint-php.yml
vendored
2
.github/workflows/lint-php.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
|
||||
|
||||
2
.github/workflows/lint-stylelint.yml
vendored
2
.github/workflows/lint-stylelint.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
fallbackNpm: '^10'
|
||||
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.1.0
|
||||
with:
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
|
||||
2
.github/workflows/nodejs.yml
vendored
2
.github/workflows/nodejs.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.1.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Set up npm7
|
||||
|
||||
18
.github/workflows/npm-audit-fix.yml
vendored
18
.github/workflows/npm-audit-fix.yml
vendored
@@ -14,6 +14,9 @@ on:
|
||||
# At 2:30 on Sundays
|
||||
- cron: '30 2 * * 0'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -21,15 +24,18 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
branches: ['main', 'master', 'stable30', 'stable29', 'stable28']
|
||||
branches: ['main', 'master', 'stable31', 'stable30', 'stable29']
|
||||
|
||||
name: npm-audit-fix-${{ matrix.branches }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ matrix.branches }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Read package.json node and npm engines version
|
||||
uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3
|
||||
@@ -39,7 +45,7 @@ jobs:
|
||||
fallbackNpm: '^10'
|
||||
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.1.0
|
||||
with:
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
@@ -48,10 +54,10 @@ jobs:
|
||||
|
||||
- name: Fix npm audit
|
||||
id: npm-audit
|
||||
uses: nextcloud-libraries/npm-audit-action@2a60bd2e79cc77f2cc4d9a3fe40f1a69896f3a87 # v0.1.0
|
||||
uses: nextcloud-libraries/npm-audit-action@1b1728b2b4a7a78d69de65608efcf4db0e3e42d0 # v0.2.0
|
||||
|
||||
- name: Run npm ci and npm run build
|
||||
if: always()
|
||||
if: steps.checkout.outcome == 'success'
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
run: |
|
||||
@@ -59,8 +65,8 @@ jobs:
|
||||
npm run build --if-present
|
||||
|
||||
- name: Create Pull Request
|
||||
if: always()
|
||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||
if: steps.checkout.outcome == 'success'
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||
commit-message: 'fix(deps): Fix npm audit'
|
||||
|
||||
2
.github/workflows/phpunit-mysql.yml
vendored
2
.github/workflows/phpunit-mysql.yml
vendored
@@ -103,7 +103,7 @@ jobs:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
|
||||
|
||||
2
.github/workflows/phpunit-pgsql.yml
vendored
2
.github/workflows/phpunit-pgsql.yml
vendored
@@ -106,7 +106,7 @@ jobs:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
|
||||
|
||||
2
.github/workflows/phpunit-sqlite.yml
vendored
2
.github/workflows/phpunit-sqlite.yml
vendored
@@ -95,7 +95,7 @@ jobs:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
|
||||
|
||||
5
.github/workflows/pr-feedback.yml
vendored
5
.github/workflows/pr-feedback.yml
vendored
@@ -15,8 +15,13 @@ on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
pr-feedback:
|
||||
if: ${{ github.repository_owner == 'nextcloud' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: The get-github-handles-from-website action
|
||||
|
||||
4
.github/workflows/psalm.yml
vendored
4
.github/workflows/psalm.yml
vendored
@@ -35,8 +35,8 @@ jobs:
|
||||
- name: Check enforcement of minimum PHP version ${{ steps.versions.outputs.php-min }} in psalm.xml
|
||||
run: grep 'phpVersion="${{ steps.versions.outputs.php-min }}' psalm.xml
|
||||
|
||||
- name: Set up php${{ steps.versions.outputs.php-available }}
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
- name: Set up php${{ steps.versions.outputs.php-min }}
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: ${{ steps.versions.outputs.php-min }}
|
||||
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
|
||||
|
||||
2
.github/workflows/reuse.yml
vendored
2
.github/workflows/reuse.yml
vendored
@@ -16,7 +16,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
reuse-compliance-check:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-latest-low
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
6
.github/workflows/update-nextcloud-ocp.yml
vendored
6
.github/workflows/update-nextcloud-ocp.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
branches: ['main', 'master', 'stable30', 'stable29', 'stable28']
|
||||
branches: ['main', 'master', 'stable31', 'stable30', 'stable29']
|
||||
|
||||
name: update-nextcloud-ocp-${{ matrix.branches }}
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
- name: Set up php8.2
|
||||
if: steps.checkout.outcome == 'success'
|
||||
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
|
||||
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
|
||||
with:
|
||||
php-version: 8.2
|
||||
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- name: Create Pull Request
|
||||
if: steps.checkout.outcome == 'success'
|
||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||
commit-message: 'chore(dev-deps): Bump nextcloud/ocp package'
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -5,49 +5,6 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
# 1.15.2
|
||||
### Fixed
|
||||
* [stable31] fix: update DeleteCron to remove deleted lists by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7029
|
||||
* [stable31] fix: not show Share with a Deck card for unauthorized users by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7061
|
||||
* [stable31] fix: unstable cypress test by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7063
|
||||
* [stable31] fix: add retry and show warning on description saving error by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7091
|
||||
* [stable31] fix: styling for new stack input field by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7113
|
||||
* [stable31] fix:allow foreign label deletion by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7130
|
||||
* [stable31] fix: acl check when delete, update board acl by @backportbot[bot] in https://github.com/nextcloud/deck/pull/7132
|
||||
|
||||
### Improvements
|
||||
* [stable31] perf: don't enrich cards when finding calendar entries by @backportbot[bot] in https://github.com/nextcloud/deck/pull/6959
|
||||
|
||||
## 1.15.1
|
||||
### Fixed
|
||||
* [stable31] fix: Properly show attachment extension by @backportbot in https://github.com/nextcloud/deck/pull/6701
|
||||
* [stable31] Clear selected stack when selected board changed by @backportbot in https://github.com/nextcloud/deck/pull/6915
|
||||
* [stable31] fix: Use strings as rich object ids by @backportbot in https://github.com/nextcloud/deck/pull/6951
|
||||
* [stable31] fix: Limit label actions to labels of the cards board by @backportbot in https://github.com/nextcloud/deck/pull/6954
|
||||
* [stable31] fix: only delete assignments on unshared board by @grnd-alt in https://github.com/nextcloud/deck/pull/6934
|
||||
|
||||
### Improvements
|
||||
* [stable31] perf: Skip doing a query just to check if a board is deleted by @backportbot in https://github.com/nextcloud/deck/pull/6894
|
||||
|
||||
## 1.15.0
|
||||
### Fixed
|
||||
- Fix: Adapt URLs generated in the backend to new routes #6743
|
||||
- Fix npm audit #6719
|
||||
- Fix: skip exporting a deleted card #6723
|
||||
|
||||
## 1.15.0-beta.2
|
||||
|
||||
### Added
|
||||
|
||||
- feat: Implement reference resolving for cards that have a link in the title @juliusknorr [#6286](https://github.com/nextcloud/deck/pull/6286)
|
||||
|
||||
### Other
|
||||
|
||||
- Remove old project from README @edent [#6658](https://github.com/nextcloud/deck/pull/6658)
|
||||
- devcontainer(image): Fix package path @niclasheinz [#6653](https://github.com/nextcloud/deck/pull/6653)
|
||||
- remove deprecated nextcloud-vue-collections @grnd-alt [#6664](https://github.com/nextcloud/deck/pull/6664)
|
||||
- fix: set cypress ci server version to stable31 @grnd-alt [#6705](https://github.com/nextcloud/deck/pull/6705)
|
||||
|
||||
## 1.15.0-beta.1
|
||||
### Fixed
|
||||
- fix: Catch not found card @juliusknorr [#6646](https://github.com/nextcloud/deck/pull/6646)
|
||||
|
||||
@@ -12,7 +12,7 @@ SPDX-FileCopyrightText = "none"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = ["l10n/**.js", "l10n/**.json", "js/**.js.map", "js/**.js", "js/**.mjs", "js/**.mjs.map", "js/templates/**.handlebars", "lib/Service/Importer/fixtures/config-deckJson-schema.json", "lib/Service/Importer/fixtures/config-trelloApi-schema.json", "lib/Service/Importer/fixtures/config-trelloJson-schema.json", "screenshots/screenshot1.png", "src/assets/file-placeholder.svg", "img/favicon.ico", "img/favicon.png", "img/favicon.svg", "img/activity.svg", "img/activity-dark.svg", "img/deck.svg", "img/deck-current.svg", "img/deck-dark.svg", "img/details-white.svg", "img/card.svg"]
|
||||
path = ["l10n/**.js", "l10n/**.json", "js/**.js.map", "js/**.js", "js/**.mjs", "js/**.mjs.map", "js/templates/**.handlebars", "lib/Service/Importer/fixtures/config-deckJson-schema.json", "lib/Service/Importer/fixtures/config-trelloApi-schema.json", "lib/Service/Importer/fixtures/config-trelloJson-schema.json", "lib/Service/fixtures/default-board.json", "screenshots/screenshot1.png", "src/assets/file-placeholder.svg", "img/favicon.ico", "img/favicon.png", "img/favicon.svg", "img/activity.svg", "img/activity-dark.svg", "img/deck.svg", "img/deck-current.svg", "img/deck-dark.svg", "img/details-white.svg", "img/card.svg", "img/sample-image.jpg"]
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2019 Nextcloud GmbH and Nextcloud contributors"
|
||||
SPDX-License-Identifier = "AGPL-3.0-or-later"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
- 🚀 Get your project organized
|
||||
|
||||
</description>
|
||||
<version>1.15.2</version>
|
||||
<version>2.0.0-dev.0</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Julius Härtl</author>
|
||||
<namespace>Deck</namespace>
|
||||
@@ -42,7 +42,7 @@
|
||||
<database min-version="9.4">pgsql</database>
|
||||
<database>sqlite</database>
|
||||
<database min-version="8.0">mysql</database>
|
||||
<nextcloud min-version="31" max-version="31"/>
|
||||
<nextcloud min-version="32" max-version="32"/>
|
||||
</dependencies>
|
||||
<background-jobs>
|
||||
<job>OCA\Deck\Cron\DeleteCron</job>
|
||||
@@ -54,6 +54,9 @@
|
||||
<live-migration>
|
||||
<step>OCA\Deck\Migration\DeletedCircleCleanup</step>
|
||||
</live-migration>
|
||||
<post-migration>
|
||||
<step>OCA\Deck\Migration\LabelMismatchCleanup</step>
|
||||
</post-migration>
|
||||
</repair-steps>
|
||||
<commands>
|
||||
<command>OCA\Deck\Command\UserExport</command>
|
||||
|
||||
@@ -29,6 +29,7 @@ return [
|
||||
['name' => 'board#clone', 'url' => '/boards/{boardId}/clone', 'verb' => 'POST'],
|
||||
['name' => 'board#transferOwner', 'url' => '/boards/{boardId}/transferOwner', 'verb' => 'PUT'],
|
||||
['name' => 'board#export', 'url' => '/boards/{boardId}/export', 'verb' => 'GET'],
|
||||
['name' => 'board#import', 'url' => '/boards/import', 'verb' => 'POST'],
|
||||
|
||||
// stacks
|
||||
['name' => 'stack#index', 'url' => '/stacks/{boardId}', 'verb' => 'GET'],
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"roave/security-advisories": "dev-master",
|
||||
"phpunit/phpunit": "^9",
|
||||
"nextcloud/coding-standard": "^1.1",
|
||||
"nextcloud/ocp": "dev-stable31",
|
||||
"nextcloud/ocp": "dev-master",
|
||||
"psalm/phar": "^5.13"
|
||||
},
|
||||
"config": {
|
||||
|
||||
157
composer.lock
generated
157
composer.lock
generated
@@ -4,146 +4,34 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c89537a172cee5c19093b4ea0cb5365c",
|
||||
"content-hash": "6950663d9d213151028e780637480220",
|
||||
"packages": [
|
||||
{
|
||||
"name": "icecave/parity",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/icecave/parity.git",
|
||||
"reference": "0109fef58b3230d23b20b2ac52ecdf477218d300"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/icecave/parity/zipball/0109fef58b3230d23b20b2ac52ecdf477218d300",
|
||||
"reference": "0109fef58b3230d23b20b2ac52ecdf477218d300",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"icecave/repr": "~1",
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"eloquent/liberator": "~1",
|
||||
"icecave/archer": "~1"
|
||||
},
|
||||
"suggest": {
|
||||
"eloquent/asplode": "Drop-in exception-based error handling."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Icecave\\Parity": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "James Harris",
|
||||
"email": "james.harris@icecave.com.au",
|
||||
"homepage": "https://github.com/jmalloc"
|
||||
}
|
||||
],
|
||||
"description": "A customizable deep comparison library.",
|
||||
"homepage": "https://github.com/IcecaveStudios/parity",
|
||||
"keywords": [
|
||||
"compare",
|
||||
"comparison",
|
||||
"equal",
|
||||
"equality",
|
||||
"greater",
|
||||
"less",
|
||||
"sort",
|
||||
"sorting"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/icecave/parity/issues",
|
||||
"source": "https://github.com/icecave/parity/tree/1.0.0"
|
||||
},
|
||||
"time": "2014-01-17T05:56:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "icecave/repr",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/icecave/repr.git",
|
||||
"reference": "8a3d2953adf5f464a06e3e2587aeacc97e2bed07"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/icecave/repr/zipball/8a3d2953adf5f464a06e3e2587aeacc97e2bed07",
|
||||
"reference": "8a3d2953adf5f464a06e3e2587aeacc97e2bed07",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"icecave/archer": "~1"
|
||||
},
|
||||
"suggest": {
|
||||
"eloquent/asplode": "Drop-in exception-based error handling."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Icecave\\Repr\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "James Harris",
|
||||
"email": "james.harris@icecave.com.au",
|
||||
"homepage": "https://github.com/jmalloc"
|
||||
}
|
||||
],
|
||||
"description": "A library for generating string representations of any value, inspired by Python's reprlib library.",
|
||||
"homepage": "https://github.com/IcecaveStudios/repr",
|
||||
"keywords": [
|
||||
"human",
|
||||
"readable",
|
||||
"repr",
|
||||
"representation",
|
||||
"string"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/icecave/repr/issues",
|
||||
"source": "https://github.com/icecave/repr/tree/1.0.1"
|
||||
},
|
||||
"time": "2014-07-25T05:44:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "6.0.0",
|
||||
"version": "6.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jsonrainbow/json-schema.git",
|
||||
"reference": "a38c6198d53b09c0702f440585a4f4a5d9137bd9"
|
||||
"reference": "35d262c94959571e8736db1e5c9bc36ab94ae900"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/a38c6198d53b09c0702f440585a4f4a5d9137bd9",
|
||||
"reference": "a38c6198d53b09c0702f440585a4f4a5d9137bd9",
|
||||
"url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/35d262c94959571e8736db1e5c9bc36ab94ae900",
|
||||
"reference": "35d262c94959571e8736db1e5c9bc36ab94ae900",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"icecave/parity": "1.0.0",
|
||||
"marc-mabe/php-enum": "^2.0 || ^3.0 || ^4.0",
|
||||
"php": ">=5.3.3"
|
||||
"ext-json": "*",
|
||||
"marc-mabe/php-enum": "^4.0",
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "~2.2.20 || ~2.19.0",
|
||||
"friendsofphp/php-cs-fixer": "3.3.0",
|
||||
"json-schema/json-schema-test-suite": "1.2.0",
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
"marc-mabe/php-enum-phpstan": "^2.0",
|
||||
"phpspec/prophecy": "^1.19",
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpunit/phpunit": "^8.5"
|
||||
},
|
||||
"bin": [
|
||||
"bin/validate-json"
|
||||
@@ -189,9 +77,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/jsonrainbow/json-schema/issues",
|
||||
"source": "https://github.com/jsonrainbow/json-schema/tree/6.0.0"
|
||||
"source": "https://github.com/jsonrainbow/json-schema/tree/6.4.1"
|
||||
},
|
||||
"time": "2024-07-30T17:49:21+00:00"
|
||||
"time": "2025-04-04T13:08:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "marc-mabe/php-enum",
|
||||
@@ -488,16 +376,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nextcloud/ocp",
|
||||
"version": "dev-stable31",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nextcloud-deps/ocp.git",
|
||||
"reference": "77679c75abf21dfc48a75fd282dea09b09ac096c"
|
||||
"reference": "97d7aa6e535670437a178084eea91a42998fef5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/77679c75abf21dfc48a75fd282dea09b09ac096c",
|
||||
"reference": "77679c75abf21dfc48a75fd282dea09b09ac096c",
|
||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/97d7aa6e535670437a178084eea91a42998fef5c",
|
||||
"reference": "97d7aa6e535670437a178084eea91a42998fef5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -507,10 +395,11 @@
|
||||
"psr/event-dispatcher": "^1.0",
|
||||
"psr/log": "^3.0.2"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-stable31": "31.0.0-dev"
|
||||
"dev-master": "32.0.0-dev"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -530,9 +419,9 @@
|
||||
"description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API",
|
||||
"support": {
|
||||
"issues": "https://github.com/nextcloud-deps/ocp/issues",
|
||||
"source": "https://github.com/nextcloud-deps/ocp/tree/stable31"
|
||||
"source": "https://github.com/nextcloud-deps/ocp/tree/master"
|
||||
},
|
||||
"time": "2025-07-04T00:53:40+00:00"
|
||||
"time": "2025-06-13T00:53:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
|
||||
@@ -129,3 +129,81 @@ describe('Board cloning', function() {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Board export', function() {
|
||||
before(function() {
|
||||
cy.createUser(user)
|
||||
})
|
||||
|
||||
it('Exports a board as JSON', function() {
|
||||
const boardName = 'Export JSON board'
|
||||
const board = sampleBoard(boardName)
|
||||
cy.createExampleBoard({ user, board }).then((board) => {
|
||||
const boardId = board.id
|
||||
cy.visit(`/apps/deck/board/${boardId}`)
|
||||
cy.get('.app-navigation__list .app-navigation-entry:contains("' + boardName + '")')
|
||||
.parent()
|
||||
.find('button[aria-label="Actions"]')
|
||||
.click()
|
||||
cy.get('button:contains("Export board")')
|
||||
.click()
|
||||
cy.get('.modal-container .checkbox-radio-switch__text:contains("Export as JSON")')
|
||||
.click()
|
||||
cy.get('.modal-container button:contains("Export")')
|
||||
.click()
|
||||
|
||||
const downloadsFolder = Cypress.config('downloadsFolder')
|
||||
cy.readFile(`${downloadsFolder}/${boardName}.json`)
|
||||
})
|
||||
})
|
||||
|
||||
it('Exports a board as CSV', function() {
|
||||
const boardName = 'Export CSV board'
|
||||
const board = sampleBoard(boardName)
|
||||
cy.createExampleBoard({ user, board }).then((board) => {
|
||||
const boardId = board.id
|
||||
cy.visit(`/apps/deck/board/${boardId}`)
|
||||
cy.get('.app-navigation__list .app-navigation-entry:contains("' + boardName + '")')
|
||||
.parent()
|
||||
.find('button[aria-label="Actions"]')
|
||||
.click()
|
||||
cy.get('button:contains("Export board")')
|
||||
.click()
|
||||
cy.get('.modal-container .checkbox-radio-switch__text:contains("Export as CSV")')
|
||||
.click()
|
||||
cy.get('.modal-container button:contains("Export")')
|
||||
.click()
|
||||
|
||||
const downloadsFolder = Cypress.config('downloadsFolder')
|
||||
cy.readFile(`${downloadsFolder}/${boardName}.csv`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Board import', function() {
|
||||
before(function () {
|
||||
cy.createUser(user)
|
||||
})
|
||||
beforeEach(function() {
|
||||
cy.login(user)
|
||||
cy.visit('/apps/deck')
|
||||
})
|
||||
|
||||
it('Imports a board from JSON', function() {
|
||||
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry:contains("Import board")')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
// Upload a JSON file
|
||||
cy.get('input[type="file"]')
|
||||
.selectFile([
|
||||
{
|
||||
contents: 'cypress/fixtures/import-board.json',
|
||||
fileName: 'import-board.json',
|
||||
},
|
||||
], { force: true })
|
||||
|
||||
cy.get('.app-navigation__list .app-navigation-entry:contains("Imported board")')
|
||||
.should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('Card', function () {
|
||||
cy.get('.modal-mask.card-selector .multiselect-list').should('be.visible').click()
|
||||
cy.get('.vs__dropdown-menu span[title="TestList"]').should('be.visible').click()
|
||||
|
||||
cy.get('.modal-mask.card-selector button.button-vue--vue-primary').should('be.visible').click()
|
||||
cy.get('.modal-mask.card-selector button.button-vue--primary').contains('Create card').should('be.visible').click()
|
||||
cy.wait('@save', { timeout: 7000 })
|
||||
|
||||
cy.reload()
|
||||
@@ -187,7 +187,7 @@ describe('Card', function () {
|
||||
cy.get('.file-picker__main').should('be.visible')
|
||||
cy.get('.file-picker__main [data-filename="welcome.txt"]', { timeout: 30000 }).should('be.visible')
|
||||
.click()
|
||||
cy.get('.dialog__actions button.button-vue--vue-primary').click()
|
||||
cy.get('.dialog__actions button.button-vue--primary').click()
|
||||
cy.get('.attachment-list .filename').contains('welcome')
|
||||
cy.get('.attachment-list .filename .extension').contains('txt')
|
||||
})
|
||||
@@ -302,8 +302,10 @@ describe('Card', function () {
|
||||
.first().click()
|
||||
cy.get(`.card:contains("${newCardTitle}")`).should('be.visible').click()
|
||||
|
||||
cy.get('#app-sidebar-vue [data-test="tag-selector"] .vs__dropdown-toggle').should('be.visible').click()
|
||||
cy.get('#app-sidebar-vue [data-test="tag-selector"] .vs__dropdown-toggle .vs__actions').should('be.visible').click()
|
||||
cy.get('.vs__dropdown-menu .tag:contains("Action needed")').should('be.visible').click()
|
||||
cy.get('.vs__dropdown-menu .tag:contains("Later")').should('not.exist')
|
||||
cy.get('#app-sidebar-vue [data-test="tag-selector"] .vs__dropdown-toggle .vs__actions').should('be.visible').click()
|
||||
cy.get('.vs__dropdown-menu .tag:contains("Later")').should('be.visible').click()
|
||||
|
||||
cy.get('.vs__selected .tag:contains("Action needed")').should('be.visible')
|
||||
|
||||
@@ -22,12 +22,12 @@ describe('Deck dashboard', function() {
|
||||
.should($el => expect($el.text().trim()).to.equal('Upcoming cards'))
|
||||
})
|
||||
|
||||
it('Can see the default "Personal Board" created for user by default', function() {
|
||||
it('Can see the default "Welcome Board" created for user by default', function() {
|
||||
cy.visit('/apps/deck')
|
||||
|
||||
const defaultBoard = 'Personal'
|
||||
const defaultBoard = 'Welcome to Nextcloud Deck!'
|
||||
|
||||
cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
||||
cy.get('#deck-navigation-all')
|
||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
|
||||
.first()
|
||||
.contains(defaultBoard)
|
||||
|
||||
102
cypress/fixtures/import-board.json
Normal file
102
cypress/fixtures/import-board.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"boards": [
|
||||
{
|
||||
"id": 70,
|
||||
"title": "Imported board",
|
||||
"owner": "unvjrmwuag",
|
||||
"color": "00ff00",
|
||||
"archived": false,
|
||||
"labels": [
|
||||
{
|
||||
"id": 293,
|
||||
"title": "Finished",
|
||||
"color": "31CC7C",
|
||||
"boardId": 70,
|
||||
"cardId": null,
|
||||
"lastModified": 0,
|
||||
"ETag": "cfcd208495d565ef66e7dff9f98764da"
|
||||
},
|
||||
{
|
||||
"id": 294,
|
||||
"title": "To review",
|
||||
"color": "317CCC",
|
||||
"boardId": 70,
|
||||
"cardId": null,
|
||||
"lastModified": 0,
|
||||
"ETag": "cfcd208495d565ef66e7dff9f98764da"
|
||||
},
|
||||
{
|
||||
"id": 295,
|
||||
"title": "Action needed",
|
||||
"color": "FF7A66",
|
||||
"boardId": 70,
|
||||
"cardId": null,
|
||||
"lastModified": 0,
|
||||
"ETag": "cfcd208495d565ef66e7dff9f98764da"
|
||||
},
|
||||
{
|
||||
"id": 296,
|
||||
"title": "Later",
|
||||
"color": "F1DB50",
|
||||
"boardId": 70,
|
||||
"cardId": null,
|
||||
"lastModified": 0,
|
||||
"ETag": "cfcd208495d565ef66e7dff9f98764da"
|
||||
}
|
||||
],
|
||||
"acl": [],
|
||||
"permissions": [],
|
||||
"users": [],
|
||||
"stacks": {
|
||||
"114": {
|
||||
"id": 114,
|
||||
"title": "TestList",
|
||||
"boardId": 70,
|
||||
"deletedAt": 0,
|
||||
"lastModified": 1743495533,
|
||||
"cards": [
|
||||
{
|
||||
"id": 124,
|
||||
"title": "Hello world",
|
||||
"description": "# Hello world",
|
||||
"descriptionPrev": null,
|
||||
"stackId": 114,
|
||||
"type": "plain",
|
||||
"lastModified": 1743495533,
|
||||
"lastEditor": null,
|
||||
"createdAt": 1743495533,
|
||||
"labels": [],
|
||||
"assignedUsers": null,
|
||||
"attachments": null,
|
||||
"attachmentCount": null,
|
||||
"owner": {
|
||||
"primaryKey": "unvjrmwuag",
|
||||
"uid": "unvjrmwuag",
|
||||
"displayname": "unvjrmwuag",
|
||||
"type": 0
|
||||
},
|
||||
"order": 999,
|
||||
"archived": false,
|
||||
"done": null,
|
||||
"duedate": null,
|
||||
"notified": false,
|
||||
"deletedAt": 0,
|
||||
"commentsUnread": 0,
|
||||
"commentsCount": 0,
|
||||
"relatedStack": null,
|
||||
"relatedBoard": null,
|
||||
"ETag": "aa85bb973089e7fbc0bbf122e926c23f"
|
||||
}
|
||||
],
|
||||
"order": 0,
|
||||
"ETag": "aa85bb973089e7fbc0bbf122e926c23f"
|
||||
}
|
||||
},
|
||||
"activeSessions": [],
|
||||
"deletedAt": 0,
|
||||
"lastModified": 1743495533,
|
||||
"settings": [],
|
||||
"ETag": "aa85bb973089e7fbc0bbf122e926c23f"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -93,7 +93,7 @@ Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
|
||||
})
|
||||
|
||||
Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
|
||||
return cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
||||
return cy.get('#deck-navigation-all')
|
||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
|
||||
.find('a.app-navigation-entry-link')
|
||||
})
|
||||
|
||||
BIN
img/sample-image.jpg
Normal file
BIN
img/sample-image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 188 KiB |
35
l10n/be.js
35
l10n/be.js
@@ -1,35 +0,0 @@
|
||||
OC.L10N.register(
|
||||
"deck",
|
||||
{
|
||||
"Missing a temporary folder" : "Адсутнічае часовая папка",
|
||||
"Could not write file to disk" : "Не ўдалося запісаць файл на дыск",
|
||||
"copy" : "копія",
|
||||
"Done" : "Гатова",
|
||||
"File" : "Файл",
|
||||
"Cancel" : "Скасаваць",
|
||||
"Active filters" : "Актыўныя фільтры",
|
||||
"Apply filter" : "Ужыць фільтр",
|
||||
"Open" : "Адкрыць",
|
||||
"Clear filter" : "Ачысціць фільтр",
|
||||
"Tags" : "Тэгі",
|
||||
"Activity" : "Актыўнасць",
|
||||
"Can edit" : "Можа рэдагаваць",
|
||||
"Can share" : "Можа абагульваць",
|
||||
"Owner" : "Уладальнік",
|
||||
"Delete" : "Выдаліць",
|
||||
"Download" : "Спампаваць",
|
||||
"Modified" : "Зменены",
|
||||
"Save" : "Захаваць",
|
||||
"Created:" : "Створана:",
|
||||
"Open link" : "Адкрыць спасылку",
|
||||
"seconds ago" : "с таму",
|
||||
"Keyboard shortcuts" : "Спалучэнні клавіш",
|
||||
"Keyboard shortcut" : "Спалучэнне клавіш",
|
||||
"Search" : "Пошук",
|
||||
"Shared with you" : "Абагулена з вамі",
|
||||
"An error occurred" : "Узнікла памылка",
|
||||
"Today" : "Сёння",
|
||||
"Close" : "Закрыць",
|
||||
"Share" : "Абагуліць"
|
||||
},
|
||||
"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
|
||||
33
l10n/be.json
33
l10n/be.json
@@ -1,33 +0,0 @@
|
||||
{ "translations": {
|
||||
"Missing a temporary folder" : "Адсутнічае часовая папка",
|
||||
"Could not write file to disk" : "Не ўдалося запісаць файл на дыск",
|
||||
"copy" : "копія",
|
||||
"Done" : "Гатова",
|
||||
"File" : "Файл",
|
||||
"Cancel" : "Скасаваць",
|
||||
"Active filters" : "Актыўныя фільтры",
|
||||
"Apply filter" : "Ужыць фільтр",
|
||||
"Open" : "Адкрыць",
|
||||
"Clear filter" : "Ачысціць фільтр",
|
||||
"Tags" : "Тэгі",
|
||||
"Activity" : "Актыўнасць",
|
||||
"Can edit" : "Можа рэдагаваць",
|
||||
"Can share" : "Можа абагульваць",
|
||||
"Owner" : "Уладальнік",
|
||||
"Delete" : "Выдаліць",
|
||||
"Download" : "Спампаваць",
|
||||
"Modified" : "Зменены",
|
||||
"Save" : "Захаваць",
|
||||
"Created:" : "Створана:",
|
||||
"Open link" : "Адкрыць спасылку",
|
||||
"seconds ago" : "с таму",
|
||||
"Keyboard shortcuts" : "Спалучэнні клавіш",
|
||||
"Keyboard shortcut" : "Спалучэнне клавіш",
|
||||
"Search" : "Пошук",
|
||||
"Shared with you" : "Абагулена з вамі",
|
||||
"An error occurred" : "Узнікла памылка",
|
||||
"Today" : "Сёння",
|
||||
"Close" : "Закрыць",
|
||||
"Share" : "Абагуліць"
|
||||
},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
|
||||
}
|
||||
@@ -33,7 +33,6 @@ OC.L10N.register(
|
||||
"Tags" : "Etiquetas",
|
||||
"Activity" : "Actividad",
|
||||
"Undo" : "Deshacer",
|
||||
"Transfer" : "Transferencia",
|
||||
"Can edit" : "Puede editar",
|
||||
"Can share" : "Puede compartir",
|
||||
"Owner" : "Dueño",
|
||||
@@ -59,7 +58,6 @@ OC.L10N.register(
|
||||
"Search" : "Buscar",
|
||||
"Archived boards" : "Tableros archivados",
|
||||
"Shared with you" : "Compartido con usted",
|
||||
"Cancel edit" : "Cancelar edición",
|
||||
"Board details" : "Detalles del tablero",
|
||||
"Edit board" : "Editar el tablero",
|
||||
"Unarchive board" : "Desarchivar tablero",
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
"Tags" : "Etiquetas",
|
||||
"Activity" : "Actividad",
|
||||
"Undo" : "Deshacer",
|
||||
"Transfer" : "Transferencia",
|
||||
"Can edit" : "Puede editar",
|
||||
"Can share" : "Puede compartir",
|
||||
"Owner" : "Dueño",
|
||||
@@ -57,7 +56,6 @@
|
||||
"Search" : "Buscar",
|
||||
"Archived boards" : "Tableros archivados",
|
||||
"Shared with you" : "Compartido con usted",
|
||||
"Cancel edit" : "Cancelar edición",
|
||||
"Board details" : "Detalles del tablero",
|
||||
"Edit board" : "Editar el tablero",
|
||||
"Unarchive board" : "Desarchivar tablero",
|
||||
|
||||
@@ -266,7 +266,6 @@ OC.L10N.register(
|
||||
"{count} comments, {unread} unread" : "Komentarze: {count}, nieprzeczytane: {unread}",
|
||||
"Todo items" : "Do zrobienia",
|
||||
"Edit card title" : "Edytuj nazwę karty",
|
||||
"Open link" : "Otwórz link",
|
||||
"Card deleted" : "Karta usunięta",
|
||||
"Edit title" : "Edytuj tytuł",
|
||||
"Assign to me" : "Przydziel do mnie",
|
||||
@@ -313,7 +312,6 @@ OC.L10N.register(
|
||||
"Clone cards" : "Klonuj karty",
|
||||
"Advanced options" : "Opcje zaawansowane",
|
||||
"Clone" : "Klonuj",
|
||||
"Export {boardTitle}" : "Eksportuj {boardTitle}",
|
||||
"Export" : "Eksportuj",
|
||||
"Loading filtered view" : "Wczytywanie przefiltrowanego widoku",
|
||||
"Today" : "Dzisiaj",
|
||||
|
||||
@@ -264,7 +264,6 @@
|
||||
"{count} comments, {unread} unread" : "Komentarze: {count}, nieprzeczytane: {unread}",
|
||||
"Todo items" : "Do zrobienia",
|
||||
"Edit card title" : "Edytuj nazwę karty",
|
||||
"Open link" : "Otwórz link",
|
||||
"Card deleted" : "Karta usunięta",
|
||||
"Edit title" : "Edytuj tytuł",
|
||||
"Assign to me" : "Przydziel do mnie",
|
||||
@@ -311,7 +310,6 @@
|
||||
"Clone cards" : "Klonuj karty",
|
||||
"Advanced options" : "Opcje zaawansowane",
|
||||
"Clone" : "Klonuj",
|
||||
"Export {boardTitle}" : "Eksportuj {boardTitle}",
|
||||
"Export" : "Eksportuj",
|
||||
"Loading filtered view" : "Wczytywanie przefiltrowanego widoku",
|
||||
"Today" : "Dzisiaj",
|
||||
|
||||
@@ -124,7 +124,6 @@ OC.L10N.register(
|
||||
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Овај коментар има више од %s карактера.\nДодат је као прилог картици под именом %s.\nДоступно је на URL адреси: %s.",
|
||||
"Attachments" : "Прилози",
|
||||
"File" : "Фајл",
|
||||
"date" : "датум",
|
||||
"Card not found" : "Картица није нађена",
|
||||
"Path is already shared with this card" : "Путања се већ дели са овом картицом",
|
||||
"Invalid date, date format must be YYYY-MM-DD" : "Неисправан датум, формат датума мора бити ГГГГ-ММ-ДД",
|
||||
@@ -240,7 +239,6 @@ OC.L10N.register(
|
||||
"Modified" : "Измењен",
|
||||
"Created" : "Направљен",
|
||||
"The title cannot be empty." : "Наслов не може бити празан.",
|
||||
"Cannot close unsaved card!" : "Не може да се затвори несачувана картица!",
|
||||
"Open in sidebar view" : "Отвори у бочном прегледу",
|
||||
"Open in bigger view" : "Отвори на већем приказу",
|
||||
"Comments" : "Коментари",
|
||||
@@ -255,7 +253,6 @@ OC.L10N.register(
|
||||
"Reply" : "Одговори",
|
||||
"Update" : "Ажурирај",
|
||||
"Write a description …" : "Напишите опис ...",
|
||||
"Could not save description" : "Не може да се сачува опис",
|
||||
"Description" : "Опис",
|
||||
"(Unsaved)" : "(несачуван)",
|
||||
"(Saving…)" : "(чувам…)",
|
||||
|
||||
@@ -122,7 +122,6 @@
|
||||
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Овај коментар има више од %s карактера.\nДодат је као прилог картици под именом %s.\nДоступно је на URL адреси: %s.",
|
||||
"Attachments" : "Прилози",
|
||||
"File" : "Фајл",
|
||||
"date" : "датум",
|
||||
"Card not found" : "Картица није нађена",
|
||||
"Path is already shared with this card" : "Путања се већ дели са овом картицом",
|
||||
"Invalid date, date format must be YYYY-MM-DD" : "Неисправан датум, формат датума мора бити ГГГГ-ММ-ДД",
|
||||
@@ -238,7 +237,6 @@
|
||||
"Modified" : "Измењен",
|
||||
"Created" : "Направљен",
|
||||
"The title cannot be empty." : "Наслов не може бити празан.",
|
||||
"Cannot close unsaved card!" : "Не може да се затвори несачувана картица!",
|
||||
"Open in sidebar view" : "Отвори у бочном прегледу",
|
||||
"Open in bigger view" : "Отвори на већем приказу",
|
||||
"Comments" : "Коментари",
|
||||
@@ -253,7 +251,6 @@
|
||||
"Reply" : "Одговори",
|
||||
"Update" : "Ажурирај",
|
||||
"Write a description …" : "Напишите опис ...",
|
||||
"Could not save description" : "Не може да се сачува опис",
|
||||
"Description" : "Опис",
|
||||
"(Unsaved)" : "(несачуван)",
|
||||
"(Saving…)" : "(чувам…)",
|
||||
|
||||
@@ -240,7 +240,6 @@ OC.L10N.register(
|
||||
"Modified" : "Değiştirilme",
|
||||
"Created" : "Oluşturulma",
|
||||
"The title cannot be empty." : "Başlık boş olamaz.",
|
||||
"Cannot close unsaved card!" : "Kaydedilmemiş bir kart kapatılamaz!",
|
||||
"Open in sidebar view" : "Kenar çubuğu görünümünde aç",
|
||||
"Open in bigger view" : "Daha büyük görünümde aç",
|
||||
"Comments" : "Yorumlar",
|
||||
@@ -255,7 +254,6 @@ OC.L10N.register(
|
||||
"Reply" : "Yanıtla",
|
||||
"Update" : "Güncelle",
|
||||
"Write a description …" : "Bir açıklama yazın …",
|
||||
"Could not save description" : "Açıklama kaydedilemedi",
|
||||
"Description" : "Açıklama",
|
||||
"(Unsaved)" : "(Kaydedilmemiş)",
|
||||
"(Saving…)" : "(Kaydediliyor…)",
|
||||
|
||||
@@ -238,7 +238,6 @@
|
||||
"Modified" : "Değiştirilme",
|
||||
"Created" : "Oluşturulma",
|
||||
"The title cannot be empty." : "Başlık boş olamaz.",
|
||||
"Cannot close unsaved card!" : "Kaydedilmemiş bir kart kapatılamaz!",
|
||||
"Open in sidebar view" : "Kenar çubuğu görünümünde aç",
|
||||
"Open in bigger view" : "Daha büyük görünümde aç",
|
||||
"Comments" : "Yorumlar",
|
||||
@@ -253,7 +252,6 @@
|
||||
"Reply" : "Yanıtla",
|
||||
"Update" : "Güncelle",
|
||||
"Write a description …" : "Bir açıklama yazın …",
|
||||
"Could not save description" : "Açıklama kaydedilemedi",
|
||||
"Description" : "Açıklama",
|
||||
"(Unsaved)" : "(Kaydedilmemiş)",
|
||||
"(Saving…)" : "(Kaydediliyor…)",
|
||||
|
||||
171
l10n/uk.js
171
l10n/uk.js
@@ -48,30 +48,8 @@ OC.L10N.register(
|
||||
"You have updated the due date of card {card} to {after}" : "Ви оновили дату завершення картки {card} на {after}",
|
||||
"{user} has updated the due date of card {card} to {after}" : "{user} оновив(-ла) дату завершення картки {card} на {after}",
|
||||
"You have added the tag {label} to card {card} in list {stack} on board {board}" : "Ви додали мітку {label} до картки {card} в списку {board}",
|
||||
"{user} has added the tag {label} to card {card} in list {stack} on board {board}" : "{user} додав тег {label} до карти {card} у списку {stack} на борту {board}",
|
||||
"You have removed the tag {label} from card {card} in list {stack} on board {board}" : "Ви видалили тег {label} з карти {card} у списку {stack} на борту {board}",
|
||||
"{user} has removed the tag {label} from card {card} in list {stack} on board {board}" : "{user} видалив тег {label} з карти {card} у списку {stack} на борту {board}",
|
||||
"You have assigned {assigneduser} to card {card} on board {board}" : "Ви призначили {assigneduser} на картку {card} на борту {board}",
|
||||
"{user} has assigned {assigneduser} to card {card} on board {board}" : "{user} призначив {assigneduser} на картку {card} на дошці {board}",
|
||||
"You have unassigned {assigneduser} from card {card} on board {board}" : "Ви скасували призначення {assigneduser} з карти {card} на борту {board}",
|
||||
"{user} has unassigned {assigneduser} from card {card} on board {board}" : "{user} скасував призначення {assigneduser} з карти {card} на дошці {board}",
|
||||
"You have moved the card {card} from list {stackBefore} to {stack}" : "Ви перемістили картку {card} зі списку {stackBefore} до {stack}",
|
||||
"{user} has moved the card {card} from list {stackBefore} to {stack}" : "{user} перемістив картку {card} зі списку {stackBefore} до {stack}",
|
||||
"You have added the attachment {attachment} to card {card}" : "Ви додали вкладення {attachment} до картки {card}",
|
||||
"{user} has added the attachment {attachment} to card {card}" : "{user} додав вкладення {attachment} до картки {card}",
|
||||
"You have updated the attachment {attachment} on card {card}" : "Ви оновили вкладення {attachment} на картці {card}",
|
||||
"{user} has updated the attachment {attachment} on card {card}" : " {user}оновив вкладення {attachment} на картці {card}",
|
||||
"You have deleted the attachment {attachment} from card {card}" : "Ви видалили вкладення {attachment} з картки {card}",
|
||||
"{user} has deleted the attachment {attachment} from card {card}" : "{user} видалив вкладення {attachment} з картки {card}",
|
||||
"You have restored the attachment {attachment} to card {card}" : "Ви відновили вкладення {attachment} до картки {card}",
|
||||
"{user} has restored the attachment {attachment} to card {card}" : "{user} відновив вкладення {attachment} до картки {card}",
|
||||
"You have commented on card {card}" : "Ви залишили коментар до картки {card}",
|
||||
"{user} has commented on card {card}" : "{user} прокоментував картку {card}",
|
||||
"Deck" : "Колода",
|
||||
"Changes in the <strong>Deck app</strong>" : "Зміни у застосунку <strong>Колода</strong>",
|
||||
"A <strong>board, list or card</strong> was changed" : "Змінено <strong>таблицю, список або картку</strong>",
|
||||
"A <strong>comment</strong> was created on a card" : "Створено <strong>коментар</strong> на картці",
|
||||
"A <strong>card description</strong> has been changed" : "Змінено <strong>опис картки</strong>",
|
||||
"The file was uploaded" : "Файл завантажено",
|
||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Файл для завантаження перевищує параметр upload_max_filesize у php.ini",
|
||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Розмір завантаженого файлу перевищує значення MAX_FILE_SIZE, яке було зазначено у HTML формі",
|
||||
@@ -81,66 +59,30 @@ OC.L10N.register(
|
||||
"Could not write file to disk" : "Неможливо записати файл на диск",
|
||||
"A PHP extension stopped the file upload" : "Розширення PHP призупинило завантаження файлу",
|
||||
"No file uploaded or file size exceeds maximum of %s" : "Немає завантажених файлів або розмір файлу перевищує максимум %s",
|
||||
"Invalid file type. Only JSON files are allowed." : "Неправильний тип файлу. Допускаються тільки файли у форматі JSON.",
|
||||
"Invalid JSON data" : "Неправильні дані JSON",
|
||||
"Failed to import board" : "Не вдалося імпортувати дошку",
|
||||
"Cards due today" : "Картки повинні бути оплачені сьогодні",
|
||||
"Cards due tomorrow" : "Картки на завтра",
|
||||
"Upcoming cards" : "Очікують на виконання",
|
||||
"Load more" : "Більше",
|
||||
"Welcome to Nextcloud Deck!" : "Ласкаво просимо до колоди Nextcloud!",
|
||||
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Картка \"%s\" на \"%s\" була призначена вам %s.",
|
||||
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} призначив вам карту {deck-card} на {deck-board}.",
|
||||
"The card \"%s\" on \"%s\" has reached its due date." : "Термін дії картки \"%s\" на \"%s\" закінчився.",
|
||||
"The card {deck-card} on {deck-board} has reached its due date." : "Термін дії карти {deck-card} на {deck-board} закінчився.",
|
||||
"%s has mentioned you in a comment on \"%s\"." : "%s згадав вас у коментарі до \"%s\".",
|
||||
"{user} has mentioned you in a comment on {deck-card}." : "{user} згадав вас у коментарі до {deck-card}.",
|
||||
"The board \"%s\" has been shared with you by %s." : "Дошку \"%s\" з вами поділився %s.",
|
||||
"{user} has shared {deck-board} with you." : "{user} поділився з вами {deck-board}.",
|
||||
"Deck board" : "Дошка Deck",
|
||||
"Owned by %1$s" : "Належить %1$s",
|
||||
"Deck boards, cards and comments" : "Дошки колод, карти та коментарі",
|
||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "З %1$s, в %2$s/%3$s, у власності %4$s",
|
||||
"Create a new deck card" : "Створіть нову карту колоди",
|
||||
"Card comments" : "Коментарі до картки",
|
||||
"%s on %s" : "%s на %s",
|
||||
"Deck boards and cards" : "Колода та карти",
|
||||
"No data was provided to create an attachment." : "Для створення вкладення не було надано жодних даних.",
|
||||
"Finished" : "Завершено",
|
||||
"To review" : "На перегляд",
|
||||
"Action needed" : "Потребує дій",
|
||||
"Later" : "Пізніше",
|
||||
"copy" : "копіювати",
|
||||
"Read more inside" : "Читайте більше всередині",
|
||||
"Custom lists - click to rename!" : "Користувацькі списки - натисніть, щоб перейменувати!",
|
||||
"To Do" : "Зробити",
|
||||
"In Progress" : "В процесі",
|
||||
"Done" : "Готово",
|
||||
"1. Open to learn more about boards and cards" : "1. Відкриті, щоб дізнатися більше про дошки та картки",
|
||||
"2. Drag cards left and right, up and down" : "2. Перетягування карток вліво і вправо, вгору і вниз",
|
||||
"3. Apply rich formatting and link content" : "3. Застосовуйте багате форматування та посилання на вміст",
|
||||
"4. Share, comment and collaborate!" : "4. Діліться, коментуйте та співпрацюйте!",
|
||||
"Create your first card!" : "Створіть свою першу листівку!",
|
||||
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Цей коментар містить більше %s символів.\nДодано як вкладення до картки з іменем %s.\nДоступний за URL: %s.",
|
||||
"Attachments" : "Вкладення",
|
||||
"File" : "Файл",
|
||||
"date" : "дата",
|
||||
"Card not found" : "Картку не знайдено",
|
||||
"Path is already shared with this card" : "Шлях вже є спільним з цією карткою",
|
||||
"Invalid date, date format must be YYYY-MM-DD" : "Недійсна дата, формат дати має бути РРРР-ММ-ДД",
|
||||
"Personal planning and team project organization" : "Особисте планування та організація командних проектів",
|
||||
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in Markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 📎 Attach files and embed them in your Markdown description\n- 💬 Discuss with your team using comments\n- ⚡ Keep track of changes in the activity stream\n- 🚀 Get your project organized" : "Deck - це інструмент організації в стилі канбан, призначений для особистого планування та організації проектів для команд, інтегрованих з Nextcloud.\n\n\n- 📥 Додавайте свої завдання на картки та впорядковуйте їх\n- 📄 Записуйте додаткові нотатки в Markdown\n- 🔖 Призначайте мітки для ще кращої організації\n- 👥 Діліться з командою, друзями чи родиною\n- Прикріплюйте файли та вбудовуйте їх в опис Markdown\n- 💬 Обговорюйте зі своєю командою за допомогою коментарів\n- ⚡ Відстежуйте зміни в потоці активностей\n- 🚀 Організуйте свій проект",
|
||||
"Add board" : "Додати дошку",
|
||||
"Card details" : "Деталі картки",
|
||||
"Select the board to link to a project" : "Виберіть дошку для прив'зки до проєкту",
|
||||
"Search by board title" : "Шукати за назвою дошки",
|
||||
"Select board" : "Вибрати дошку",
|
||||
"Move/copy card" : "Картка переміщення/копіювання",
|
||||
"Select a board" : "Вибрати дошку",
|
||||
"No lists available" : "Списки відсутні",
|
||||
"Select a list" : "Виберіть список",
|
||||
"Move card" : "Пересунути картку",
|
||||
"Copy card" : "Копіювальна картка",
|
||||
"Select the card to link to a project" : "Виберіть картку для прив'язки до проєкту",
|
||||
"Link to card" : "Прив'язати до картки",
|
||||
"Select a card" : "Вибрати картку",
|
||||
@@ -161,27 +103,19 @@ OC.L10N.register(
|
||||
"Filter by tag" : "Відібрати за мітками",
|
||||
"Filter by assigned user" : "Відібрати за призначеним користувачем",
|
||||
"Unassigned" : "Скасовано призначення",
|
||||
"Filter by status" : "Фільтр за статусом",
|
||||
"Open and completed" : "Відкрито та завершено",
|
||||
"Open" : "Відкрити",
|
||||
"Completed" : "Виконано",
|
||||
"Filter by due date" : "Відібрати за датою завершення",
|
||||
"Overdue" : "Протерміновано",
|
||||
"Next 24 hours" : "Наступні 24 години",
|
||||
"Next 7 days" : "Наступні 7 днів",
|
||||
"Next 30 days" : "Наступні 30 днів",
|
||||
"No due date" : "Без дати завершення",
|
||||
"Clear filter" : "Очистити фільтр",
|
||||
"View Modes" : "Режими перегляду",
|
||||
"Toggle View Modes" : "Перемикання режимів перегляду",
|
||||
"Hide archived cards" : "Приховати архівні картки",
|
||||
"Show archived cards" : "Показати архівні картки",
|
||||
"Toggle compact mode" : "Перемкнути компактний вигляд",
|
||||
"Hide card cover images" : "Приховати зображення обкладинок карток",
|
||||
"Show card cover images" : "Показати зображення обкладинки картки",
|
||||
"Open details" : "Показати деталі",
|
||||
"Details" : "Деталі",
|
||||
"Currently present people" : "Наразі присутні люди",
|
||||
"Loading board" : "Завантаження дошки",
|
||||
"Board not found" : "Дошку не знайдено",
|
||||
"Create a new list to add cards to this board" : "Створіть список щоб додати картки на цю дошку",
|
||||
@@ -192,62 +126,35 @@ OC.L10N.register(
|
||||
"Deleted lists" : "Вилучені списки",
|
||||
"Undo" : "Скасувати",
|
||||
"Deleted cards" : "Вилучені картки",
|
||||
"Failed to create share with {displayName}" : "Не вдалося створити спільний доступ з {displayName}",
|
||||
"Are you sure you want to transfer the board {title} to {user}?" : "Ви впевнені, що хочете перенести дошку {title} користувачеві {user}?",
|
||||
"Transfer the board." : "Перенесіть дошку.",
|
||||
"Transfer" : "Передати",
|
||||
"The board has been transferred to {user}" : "Дошка була передана {user}",
|
||||
"Failed to transfer the board to {user}" : "Не вдалося передати дошку користувачеві {user}",
|
||||
"Share board with a user, group or team …" : "Спільний доступ до дошки з користувачем, групою або командою ...",
|
||||
"Searching for users, groups and teams …" : "Пошук користувачів, груп і команд ...",
|
||||
"No participants found" : "Учасників не знайдено",
|
||||
"Board owner" : "Власник дошки",
|
||||
"(Group)" : "(група)",
|
||||
"(Team)" : "(Команда)",
|
||||
"Can edit" : "Може редагувати",
|
||||
"Can share" : "Can share",
|
||||
"Can manage" : "Може керувати",
|
||||
"Owner" : "Власник",
|
||||
"Delete" : "Вилучити",
|
||||
"List deleted" : "Список видалено",
|
||||
"Edit list title" : "Редагувати заголовок списку",
|
||||
"Archive all cards" : "Архівувати всі картки",
|
||||
"Unarchive all cards" : "Розархівувати всі картки",
|
||||
"Delete list" : "Вилучити список",
|
||||
"Archive all cards in this list" : "Архівуйте всі картки в цьому списку",
|
||||
"Unarchive all cards in this list" : "Розархівувати всі картки в цьому списку",
|
||||
"Add a new card" : "Додати нову картку",
|
||||
"Card name" : "Назва картки",
|
||||
"title and color value must be provided" : "потрібно зазначити назву та колір",
|
||||
"Edit" : "Редагувати",
|
||||
"Add a new tag" : "Додати нову мітку",
|
||||
"Board name" : "Назва дошки",
|
||||
"Members" : "Учасники",
|
||||
"Assign to users/groups/team" : "Призначити користувачам/групам/команді",
|
||||
"Assign a user to this card…" : "Призначити користувачеві цю картку",
|
||||
"Select a user to assign to this card…" : "Виберіть користувача, якого слід призначити на цю картку...",
|
||||
"File to share" : "Виберіть файл для надання доступу",
|
||||
"Invalid path selected" : "Вибрано неправильний шлях",
|
||||
"Upload new files" : "Додати файл",
|
||||
"Share from Files" : "Відкрити Файли",
|
||||
"Pending share" : "Очікувана акція",
|
||||
"Add this attachment" : "Долучити вкладення",
|
||||
"Show in Files" : "Показати в Файлах",
|
||||
"Download" : "Звантажити",
|
||||
"Remove attachment" : "Зніміть вкладення",
|
||||
"Delete Attachment" : "Забрати вкладення",
|
||||
"Restore Attachment" : "Відновити вкладення",
|
||||
"Modified" : "Змінено",
|
||||
"Created" : "Створено",
|
||||
"The title cannot be empty." : "Заголовок не може бути порожнім.",
|
||||
"Cannot close unsaved card!" : "Неможливо закрити незбережену картку!",
|
||||
"Open in sidebar view" : "Відкрити у вигляді бічної панелі",
|
||||
"Open in bigger view" : "Відкрити в більшому розмірі",
|
||||
"Comments" : "Коментарі",
|
||||
"Failed to load comments" : "Не вдалося завантажити коментарі",
|
||||
"No comments yet. Begin the discussion!" : "Коментарів немає, почніть обговорення!",
|
||||
"The comment cannot be empty." : "Коментар не може бути порожнім.",
|
||||
"The comment cannot be longer than 1000 characters." : "Коментар не може бути довшим за 1000 символів.",
|
||||
"Save" : "Зберегти",
|
||||
"Created:" : "Створено:",
|
||||
"In reply to" : "У відповідь",
|
||||
@@ -255,7 +162,6 @@ OC.L10N.register(
|
||||
"Reply" : "Відповісти",
|
||||
"Update" : "Оновлення",
|
||||
"Write a description …" : "Додайте опис ...",
|
||||
"Could not save description" : "Не вдалося зберегти опис",
|
||||
"Description" : "Опис",
|
||||
"(Unsaved)" : "(Не збережено)",
|
||||
"(Saving…)" : "(Зберігання...)",
|
||||
@@ -265,150 +171,73 @@ OC.L10N.register(
|
||||
"Add Attachment" : "Долучити вкладення",
|
||||
"Choose attachment" : "Вибрати вкладення",
|
||||
"Select Date" : "Вкажіть дату",
|
||||
"Later today – {timeLocale}" : "Пізніше сьогодні - {timeLocale}",
|
||||
"Set due date for later today" : "Встановіть термін сплати на сьогоднішній день",
|
||||
"Tomorrow – {timeLocale}" : "Завтра - {timeLocale}",
|
||||
"Set due date for tomorrow" : "Встановіть дедлайн на завтра",
|
||||
"This weekend – {timeLocale}" : "На цих вихідних - {timeLocale}",
|
||||
"Set due date for this weekend" : "Встановіть термін виконання на ці вихідні",
|
||||
"Next week – {timeLocale}" : "Наступного тижня - {timeLocale}",
|
||||
"Set due date for next week" : "Встановіть термін виконання на наступний тиждень",
|
||||
"Assign a due date to this card…" : "Призначте цій картці термін оплати...",
|
||||
"Set a due date" : "Встановити дату завершення",
|
||||
"Add due date" : "Додати термін сплати",
|
||||
"Choose a date" : "Виберіть дату",
|
||||
"Remove due date" : "Вилучити до",
|
||||
"Mark as done" : "Позначити як виконане",
|
||||
"Due at:" : "На часі:",
|
||||
"Not done" : "Ще не все.",
|
||||
"Unarchive card" : "Розархівувати картку",
|
||||
"Archive card" : "Архівувати картку",
|
||||
"Assign a tag to this card…" : "Додати мітку до цієї картки",
|
||||
"Select or create a tag…" : "Виберіть або створіть тег...",
|
||||
"Create a new tag:" : "Створіть новий тег:",
|
||||
"(group)" : "(група)",
|
||||
"{count} comments, {unread} unread" : "{count} коментарів, {unread} непрочитаних",
|
||||
"Todo items" : "Пункти завдань",
|
||||
"Edit card title" : "Редагувати заголовок картки",
|
||||
"Open link" : "Відкрити посилання",
|
||||
"Card deleted" : "Картку видалено",
|
||||
"Edit title" : "Редагувати",
|
||||
"Assign to me" : "Призначити мені",
|
||||
"Unassign myself" : "Зняти з себе повноваження.",
|
||||
"Mark as not done" : "Позначити як не виконано",
|
||||
"Delete card" : "Вилучити картку",
|
||||
"seconds ago" : "секунд тому",
|
||||
"Keyboard shortcuts" : "Скорочення",
|
||||
"Boost your productivity using Deck with keyboard shortcuts." : "Підвищуйте свою продуктивність, використовуючи Deck за допомогою комбінацій клавіш.",
|
||||
"Board actions" : "Дії правління",
|
||||
"Keyboard shortcut" : "Сполучення клавіш",
|
||||
"Action" : "Дія ",
|
||||
"Shift" : "Shift",
|
||||
"Scroll" : "Прокрутка",
|
||||
"Scroll sideways" : "Прокрутіть вбік",
|
||||
"Navigate between cards" : "Перехід між картками",
|
||||
"Esc" : "Esc",
|
||||
"Close card details" : "Закрити дані картки",
|
||||
"Ctrl" : "Ctrl",
|
||||
"Search" : "Пошук",
|
||||
"Show card filters" : "Показати фільтри карток",
|
||||
"Clear card filters" : "Очистити фільтри карток",
|
||||
"Show help dialog" : "Показати діалогове вікно довідки",
|
||||
"Card actions" : "Дії з карткою",
|
||||
"The following actions can be triggered on the currently highlighted card" : "На поточній виділеній картці можна виконати такі дії",
|
||||
"Enter" : "Ввід",
|
||||
"Space" : "Простір",
|
||||
"Open card details" : "Відкрити реквізити картки",
|
||||
"Edit the card title" : "Відредагуйте назву картки",
|
||||
"Assign yourself to the current card" : "Приєднайте себе до поточної картки",
|
||||
"Archive/unarchive the current card" : "Архівувати/розархівувати поточну картку",
|
||||
"Mark card as completed/not completed" : "Позначте картку як завершену/не завершену",
|
||||
"Open card menu" : "Відкрити меню картки",
|
||||
"All boards" : "Усі дошки",
|
||||
"Archived boards" : "Архівні дошки",
|
||||
"Shared with you" : "Вам надано доступ",
|
||||
"Deck settings" : "Налаштування колоди",
|
||||
"Use bigger card view" : "Режим перегляду зі збільшеними картками",
|
||||
"Show card ID badge" : "Покажіть бейдж з ідентифікаційною карткою",
|
||||
"Show boards in calendar/tasks" : "Показувати дошки в календарі та завданнях",
|
||||
"Limit board creation to some groups" : "Обмежити створення дошки для деяких груп",
|
||||
"Users outside of those groups will not be able to create their own boards, but will still be able to work on boards that have been shared with them." : "Користувачі поза цими групами не зможуть створювати власні дошки, але зможуть працювати на дошках, до яких їм надано спільний доступ.",
|
||||
"Cancel edit" : "Скасувати редагування",
|
||||
"Save board" : "Зберегти дошку",
|
||||
"Board {0} deleted" : "Дошку {0} вилучено",
|
||||
"All cards" : "Всі карти",
|
||||
"Only assigned cards" : "Тільки призначені картки",
|
||||
"No reminder" : "Відсутні нагадування",
|
||||
"An error occurred" : "Виникла помилка",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Ви впевнені, що хочете вилучити дошку {title}? Це призведе до видалення всіх даних цієї дошки, включно архівні картки.",
|
||||
"Delete the board?" : "Вилучити дошку?",
|
||||
"Exporting board..." : "Експортна дошка...",
|
||||
"Board details" : "Деталі дошки",
|
||||
"Edit board" : "Редагувати дошку",
|
||||
"Clone board" : "Копіювати дошку",
|
||||
"Unarchive board" : "Розархівувати дошку",
|
||||
"Archive board" : "Архівувати дошку",
|
||||
"Export board" : "Дошка експорту",
|
||||
"Turn on due date reminders" : "Нагадування про термін виконання",
|
||||
"Turn off due date reminders" : "Вимкнути нагадування про терміни виконання",
|
||||
"Due date reminders" : "Нагадування про терміни виконання",
|
||||
"Assigned cards" : "Призначені картки",
|
||||
"No notifications" : "Немає сповіщень",
|
||||
"Delete board" : "Вилучити дошку",
|
||||
"Importing board..." : "Імпортна дошка...",
|
||||
"Board imported successfully" : "Плату успішно імпортовано",
|
||||
"Import board" : "Імпортна дошка",
|
||||
"Clone {boardTitle}" : "Клон {boardTitle}",
|
||||
"Clone cards" : "Копіювати картки",
|
||||
"Clone assignments" : "Призначення клонів",
|
||||
"Clone labels" : "Етикетки клонів",
|
||||
"Clone due dates" : "Клонувати дві дати",
|
||||
"Advanced options" : "Розширені налаштування",
|
||||
"Move all cards to the first list" : "Перемістіть усі картки до першого списку",
|
||||
"Restore archived cards" : "Відновлення заархівованих карток",
|
||||
"Clone" : "Копіювати",
|
||||
"Export {boardTitle}" : "Експорт {boardTitle}",
|
||||
"Export as JSON" : "Експорт як JSON",
|
||||
"Export as CSV" : "Експорт у CSV",
|
||||
"Note: Only the JSON format is supported for importing back into the Deck app." : "Примітка: Для імпорту в додаток Deck підтримується лише формат JSON.",
|
||||
"Export" : "Експортувати",
|
||||
"Loading filtered view" : "Завантаження відфільтрованого перегляду",
|
||||
"Today" : "Сьогодні",
|
||||
"Tomorrow" : "Завтра",
|
||||
"No due" : "Без призначеної дати",
|
||||
"Search for {searchQuery} in all boards" : "Шукати {searchQuery} на всіх дошках оголошень",
|
||||
"No results found" : "Не знайдено жодного результату",
|
||||
"Deck board {name}\n* Last modified on {lastMod}" : "Колода {name}\n* Востаннє змінено на {lastMod}",
|
||||
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Створено на {created}\n* Востаннє змінено на {lastMod}\n* {nbAttachments} вкладення\n* {nbComments} коментарі",
|
||||
"{nbCards} cards" : "{nbCards} картки",
|
||||
"Due on {date}" : "До {date}",
|
||||
"{stack} in {board}" : "{stack} в {board}",
|
||||
"Click to expand description" : "Натисніть, щоб розгорнути опис",
|
||||
"Click to expand comment" : "Натисніть, щоб розгорнути коментар",
|
||||
"Create card" : "Створити картку",
|
||||
"Create a new card" : "Створити нову картку",
|
||||
"Card title" : "Заголовок картки",
|
||||
"Creating the new card …" : "Створення нової картки ...",
|
||||
"Card \"{card}\" was added to \"{board}\"" : "Картку \"{card}\" додано до \"{board}\"",
|
||||
"Open card" : "Відкрити картку",
|
||||
"Close" : "Закрити",
|
||||
"No upcoming cards" : "Немає майбутніх завдань",
|
||||
"upcoming cards today" : "найближчі картки на сьогодні",
|
||||
"upcoming cards tomorrow" : "найближчі картки завтра",
|
||||
"upcoming cards" : "очікують на виконання",
|
||||
"New card" : "Нова картка",
|
||||
"Link to a board" : "Прив'язати до дошки",
|
||||
"Link to a card" : "Прив'язати до картки",
|
||||
"Create a card" : "Створити картку",
|
||||
"Message from {author} in {conversationName}" : "Повідомлення від {author} у {conversationName}",
|
||||
"Something went wrong" : "От халепа!",
|
||||
"Failed to upload {name}" : "Не вдалося завантажити {name}",
|
||||
"Maximum file size of {size} exceeded" : "Досягнуто максимальний розмір файлу {size}",
|
||||
"Assigned users" : "Призначені користувачі",
|
||||
"Due date" : "Дата виконання",
|
||||
"Error creating the share" : "Помилка створення спільного доступу",
|
||||
"Share with a Deck card" : "Поділіться з картою з колоди",
|
||||
"Share {file} with a Deck card" : "Поділіться {file} з картою колоди",
|
||||
"Share" : "Спільний доступ",
|
||||
"Personal" : "Особисте",
|
||||
"To do" : "Заплановано",
|
||||
|
||||
171
l10n/uk.json
171
l10n/uk.json
@@ -46,30 +46,8 @@
|
||||
"You have updated the due date of card {card} to {after}" : "Ви оновили дату завершення картки {card} на {after}",
|
||||
"{user} has updated the due date of card {card} to {after}" : "{user} оновив(-ла) дату завершення картки {card} на {after}",
|
||||
"You have added the tag {label} to card {card} in list {stack} on board {board}" : "Ви додали мітку {label} до картки {card} в списку {board}",
|
||||
"{user} has added the tag {label} to card {card} in list {stack} on board {board}" : "{user} додав тег {label} до карти {card} у списку {stack} на борту {board}",
|
||||
"You have removed the tag {label} from card {card} in list {stack} on board {board}" : "Ви видалили тег {label} з карти {card} у списку {stack} на борту {board}",
|
||||
"{user} has removed the tag {label} from card {card} in list {stack} on board {board}" : "{user} видалив тег {label} з карти {card} у списку {stack} на борту {board}",
|
||||
"You have assigned {assigneduser} to card {card} on board {board}" : "Ви призначили {assigneduser} на картку {card} на борту {board}",
|
||||
"{user} has assigned {assigneduser} to card {card} on board {board}" : "{user} призначив {assigneduser} на картку {card} на дошці {board}",
|
||||
"You have unassigned {assigneduser} from card {card} on board {board}" : "Ви скасували призначення {assigneduser} з карти {card} на борту {board}",
|
||||
"{user} has unassigned {assigneduser} from card {card} on board {board}" : "{user} скасував призначення {assigneduser} з карти {card} на дошці {board}",
|
||||
"You have moved the card {card} from list {stackBefore} to {stack}" : "Ви перемістили картку {card} зі списку {stackBefore} до {stack}",
|
||||
"{user} has moved the card {card} from list {stackBefore} to {stack}" : "{user} перемістив картку {card} зі списку {stackBefore} до {stack}",
|
||||
"You have added the attachment {attachment} to card {card}" : "Ви додали вкладення {attachment} до картки {card}",
|
||||
"{user} has added the attachment {attachment} to card {card}" : "{user} додав вкладення {attachment} до картки {card}",
|
||||
"You have updated the attachment {attachment} on card {card}" : "Ви оновили вкладення {attachment} на картці {card}",
|
||||
"{user} has updated the attachment {attachment} on card {card}" : " {user}оновив вкладення {attachment} на картці {card}",
|
||||
"You have deleted the attachment {attachment} from card {card}" : "Ви видалили вкладення {attachment} з картки {card}",
|
||||
"{user} has deleted the attachment {attachment} from card {card}" : "{user} видалив вкладення {attachment} з картки {card}",
|
||||
"You have restored the attachment {attachment} to card {card}" : "Ви відновили вкладення {attachment} до картки {card}",
|
||||
"{user} has restored the attachment {attachment} to card {card}" : "{user} відновив вкладення {attachment} до картки {card}",
|
||||
"You have commented on card {card}" : "Ви залишили коментар до картки {card}",
|
||||
"{user} has commented on card {card}" : "{user} прокоментував картку {card}",
|
||||
"Deck" : "Колода",
|
||||
"Changes in the <strong>Deck app</strong>" : "Зміни у застосунку <strong>Колода</strong>",
|
||||
"A <strong>board, list or card</strong> was changed" : "Змінено <strong>таблицю, список або картку</strong>",
|
||||
"A <strong>comment</strong> was created on a card" : "Створено <strong>коментар</strong> на картці",
|
||||
"A <strong>card description</strong> has been changed" : "Змінено <strong>опис картки</strong>",
|
||||
"The file was uploaded" : "Файл завантажено",
|
||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Файл для завантаження перевищує параметр upload_max_filesize у php.ini",
|
||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Розмір завантаженого файлу перевищує значення MAX_FILE_SIZE, яке було зазначено у HTML формі",
|
||||
@@ -79,66 +57,30 @@
|
||||
"Could not write file to disk" : "Неможливо записати файл на диск",
|
||||
"A PHP extension stopped the file upload" : "Розширення PHP призупинило завантаження файлу",
|
||||
"No file uploaded or file size exceeds maximum of %s" : "Немає завантажених файлів або розмір файлу перевищує максимум %s",
|
||||
"Invalid file type. Only JSON files are allowed." : "Неправильний тип файлу. Допускаються тільки файли у форматі JSON.",
|
||||
"Invalid JSON data" : "Неправильні дані JSON",
|
||||
"Failed to import board" : "Не вдалося імпортувати дошку",
|
||||
"Cards due today" : "Картки повинні бути оплачені сьогодні",
|
||||
"Cards due tomorrow" : "Картки на завтра",
|
||||
"Upcoming cards" : "Очікують на виконання",
|
||||
"Load more" : "Більше",
|
||||
"Welcome to Nextcloud Deck!" : "Ласкаво просимо до колоди Nextcloud!",
|
||||
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Картка \"%s\" на \"%s\" була призначена вам %s.",
|
||||
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} призначив вам карту {deck-card} на {deck-board}.",
|
||||
"The card \"%s\" on \"%s\" has reached its due date." : "Термін дії картки \"%s\" на \"%s\" закінчився.",
|
||||
"The card {deck-card} on {deck-board} has reached its due date." : "Термін дії карти {deck-card} на {deck-board} закінчився.",
|
||||
"%s has mentioned you in a comment on \"%s\"." : "%s згадав вас у коментарі до \"%s\".",
|
||||
"{user} has mentioned you in a comment on {deck-card}." : "{user} згадав вас у коментарі до {deck-card}.",
|
||||
"The board \"%s\" has been shared with you by %s." : "Дошку \"%s\" з вами поділився %s.",
|
||||
"{user} has shared {deck-board} with you." : "{user} поділився з вами {deck-board}.",
|
||||
"Deck board" : "Дошка Deck",
|
||||
"Owned by %1$s" : "Належить %1$s",
|
||||
"Deck boards, cards and comments" : "Дошки колод, карти та коментарі",
|
||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "З %1$s, в %2$s/%3$s, у власності %4$s",
|
||||
"Create a new deck card" : "Створіть нову карту колоди",
|
||||
"Card comments" : "Коментарі до картки",
|
||||
"%s on %s" : "%s на %s",
|
||||
"Deck boards and cards" : "Колода та карти",
|
||||
"No data was provided to create an attachment." : "Для створення вкладення не було надано жодних даних.",
|
||||
"Finished" : "Завершено",
|
||||
"To review" : "На перегляд",
|
||||
"Action needed" : "Потребує дій",
|
||||
"Later" : "Пізніше",
|
||||
"copy" : "копіювати",
|
||||
"Read more inside" : "Читайте більше всередині",
|
||||
"Custom lists - click to rename!" : "Користувацькі списки - натисніть, щоб перейменувати!",
|
||||
"To Do" : "Зробити",
|
||||
"In Progress" : "В процесі",
|
||||
"Done" : "Готово",
|
||||
"1. Open to learn more about boards and cards" : "1. Відкриті, щоб дізнатися більше про дошки та картки",
|
||||
"2. Drag cards left and right, up and down" : "2. Перетягування карток вліво і вправо, вгору і вниз",
|
||||
"3. Apply rich formatting and link content" : "3. Застосовуйте багате форматування та посилання на вміст",
|
||||
"4. Share, comment and collaborate!" : "4. Діліться, коментуйте та співпрацюйте!",
|
||||
"Create your first card!" : "Створіть свою першу листівку!",
|
||||
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Цей коментар містить більше %s символів.\nДодано як вкладення до картки з іменем %s.\nДоступний за URL: %s.",
|
||||
"Attachments" : "Вкладення",
|
||||
"File" : "Файл",
|
||||
"date" : "дата",
|
||||
"Card not found" : "Картку не знайдено",
|
||||
"Path is already shared with this card" : "Шлях вже є спільним з цією карткою",
|
||||
"Invalid date, date format must be YYYY-MM-DD" : "Недійсна дата, формат дати має бути РРРР-ММ-ДД",
|
||||
"Personal planning and team project organization" : "Особисте планування та організація командних проектів",
|
||||
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in Markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 📎 Attach files and embed them in your Markdown description\n- 💬 Discuss with your team using comments\n- ⚡ Keep track of changes in the activity stream\n- 🚀 Get your project organized" : "Deck - це інструмент організації в стилі канбан, призначений для особистого планування та організації проектів для команд, інтегрованих з Nextcloud.\n\n\n- 📥 Додавайте свої завдання на картки та впорядковуйте їх\n- 📄 Записуйте додаткові нотатки в Markdown\n- 🔖 Призначайте мітки для ще кращої організації\n- 👥 Діліться з командою, друзями чи родиною\n- Прикріплюйте файли та вбудовуйте їх в опис Markdown\n- 💬 Обговорюйте зі своєю командою за допомогою коментарів\n- ⚡ Відстежуйте зміни в потоці активностей\n- 🚀 Організуйте свій проект",
|
||||
"Add board" : "Додати дошку",
|
||||
"Card details" : "Деталі картки",
|
||||
"Select the board to link to a project" : "Виберіть дошку для прив'зки до проєкту",
|
||||
"Search by board title" : "Шукати за назвою дошки",
|
||||
"Select board" : "Вибрати дошку",
|
||||
"Move/copy card" : "Картка переміщення/копіювання",
|
||||
"Select a board" : "Вибрати дошку",
|
||||
"No lists available" : "Списки відсутні",
|
||||
"Select a list" : "Виберіть список",
|
||||
"Move card" : "Пересунути картку",
|
||||
"Copy card" : "Копіювальна картка",
|
||||
"Select the card to link to a project" : "Виберіть картку для прив'язки до проєкту",
|
||||
"Link to card" : "Прив'язати до картки",
|
||||
"Select a card" : "Вибрати картку",
|
||||
@@ -159,27 +101,19 @@
|
||||
"Filter by tag" : "Відібрати за мітками",
|
||||
"Filter by assigned user" : "Відібрати за призначеним користувачем",
|
||||
"Unassigned" : "Скасовано призначення",
|
||||
"Filter by status" : "Фільтр за статусом",
|
||||
"Open and completed" : "Відкрито та завершено",
|
||||
"Open" : "Відкрити",
|
||||
"Completed" : "Виконано",
|
||||
"Filter by due date" : "Відібрати за датою завершення",
|
||||
"Overdue" : "Протерміновано",
|
||||
"Next 24 hours" : "Наступні 24 години",
|
||||
"Next 7 days" : "Наступні 7 днів",
|
||||
"Next 30 days" : "Наступні 30 днів",
|
||||
"No due date" : "Без дати завершення",
|
||||
"Clear filter" : "Очистити фільтр",
|
||||
"View Modes" : "Режими перегляду",
|
||||
"Toggle View Modes" : "Перемикання режимів перегляду",
|
||||
"Hide archived cards" : "Приховати архівні картки",
|
||||
"Show archived cards" : "Показати архівні картки",
|
||||
"Toggle compact mode" : "Перемкнути компактний вигляд",
|
||||
"Hide card cover images" : "Приховати зображення обкладинок карток",
|
||||
"Show card cover images" : "Показати зображення обкладинки картки",
|
||||
"Open details" : "Показати деталі",
|
||||
"Details" : "Деталі",
|
||||
"Currently present people" : "Наразі присутні люди",
|
||||
"Loading board" : "Завантаження дошки",
|
||||
"Board not found" : "Дошку не знайдено",
|
||||
"Create a new list to add cards to this board" : "Створіть список щоб додати картки на цю дошку",
|
||||
@@ -190,62 +124,35 @@
|
||||
"Deleted lists" : "Вилучені списки",
|
||||
"Undo" : "Скасувати",
|
||||
"Deleted cards" : "Вилучені картки",
|
||||
"Failed to create share with {displayName}" : "Не вдалося створити спільний доступ з {displayName}",
|
||||
"Are you sure you want to transfer the board {title} to {user}?" : "Ви впевнені, що хочете перенести дошку {title} користувачеві {user}?",
|
||||
"Transfer the board." : "Перенесіть дошку.",
|
||||
"Transfer" : "Передати",
|
||||
"The board has been transferred to {user}" : "Дошка була передана {user}",
|
||||
"Failed to transfer the board to {user}" : "Не вдалося передати дошку користувачеві {user}",
|
||||
"Share board with a user, group or team …" : "Спільний доступ до дошки з користувачем, групою або командою ...",
|
||||
"Searching for users, groups and teams …" : "Пошук користувачів, груп і команд ...",
|
||||
"No participants found" : "Учасників не знайдено",
|
||||
"Board owner" : "Власник дошки",
|
||||
"(Group)" : "(група)",
|
||||
"(Team)" : "(Команда)",
|
||||
"Can edit" : "Може редагувати",
|
||||
"Can share" : "Can share",
|
||||
"Can manage" : "Може керувати",
|
||||
"Owner" : "Власник",
|
||||
"Delete" : "Вилучити",
|
||||
"List deleted" : "Список видалено",
|
||||
"Edit list title" : "Редагувати заголовок списку",
|
||||
"Archive all cards" : "Архівувати всі картки",
|
||||
"Unarchive all cards" : "Розархівувати всі картки",
|
||||
"Delete list" : "Вилучити список",
|
||||
"Archive all cards in this list" : "Архівуйте всі картки в цьому списку",
|
||||
"Unarchive all cards in this list" : "Розархівувати всі картки в цьому списку",
|
||||
"Add a new card" : "Додати нову картку",
|
||||
"Card name" : "Назва картки",
|
||||
"title and color value must be provided" : "потрібно зазначити назву та колір",
|
||||
"Edit" : "Редагувати",
|
||||
"Add a new tag" : "Додати нову мітку",
|
||||
"Board name" : "Назва дошки",
|
||||
"Members" : "Учасники",
|
||||
"Assign to users/groups/team" : "Призначити користувачам/групам/команді",
|
||||
"Assign a user to this card…" : "Призначити користувачеві цю картку",
|
||||
"Select a user to assign to this card…" : "Виберіть користувача, якого слід призначити на цю картку...",
|
||||
"File to share" : "Виберіть файл для надання доступу",
|
||||
"Invalid path selected" : "Вибрано неправильний шлях",
|
||||
"Upload new files" : "Додати файл",
|
||||
"Share from Files" : "Відкрити Файли",
|
||||
"Pending share" : "Очікувана акція",
|
||||
"Add this attachment" : "Долучити вкладення",
|
||||
"Show in Files" : "Показати в Файлах",
|
||||
"Download" : "Звантажити",
|
||||
"Remove attachment" : "Зніміть вкладення",
|
||||
"Delete Attachment" : "Забрати вкладення",
|
||||
"Restore Attachment" : "Відновити вкладення",
|
||||
"Modified" : "Змінено",
|
||||
"Created" : "Створено",
|
||||
"The title cannot be empty." : "Заголовок не може бути порожнім.",
|
||||
"Cannot close unsaved card!" : "Неможливо закрити незбережену картку!",
|
||||
"Open in sidebar view" : "Відкрити у вигляді бічної панелі",
|
||||
"Open in bigger view" : "Відкрити в більшому розмірі",
|
||||
"Comments" : "Коментарі",
|
||||
"Failed to load comments" : "Не вдалося завантажити коментарі",
|
||||
"No comments yet. Begin the discussion!" : "Коментарів немає, почніть обговорення!",
|
||||
"The comment cannot be empty." : "Коментар не може бути порожнім.",
|
||||
"The comment cannot be longer than 1000 characters." : "Коментар не може бути довшим за 1000 символів.",
|
||||
"Save" : "Зберегти",
|
||||
"Created:" : "Створено:",
|
||||
"In reply to" : "У відповідь",
|
||||
@@ -253,7 +160,6 @@
|
||||
"Reply" : "Відповісти",
|
||||
"Update" : "Оновлення",
|
||||
"Write a description …" : "Додайте опис ...",
|
||||
"Could not save description" : "Не вдалося зберегти опис",
|
||||
"Description" : "Опис",
|
||||
"(Unsaved)" : "(Не збережено)",
|
||||
"(Saving…)" : "(Зберігання...)",
|
||||
@@ -263,150 +169,73 @@
|
||||
"Add Attachment" : "Долучити вкладення",
|
||||
"Choose attachment" : "Вибрати вкладення",
|
||||
"Select Date" : "Вкажіть дату",
|
||||
"Later today – {timeLocale}" : "Пізніше сьогодні - {timeLocale}",
|
||||
"Set due date for later today" : "Встановіть термін сплати на сьогоднішній день",
|
||||
"Tomorrow – {timeLocale}" : "Завтра - {timeLocale}",
|
||||
"Set due date for tomorrow" : "Встановіть дедлайн на завтра",
|
||||
"This weekend – {timeLocale}" : "На цих вихідних - {timeLocale}",
|
||||
"Set due date for this weekend" : "Встановіть термін виконання на ці вихідні",
|
||||
"Next week – {timeLocale}" : "Наступного тижня - {timeLocale}",
|
||||
"Set due date for next week" : "Встановіть термін виконання на наступний тиждень",
|
||||
"Assign a due date to this card…" : "Призначте цій картці термін оплати...",
|
||||
"Set a due date" : "Встановити дату завершення",
|
||||
"Add due date" : "Додати термін сплати",
|
||||
"Choose a date" : "Виберіть дату",
|
||||
"Remove due date" : "Вилучити до",
|
||||
"Mark as done" : "Позначити як виконане",
|
||||
"Due at:" : "На часі:",
|
||||
"Not done" : "Ще не все.",
|
||||
"Unarchive card" : "Розархівувати картку",
|
||||
"Archive card" : "Архівувати картку",
|
||||
"Assign a tag to this card…" : "Додати мітку до цієї картки",
|
||||
"Select or create a tag…" : "Виберіть або створіть тег...",
|
||||
"Create a new tag:" : "Створіть новий тег:",
|
||||
"(group)" : "(група)",
|
||||
"{count} comments, {unread} unread" : "{count} коментарів, {unread} непрочитаних",
|
||||
"Todo items" : "Пункти завдань",
|
||||
"Edit card title" : "Редагувати заголовок картки",
|
||||
"Open link" : "Відкрити посилання",
|
||||
"Card deleted" : "Картку видалено",
|
||||
"Edit title" : "Редагувати",
|
||||
"Assign to me" : "Призначити мені",
|
||||
"Unassign myself" : "Зняти з себе повноваження.",
|
||||
"Mark as not done" : "Позначити як не виконано",
|
||||
"Delete card" : "Вилучити картку",
|
||||
"seconds ago" : "секунд тому",
|
||||
"Keyboard shortcuts" : "Скорочення",
|
||||
"Boost your productivity using Deck with keyboard shortcuts." : "Підвищуйте свою продуктивність, використовуючи Deck за допомогою комбінацій клавіш.",
|
||||
"Board actions" : "Дії правління",
|
||||
"Keyboard shortcut" : "Сполучення клавіш",
|
||||
"Action" : "Дія ",
|
||||
"Shift" : "Shift",
|
||||
"Scroll" : "Прокрутка",
|
||||
"Scroll sideways" : "Прокрутіть вбік",
|
||||
"Navigate between cards" : "Перехід між картками",
|
||||
"Esc" : "Esc",
|
||||
"Close card details" : "Закрити дані картки",
|
||||
"Ctrl" : "Ctrl",
|
||||
"Search" : "Пошук",
|
||||
"Show card filters" : "Показати фільтри карток",
|
||||
"Clear card filters" : "Очистити фільтри карток",
|
||||
"Show help dialog" : "Показати діалогове вікно довідки",
|
||||
"Card actions" : "Дії з карткою",
|
||||
"The following actions can be triggered on the currently highlighted card" : "На поточній виділеній картці можна виконати такі дії",
|
||||
"Enter" : "Ввід",
|
||||
"Space" : "Простір",
|
||||
"Open card details" : "Відкрити реквізити картки",
|
||||
"Edit the card title" : "Відредагуйте назву картки",
|
||||
"Assign yourself to the current card" : "Приєднайте себе до поточної картки",
|
||||
"Archive/unarchive the current card" : "Архівувати/розархівувати поточну картку",
|
||||
"Mark card as completed/not completed" : "Позначте картку як завершену/не завершену",
|
||||
"Open card menu" : "Відкрити меню картки",
|
||||
"All boards" : "Усі дошки",
|
||||
"Archived boards" : "Архівні дошки",
|
||||
"Shared with you" : "Вам надано доступ",
|
||||
"Deck settings" : "Налаштування колоди",
|
||||
"Use bigger card view" : "Режим перегляду зі збільшеними картками",
|
||||
"Show card ID badge" : "Покажіть бейдж з ідентифікаційною карткою",
|
||||
"Show boards in calendar/tasks" : "Показувати дошки в календарі та завданнях",
|
||||
"Limit board creation to some groups" : "Обмежити створення дошки для деяких груп",
|
||||
"Users outside of those groups will not be able to create their own boards, but will still be able to work on boards that have been shared with them." : "Користувачі поза цими групами не зможуть створювати власні дошки, але зможуть працювати на дошках, до яких їм надано спільний доступ.",
|
||||
"Cancel edit" : "Скасувати редагування",
|
||||
"Save board" : "Зберегти дошку",
|
||||
"Board {0} deleted" : "Дошку {0} вилучено",
|
||||
"All cards" : "Всі карти",
|
||||
"Only assigned cards" : "Тільки призначені картки",
|
||||
"No reminder" : "Відсутні нагадування",
|
||||
"An error occurred" : "Виникла помилка",
|
||||
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Ви впевнені, що хочете вилучити дошку {title}? Це призведе до видалення всіх даних цієї дошки, включно архівні картки.",
|
||||
"Delete the board?" : "Вилучити дошку?",
|
||||
"Exporting board..." : "Експортна дошка...",
|
||||
"Board details" : "Деталі дошки",
|
||||
"Edit board" : "Редагувати дошку",
|
||||
"Clone board" : "Копіювати дошку",
|
||||
"Unarchive board" : "Розархівувати дошку",
|
||||
"Archive board" : "Архівувати дошку",
|
||||
"Export board" : "Дошка експорту",
|
||||
"Turn on due date reminders" : "Нагадування про термін виконання",
|
||||
"Turn off due date reminders" : "Вимкнути нагадування про терміни виконання",
|
||||
"Due date reminders" : "Нагадування про терміни виконання",
|
||||
"Assigned cards" : "Призначені картки",
|
||||
"No notifications" : "Немає сповіщень",
|
||||
"Delete board" : "Вилучити дошку",
|
||||
"Importing board..." : "Імпортна дошка...",
|
||||
"Board imported successfully" : "Плату успішно імпортовано",
|
||||
"Import board" : "Імпортна дошка",
|
||||
"Clone {boardTitle}" : "Клон {boardTitle}",
|
||||
"Clone cards" : "Копіювати картки",
|
||||
"Clone assignments" : "Призначення клонів",
|
||||
"Clone labels" : "Етикетки клонів",
|
||||
"Clone due dates" : "Клонувати дві дати",
|
||||
"Advanced options" : "Розширені налаштування",
|
||||
"Move all cards to the first list" : "Перемістіть усі картки до першого списку",
|
||||
"Restore archived cards" : "Відновлення заархівованих карток",
|
||||
"Clone" : "Копіювати",
|
||||
"Export {boardTitle}" : "Експорт {boardTitle}",
|
||||
"Export as JSON" : "Експорт як JSON",
|
||||
"Export as CSV" : "Експорт у CSV",
|
||||
"Note: Only the JSON format is supported for importing back into the Deck app." : "Примітка: Для імпорту в додаток Deck підтримується лише формат JSON.",
|
||||
"Export" : "Експортувати",
|
||||
"Loading filtered view" : "Завантаження відфільтрованого перегляду",
|
||||
"Today" : "Сьогодні",
|
||||
"Tomorrow" : "Завтра",
|
||||
"No due" : "Без призначеної дати",
|
||||
"Search for {searchQuery} in all boards" : "Шукати {searchQuery} на всіх дошках оголошень",
|
||||
"No results found" : "Не знайдено жодного результату",
|
||||
"Deck board {name}\n* Last modified on {lastMod}" : "Колода {name}\n* Востаннє змінено на {lastMod}",
|
||||
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Створено на {created}\n* Востаннє змінено на {lastMod}\n* {nbAttachments} вкладення\n* {nbComments} коментарі",
|
||||
"{nbCards} cards" : "{nbCards} картки",
|
||||
"Due on {date}" : "До {date}",
|
||||
"{stack} in {board}" : "{stack} в {board}",
|
||||
"Click to expand description" : "Натисніть, щоб розгорнути опис",
|
||||
"Click to expand comment" : "Натисніть, щоб розгорнути коментар",
|
||||
"Create card" : "Створити картку",
|
||||
"Create a new card" : "Створити нову картку",
|
||||
"Card title" : "Заголовок картки",
|
||||
"Creating the new card …" : "Створення нової картки ...",
|
||||
"Card \"{card}\" was added to \"{board}\"" : "Картку \"{card}\" додано до \"{board}\"",
|
||||
"Open card" : "Відкрити картку",
|
||||
"Close" : "Закрити",
|
||||
"No upcoming cards" : "Немає майбутніх завдань",
|
||||
"upcoming cards today" : "найближчі картки на сьогодні",
|
||||
"upcoming cards tomorrow" : "найближчі картки завтра",
|
||||
"upcoming cards" : "очікують на виконання",
|
||||
"New card" : "Нова картка",
|
||||
"Link to a board" : "Прив'язати до дошки",
|
||||
"Link to a card" : "Прив'язати до картки",
|
||||
"Create a card" : "Створити картку",
|
||||
"Message from {author} in {conversationName}" : "Повідомлення від {author} у {conversationName}",
|
||||
"Something went wrong" : "От халепа!",
|
||||
"Failed to upload {name}" : "Не вдалося завантажити {name}",
|
||||
"Maximum file size of {size} exceeded" : "Досягнуто максимальний розмір файлу {size}",
|
||||
"Assigned users" : "Призначені користувачі",
|
||||
"Due date" : "Дата виконання",
|
||||
"Error creating the share" : "Помилка створення спільного доступу",
|
||||
"Share with a Deck card" : "Поділіться з картою з колоди",
|
||||
"Share {file} with a Deck card" : "Поділіться {file} з картою колоди",
|
||||
"Share" : "Спільний доступ",
|
||||
"Personal" : "Особисте",
|
||||
"To do" : "Заплановано",
|
||||
|
||||
12
l10n/uz.js
12
l10n/uz.js
@@ -20,12 +20,12 @@ OC.L10N.register(
|
||||
"Card not found" : "Karta topilmadi",
|
||||
"Add board" : "Doska qo'shing",
|
||||
"Move card" : "Kartani ko'chirish",
|
||||
"Cancel" : "Bekor qilish",
|
||||
"Cancel" : "Cancel",
|
||||
"Add card" : "Karta qo'shing",
|
||||
"Archived cards" : "Arxivlangan kartalar",
|
||||
"Add list" : "Roʻyxat qoʻshish",
|
||||
"Unassigned" : "Tayinlanmagan",
|
||||
"Open" : "Ochish",
|
||||
"Open" : "Open",
|
||||
"Overdue" : "Muddati o'tgan",
|
||||
"Next 7 days" : "Keyingi 7 kun",
|
||||
"Next 30 days" : "Keyingi 30 kun",
|
||||
@@ -37,13 +37,13 @@ OC.L10N.register(
|
||||
"Undo" : "Bekor qilish",
|
||||
"Can edit" : "Can edit",
|
||||
"Owner" : "Owner",
|
||||
"Delete" : "O'chirish",
|
||||
"Delete" : "Delete",
|
||||
"Delete list" : "Roʻyxatni oʻchirish",
|
||||
"Edit" : "Tahrirlash",
|
||||
"Download" : "Yuklab olish",
|
||||
"Download" : "Download",
|
||||
"Modified" : "Modified",
|
||||
"Comments" : "Comments",
|
||||
"Save" : "Saqlash",
|
||||
"Save" : "Save",
|
||||
"In reply to" : "ga javoban",
|
||||
"Reply" : "Javob bering",
|
||||
"Update" : "Update",
|
||||
@@ -55,7 +55,7 @@ OC.L10N.register(
|
||||
"Edit title" : "Sarlavhani tahrirlash",
|
||||
"Delete card" : "Kartani o'chirish",
|
||||
"seconds ago" : "seconds ago",
|
||||
"Search" : "Qidirish",
|
||||
"Search" : "Search",
|
||||
"Archived boards" : "Arxivlangan taxtalar",
|
||||
"Shared with you" : "Shared with you",
|
||||
"No reminder" : "Eslatma yo'q",
|
||||
|
||||
12
l10n/uz.json
12
l10n/uz.json
@@ -18,12 +18,12 @@
|
||||
"Card not found" : "Karta topilmadi",
|
||||
"Add board" : "Doska qo'shing",
|
||||
"Move card" : "Kartani ko'chirish",
|
||||
"Cancel" : "Bekor qilish",
|
||||
"Cancel" : "Cancel",
|
||||
"Add card" : "Karta qo'shing",
|
||||
"Archived cards" : "Arxivlangan kartalar",
|
||||
"Add list" : "Roʻyxat qoʻshish",
|
||||
"Unassigned" : "Tayinlanmagan",
|
||||
"Open" : "Ochish",
|
||||
"Open" : "Open",
|
||||
"Overdue" : "Muddati o'tgan",
|
||||
"Next 7 days" : "Keyingi 7 kun",
|
||||
"Next 30 days" : "Keyingi 30 kun",
|
||||
@@ -35,13 +35,13 @@
|
||||
"Undo" : "Bekor qilish",
|
||||
"Can edit" : "Can edit",
|
||||
"Owner" : "Owner",
|
||||
"Delete" : "O'chirish",
|
||||
"Delete" : "Delete",
|
||||
"Delete list" : "Roʻyxatni oʻchirish",
|
||||
"Edit" : "Tahrirlash",
|
||||
"Download" : "Yuklab olish",
|
||||
"Download" : "Download",
|
||||
"Modified" : "Modified",
|
||||
"Comments" : "Comments",
|
||||
"Save" : "Saqlash",
|
||||
"Save" : "Save",
|
||||
"In reply to" : "ga javoban",
|
||||
"Reply" : "Javob bering",
|
||||
"Update" : "Update",
|
||||
@@ -53,7 +53,7 @@
|
||||
"Edit title" : "Sarlavhani tahrirlash",
|
||||
"Delete card" : "Kartani o'chirish",
|
||||
"seconds ago" : "seconds ago",
|
||||
"Search" : "Qidirish",
|
||||
"Search" : "Search",
|
||||
"Archived boards" : "Arxivlangan taxtalar",
|
||||
"Shared with you" : "Shared with you",
|
||||
"No reminder" : "Eslatma yo'q",
|
||||
|
||||
@@ -10,10 +10,12 @@ namespace OCA\Deck\Controller;
|
||||
use OCA\Deck\Db\Acl;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCA\Deck\Service\Importer\BoardImportService;
|
||||
use OCA\Deck\Service\PermissionService;
|
||||
use OCP\AppFramework\ApiController;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
|
||||
class BoardController extends ApiController {
|
||||
@@ -22,6 +24,8 @@ class BoardController extends ApiController {
|
||||
IRequest $request,
|
||||
private BoardService $boardService,
|
||||
private PermissionService $permissionService,
|
||||
private BoardImportService $boardImportService,
|
||||
private IL10N $l10n,
|
||||
private $userId,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
@@ -163,4 +167,62 @@ class BoardController extends ApiController {
|
||||
public function export($boardId) {
|
||||
return $this->boardService->export($boardId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function import(): DataResponse {
|
||||
$file = $this->request->getUploadedFile('file');
|
||||
$error = null;
|
||||
$phpFileUploadErrors = [
|
||||
UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'),
|
||||
UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'),
|
||||
UPLOAD_ERR_FORM_SIZE => $this->l10n->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
|
||||
UPLOAD_ERR_PARTIAL => $this->l10n->t('The file was only partially uploaded'),
|
||||
UPLOAD_ERR_NO_FILE => $this->l10n->t('No file was uploaded'),
|
||||
UPLOAD_ERR_NO_TMP_DIR => $this->l10n->t('Missing a temporary folder'),
|
||||
UPLOAD_ERR_CANT_WRITE => $this->l10n->t('Could not write file to disk'),
|
||||
UPLOAD_ERR_EXTENSION => $this->l10n->t('A PHP extension stopped the file upload'),
|
||||
];
|
||||
|
||||
if (empty($file)) {
|
||||
$error = $this->l10n->t('No file uploaded or file size exceeds maximum of %s', [\OCP\Util::humanFileSize(\OCP\Util::uploadLimit())]);
|
||||
}
|
||||
if (!empty($file) && array_key_exists('error', $file) && $file['error'] !== UPLOAD_ERR_OK) {
|
||||
$error = $phpFileUploadErrors[$file['error']];
|
||||
}
|
||||
if (!empty($file) && $file['error'] === UPLOAD_ERR_OK && !in_array($file['type'], ['application/json', 'text/plain'])) {
|
||||
$error = $this->l10n->t('Invalid file type. Only JSON files are allowed.');
|
||||
}
|
||||
if ($error !== null) {
|
||||
return new DataResponse([
|
||||
'status' => 'error',
|
||||
'message' => $error,
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
$fileContent = file_get_contents($file['tmp_name']);
|
||||
$this->boardImportService->setSystem('DeckJson');
|
||||
$config = new \stdClass();
|
||||
$config->owner = $this->userId;
|
||||
$this->boardImportService->setConfigInstance($config);
|
||||
$this->boardImportService->setData(json_decode($fileContent));
|
||||
$this->boardImportService->import();
|
||||
$importedBoard = $this->boardImportService->getBoard();
|
||||
$board = $this->boardService->find($importedBoard->getId());
|
||||
|
||||
return new DataResponse($board, Http::STATUS_OK);
|
||||
} catch (\TypeError $e) {
|
||||
return new DataResponse([
|
||||
'status' => 'error',
|
||||
'message' => $this->l10n->t('Invalid JSON data'),
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
} catch (\Exception $e) {
|
||||
return new DataResponse([
|
||||
'status' => 'error',
|
||||
'message' => $this->l10n->t('Failed to import board'),
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class DefaultBoardMiddleware extends Middleware {
|
||||
public function beforeController($controller, $methodName) {
|
||||
try {
|
||||
if ($this->userId !== null && $this->defaultBoardService->checkFirstRun($this->userId) && $this->permissionService->canCreate()) {
|
||||
$this->defaultBoardService->createDefaultBoard($this->l10n->t('Personal'), $this->userId, '0087C5');
|
||||
$this->defaultBoardService->createDefaultBoard($this->l10n->t('Welcome to Nextcloud Deck!'), $this->userId, 'bf678b');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Could not create default board', ['exception' => $e]);
|
||||
|
||||
78
lib/Migration/LabelMismatchCleanup.php
Normal file
78
lib/Migration/LabelMismatchCleanup.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Deck\Migration;
|
||||
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
|
||||
class LabelMismatchCleanup implements IRepairStep {
|
||||
|
||||
public function __construct(
|
||||
private IDBConnection $db,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return 'Migrate labels with wrong board mapping';
|
||||
}
|
||||
|
||||
public function run(IOutput $output) {
|
||||
// Find assingments where a label of another (wrong) board is used
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('al.id', 'al.label_id', 'al.card_id', 's.board_id as actual_board_id', 'l.board_id as wrong_id', 'l.color', 'l.title')
|
||||
->from('deck_assigned_labels', 'al')
|
||||
->innerJoin('al', 'deck_cards', 'c', 'c.id = al.card_id')
|
||||
->innerJoin('c', 'deck_stacks', 's', 'c.stack_id = s.id')
|
||||
->innerJoin('al', 'deck_labels', 'l', 'l.id = al.label_id')
|
||||
->where($qb->expr()->neq('l.board_id', 's.board_id'));
|
||||
|
||||
$labels = $qb->executeQUery()->fetchAll();
|
||||
if (count($labels) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$output->info('Found ' . count($labels) . ' labels with wrong board mapping');
|
||||
|
||||
foreach ($labels as $label) {
|
||||
// Select existing label on the correct board
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('id')
|
||||
->from('deck_labels')
|
||||
->where($qb->expr()->eq('title', $qb->createNamedParameter($label['title'])))
|
||||
->andWhere($qb->expr()->eq('color', $qb->createNamedParameter($label['color'])))
|
||||
->andWhere($qb->expr()->eq('board_id', $qb->createNamedParameter($label['actual_board_id'])));
|
||||
$result = $qb->executeQuery();
|
||||
$newLabel = $result->fetchOne();
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$newLabel) {
|
||||
// Create a new label with the same title and color on the correct board
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->insert('deck_labels')
|
||||
->values([
|
||||
'title' => $qb->createNamedParameter($label['title']),
|
||||
'color' => $qb->createNamedParameter($label['color']),
|
||||
'board_id' => $qb->createNamedParameter($label['actual_board_id']),
|
||||
]);
|
||||
$qb->executeStatement();
|
||||
$newLabel = $qb->getLastInsertId();
|
||||
$output->debug('Created new label ' . $label['title'] . ' on board ' . $label['actual_board_id']);
|
||||
} else {
|
||||
$output->debug('Found existing label ' . $label['title'] . ' on board ' . $label['actual_board_id']);
|
||||
}
|
||||
|
||||
// Update the assignment to use the new label
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->update('deck_assigned_labels')
|
||||
->set('label_id', $qb->createNamedParameter($newLabel))
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($label['id'])));
|
||||
$qb->executeStatement();
|
||||
$output->debug('Updated label assignment ' . $label['id'] . ' to use label ' . $newLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,14 +320,14 @@ class BoardService {
|
||||
return $board;
|
||||
}
|
||||
|
||||
private function applyPermissions($boardId, $edit, $share, $manage, $oldAcl = null) {
|
||||
private function applyPermissions($boardId, $edit, $share, $manage) {
|
||||
try {
|
||||
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_MANAGE);
|
||||
} catch (NoPermissionException $e) {
|
||||
$acls = $this->aclMapper->findAll($boardId);
|
||||
$edit = $this->permissionService->userCan($acls, Acl::PERMISSION_EDIT, $this->userId) ? $edit : $oldAcl?->getPermissionEdit() ?? false;
|
||||
$share = $this->permissionService->userCan($acls, Acl::PERMISSION_SHARE, $this->userId) ? $share : $oldAcl?->getPermissionShare() ?? false;
|
||||
$manage = $this->permissionService->userCan($acls, Acl::PERMISSION_MANAGE, $this->userId) ? $manage : $oldAcl?->getPermissionManage() ?? false;
|
||||
$edit = $this->permissionService->userCan($acls, Acl::PERMISSION_EDIT, $this->userId) && $edit;
|
||||
$share = $this->permissionService->userCan($acls, Acl::PERMISSION_SHARE, $this->userId) && $share;
|
||||
$manage = $this->permissionService->userCan($acls, Acl::PERMISSION_MANAGE, $this->userId) && $manage;
|
||||
}
|
||||
return [$edit, $share, $manage];
|
||||
}
|
||||
@@ -417,7 +417,7 @@ class BoardService {
|
||||
|
||||
/** @var Acl $acl */
|
||||
$acl = $this->aclMapper->find($id);
|
||||
[$edit, $share, $manage] = $this->applyPermissions($acl->getBoardId(), $edit, $share, $manage, $acl);
|
||||
[$edit, $share, $manage] = $this->applyPermissions($acl->getBoardId(), $edit, $share, $manage);
|
||||
$acl->setPermissionEdit($edit);
|
||||
$acl->setPermissionShare($share);
|
||||
$acl->setPermissionManage($manage);
|
||||
@@ -439,7 +439,7 @@ class BoardService {
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function deleteAcl(int $id): ?Acl {
|
||||
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_MANAGE);
|
||||
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
|
||||
/** @var Acl $acl */
|
||||
$acl = $this->aclMapper->find($id);
|
||||
$this->boardMapper->mapAcl($acl);
|
||||
@@ -762,6 +762,8 @@ class BoardService {
|
||||
$fullCards = [];
|
||||
foreach ($cards as $card) {
|
||||
$fullCard = $this->cardMapper->find($card->getId());
|
||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||
$fullCard->setAssignedUsers($assignedUsers);
|
||||
array_push($fullCards, $fullCard);
|
||||
}
|
||||
$stack->setCards($fullCards);
|
||||
|
||||
@@ -652,6 +652,9 @@ class CardService {
|
||||
throw new StatusException('Operation not allowed. This card is archived.');
|
||||
}
|
||||
$label = $this->labelMapper->find($labelId);
|
||||
if ($label->getBoardId() !== $this->cardMapper->findBoardId($card->getId())) {
|
||||
throw new StatusException('Operation not allowed. Label does not exist.');
|
||||
}
|
||||
$this->cardMapper->removeLabel($cardId, $labelId);
|
||||
$this->changeHelper->cardChanged($cardId);
|
||||
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]);
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace OCA\Deck\Service;
|
||||
|
||||
use OCA\Deck\AppInfo\Application;
|
||||
use OCA\Deck\BadRequestException;
|
||||
use OCA\Deck\Db\Board;
|
||||
use OCA\Deck\Db\BoardMapper;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
@@ -21,6 +22,8 @@ class DefaultBoardService {
|
||||
private $cardService;
|
||||
private $config;
|
||||
private $l10n;
|
||||
private LabelService $labelService;
|
||||
private AttachmentService $attachmentService;
|
||||
|
||||
public function __construct(
|
||||
IL10N $l10n,
|
||||
@@ -29,6 +32,8 @@ class DefaultBoardService {
|
||||
StackService $stackService,
|
||||
CardService $cardService,
|
||||
IConfig $config,
|
||||
LabelService $labelService,
|
||||
AttachmentService $attachmentService,
|
||||
) {
|
||||
$this->boardService = $boardService;
|
||||
$this->stackService = $stackService;
|
||||
@@ -36,6 +41,8 @@ class DefaultBoardService {
|
||||
$this->config = $config;
|
||||
$this->boardMapper = $boardMapper;
|
||||
$this->l10n = $l10n;
|
||||
$this->labelService = $labelService;
|
||||
$this->attachmentService = $attachmentService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,10 +66,13 @@ class DefaultBoardService {
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getDefaultBoardData(): array {
|
||||
$defaultBoardDataJson = file_get_contents(__DIR__ . '/fixtures/default-board.json');
|
||||
return json_decode($defaultBoardDataJson, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $title
|
||||
* @param $userId
|
||||
* @param $color
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
* @throws \OCA\Deck\NoPermissionException
|
||||
* @throws \OCA\Deck\StatusException
|
||||
@@ -71,19 +81,71 @@ class DefaultBoardService {
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function createDefaultBoard(string $title, string $userId, string $color) {
|
||||
$defaultBoard = $this->boardService->create($title, $userId, $color);
|
||||
$defaultStacks = [];
|
||||
$defaultCards = [];
|
||||
$boardData = $this->getDefaultBoardData();
|
||||
|
||||
/** @var Board $defaultBoard */
|
||||
$defaultBoard = $this->boardService->create(
|
||||
$boardData['title'] ?? $title,
|
||||
$userId,
|
||||
$boardData['color'] ?? $color,
|
||||
);
|
||||
$boardId = $defaultBoard->getId();
|
||||
$additionLabels = [];
|
||||
$translatedLabelTitles = [
|
||||
'Read more inside' => $this->l10n->t('Read more inside'),
|
||||
];
|
||||
$translatedStackTitles = [
|
||||
'Custom lists - click to rename!' => $this->l10n->t('Custom lists - click to rename!'),
|
||||
'To Do' => $this->l10n->t('To Do'),
|
||||
'In Progress' => $this->l10n->t('In Progress'),
|
||||
'Done' => $this->l10n->t('Done'),
|
||||
];
|
||||
$translatedCardTitles = [
|
||||
'1. Open to learn more about boards and cards' => $this->l10n->t('1. Open to learn more about boards and cards'),
|
||||
'2. Drag cards left and right, up and down' => $this->l10n->t('2. Drag cards left and right, up and down'),
|
||||
'3. Apply rich formatting and link content' => $this->l10n->t('3. Apply rich formatting and link content'),
|
||||
'4. Share, comment and collaborate!' => $this->l10n->t('4. Share, comment and collaborate!'),
|
||||
'Create your first card!' => $this->l10n->t('Create your first card!'),
|
||||
];
|
||||
|
||||
$defaultStacks[] = $this->stackService->create($this->l10n->t('To do'), $boardId, 1);
|
||||
$defaultStacks[] = $this->stackService->create($this->l10n->t('Doing'), $boardId, 1);
|
||||
$defaultStacks[] = $this->stackService->create($this->l10n->t('Done'), $boardId, 1);
|
||||
foreach ($boardData['addition_labels'] as $labelData) {
|
||||
$additionLabels[] = $this->labelService->create(
|
||||
$translatedLabelTitles[$labelData['title']] ?? $labelData['title'],
|
||||
$labelData['color'],
|
||||
$boardId
|
||||
);
|
||||
}
|
||||
|
||||
$defaultCards[] = $this->cardService->create($this->l10n->t('Example Task 3'), $defaultStacks[0]->getId(), 'text', 0, $userId);
|
||||
$defaultCards[] = $this->cardService->create($this->l10n->t('Example Task 2'), $defaultStacks[1]->getId(), 'text', 0, $userId);
|
||||
$defaultCards[] = $this->cardService->create($this->l10n->t('Example Task 1'), $defaultStacks[2]->getId(), 'text', 0, $userId);
|
||||
$defaultLabels = array_merge($defaultBoard->getLabels() ?? [], $additionLabels);
|
||||
|
||||
foreach ($boardData['stacks'] as $stackData) {
|
||||
$stack = $this->stackService->create(
|
||||
$translatedStackTitles[$stackData['title']] ?? $stackData['title'],
|
||||
$boardId,
|
||||
$stackData['order']
|
||||
);
|
||||
|
||||
foreach ($stackData['cards'] as $cardData) {
|
||||
$card = $this->cardService->create(
|
||||
$translatedCardTitles[$cardData['title']] ?? $cardData['title'],
|
||||
$stack->getId(),
|
||||
$cardData['type'],
|
||||
$cardData['order'],
|
||||
$userId,
|
||||
$cardData['description'],
|
||||
);
|
||||
|
||||
foreach ($defaultLabels as $defaultLabel) {
|
||||
if ($defaultLabel && in_array($defaultLabel->getTitle(), $cardData['labels'])) {
|
||||
$this->cardService->assignLabel($card->getId(), $defaultLabel->getId());
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($cardData['has_example_attachment'])) {
|
||||
$this->attachmentService->create($card->getId(), 'file', 'DEFAULT_SAMPLE_FILE');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultBoard;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,15 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||
}
|
||||
|
||||
public function create(Attachment $attachment) {
|
||||
$file = $this->getUploadedFile();
|
||||
if ($attachment->getData() === 'DEFAULT_SAMPLE_FILE' && !$this->request->getUploadedFile('file')) {
|
||||
$file = [
|
||||
'name' => 'Nextcloud sample image - add your image here!.jpg',
|
||||
'tmp_name' => __DIR__ . '/../../img/sample-image.jpg',
|
||||
];
|
||||
} else {
|
||||
$file = $this->getUploadedFile();
|
||||
}
|
||||
|
||||
$fileName = $file['name'];
|
||||
|
||||
// get shares for current card
|
||||
|
||||
81
lib/Service/fixtures/default-board.json
Normal file
81
lib/Service/fixtures/default-board.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"color": "bf678b",
|
||||
"addition_labels": [
|
||||
{
|
||||
"title": "Read more inside",
|
||||
"color": "CC317C"
|
||||
}
|
||||
],
|
||||
"stacks": [
|
||||
{
|
||||
"title": "Custom lists - click to rename!",
|
||||
"order": 0,
|
||||
"cards": [
|
||||
{
|
||||
"title": "1. Open to learn more about boards and cards",
|
||||
"description": "## Welcome to Nextcloud Deck!\n\nNextcloud Deck is a kanban style project management app that integrates seamlessly with the Nextcloud ecosystem. Here, you can create and manage boards to streamline your projects and organize project tasks visually using cards. A versatile tool, Nextcloud Deck will help you stay efficient in both personal and collaborative settings!\n\n### \ud83d\udccc How to set up a new board?\n\nAdd a new board via the \u201c+\u201d button or \u201cAdd board\u201d. Name your board and add a description to clarify its purpose if you wish.\n\nAdd task lists to your board via \u201cAdd List\u201d (e.g., \"To Do\"). Add cards to these lists to represent individual tasks or items. Organize your cards visually by assigning labels or colors.\n\nManage your board list: In the board menu next to the board name, you can edit, clone, archive and export your boards.\n\n### \ud83d\uddc2\ufe0f How to create and edit task cards?\n\nAdd cards via the \u201c+\u201d button on top of the list of your choice. Name your card and add a description using the available rich text formatting instruments.\n\nSet a due date to track deadlines and add responsible team members. Label your cards with tags for better navigation. You can create and manage tags in the sidebar, which is accessible via the toolbar button.\n\n### \ud83d\udca1Useful tips for Nextcloud Deck\n\n- Integrate Nextcloud Deck with Nextcloud Calendar and track the upcoming card deadlines.\n- Use comments to keep discussions around a task within its dedicated card.\n- Enable Nextcloud Deck notifications to always stay updated on changes to your boards.",
|
||||
"labels": [
|
||||
"Read more inside"
|
||||
],
|
||||
"type": "text",
|
||||
"order": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "To Do",
|
||||
"order": 1,
|
||||
"cards": [
|
||||
{
|
||||
"title": "2. Drag cards left and right, up and down",
|
||||
"description": "## \ud83d\udea6 Manage your boards and cards\n\nDrag your cards around the board to change position in the list or move them to other lists.\n\nArchive cards to remove them from the board without losing your content. You can always access archived cards and put them back by going to \"Show archived cards\" in the board menu.\n\nUse tags to assign types, statuses and other special attributes to your cards to aid visual navigation. Open the \"Tags\" tab in the sidebar of Nextcloud Deck to manage tags you can add to your cards.\n\n## \ud83d\udc53 Change how you view your boards\n\nTo navigate large boards easily, you can filter your cards by tags, team members, statuses and due dates. Open filter menu via Nextcloud Deck toolbar.\n\nEnable compact display mode in the Deck Board menu via the three dots menu in the toolbar. In the same menu, you can disable and enable cover images.",
|
||||
"labels": [
|
||||
"Read more inside"
|
||||
],
|
||||
"type": "text",
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"title": "Create your first card!",
|
||||
"description": "",
|
||||
"labels": [
|
||||
"Action needed"
|
||||
],
|
||||
"type": "text",
|
||||
"order": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "In Progress",
|
||||
"order": 2,
|
||||
"cards": [
|
||||
{
|
||||
"title": "3. Apply rich formatting and link content",
|
||||
"description": "## \ud83c\udfa8 Make use of rich formatting in card descriptions\n\nYou can use various instruments to make your content more structured and informative. Try all the instruments yourself - use this checklist!\n\n- [ ] Apply headings of different hierarchy (H1-H6).\n- [ ] Use **bold** *italic* __underline__ and ~~strikethrough~~ text formatting.\n- [ ] Create unordered, ordered and to-do lists.\n- [ ] Highlight code and blockquotes, add various illustrated callouts.\n- [ ] Create and format tables\n- [ ] Add details\n- [ ] Add links, for example to [the Nextcloud website](https:\/\/nextcloud.com\/)\n- [ ] Insert attachments\n- [ ] Add emojis \ud83d\udd76\ufe0f \n\n## \ud83d\udd17 Attach files to cards\n\nYou can upload any files from your machine or pick from your Nextcloud Files, such as documents, presentations, images, video clips and anything else. Add files via \"Attachments\" tab.\n\n## \ud83c\udf04 Illustrate cards with images\n\nAttach images to your cards - they will automatically work as card covers.",
|
||||
"labels": [
|
||||
"Read more inside"
|
||||
],
|
||||
"type": "text",
|
||||
"order": 0,
|
||||
"has_example_attachment": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Done",
|
||||
"order": 3,
|
||||
"cards": [
|
||||
{
|
||||
"title": "4. Share, comment and collaborate!",
|
||||
"description": "## \ud83e\udd1d How to collaborate in your board?\n\nShare your cards and boards with others. In many Nextcloud apps, linked Nextcloud Deck items will appear as rich, interactive widgets. Once you assign someone to a card, they will receive a notification.\n\nEdit cards together, leave notes for your teammates, share task-related files by adding attachments and discuss your tasks using card comments.\n\n### ",
|
||||
"labels": [
|
||||
"Read more inside"
|
||||
],
|
||||
"type": "text",
|
||||
"order": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
12728
package-lock.json
generated
12728
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "deck",
|
||||
"description": "",
|
||||
"version": "1.15.2",
|
||||
"version": "2.0.0-dev.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Julius Härtl",
|
||||
@@ -31,22 +31,25 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@babel/runtime": "^7.27.0",
|
||||
"@nextcloud/auth": "^2.4.0",
|
||||
"@nextcloud/axios": "^2.5.1",
|
||||
"@nextcloud/capabilities": "^1.2.0",
|
||||
"@nextcloud/dialogs": "^6.0.1",
|
||||
"@nextcloud/event-bus": "^3.3.1",
|
||||
"@nextcloud/dialogs": "^7.0.0-rc.1",
|
||||
"@nextcloud/event-bus": "^3.3.2",
|
||||
"@nextcloud/files": "^3.10.1",
|
||||
"@nextcloud/initial-state": "^2.2.0",
|
||||
"@nextcloud/l10n": "^3.1.0",
|
||||
"@nextcloud/moment": "^1.3.2",
|
||||
"@nextcloud/notify_push": "^1.3.0",
|
||||
"@nextcloud/router": "^3.0.1",
|
||||
"@nextcloud/vue": "^8.22.0",
|
||||
"@nextcloud/vue": "^9.0.0-rc.3",
|
||||
"@vue/compiler-sfc": "^3.5.13",
|
||||
"@vue/vue3-jest": "^29.2.6",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"blueimp-md5": "^2.19.0",
|
||||
"chroma-js": "^3.1.2",
|
||||
"dompurify": "^3.2.3",
|
||||
"dompurify": "^3.2.5",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
@@ -54,16 +57,14 @@
|
||||
"moment": "^2.30.1",
|
||||
"p-queue": "^8.0.1",
|
||||
"url-search-params-polyfill": "^8.2.5",
|
||||
"vue": "^2.7.15",
|
||||
"vue-at": "^2.5.1",
|
||||
"v3-infinite-loading": "^1.3.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-click-outside": "^1.1.0",
|
||||
"vue-easymde": "^2.0.0",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
"vue-material-design-icons": "^5.3.1",
|
||||
"vue-router": "^3.6.5",
|
||||
"vue-smooth-dnd": "^0.8.1",
|
||||
"vuex": "^3.6.2",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
"vue-router": "^4.5.0",
|
||||
"vue3-smooth-dnd": "^0.0.6",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @nextcloud/browserslist-config"
|
||||
@@ -75,20 +76,18 @@
|
||||
"devDependencies": {
|
||||
"@nextcloud/babel-config": "^1.2.0",
|
||||
"@nextcloud/browserslist-config": "^3.0.1",
|
||||
"@nextcloud/cypress": "^1.0.0-beta.12",
|
||||
"@nextcloud/eslint-config": "^8.4.1",
|
||||
"@nextcloud/cypress": "^1.0.0-beta.13",
|
||||
"@nextcloud/eslint-config": "^8.4.2",
|
||||
"@nextcloud/stylelint-config": "^3.0.1",
|
||||
"@nextcloud/webpack-vue-config": "^6.2.0",
|
||||
"@nextcloud/webpack-vue-config": "github:nextcloud-libraries/webpack-vue-config#vue3",
|
||||
"@relative-ci/agent": "^4.2.14",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/vue2-jest": "^29.2.6",
|
||||
"cypress": "^13.17.0",
|
||||
"eslint-plugin-cypress": "^3.6.0",
|
||||
"eslint-webpack-plugin": "^4.2.0",
|
||||
"eslint-webpack-plugin": "^5.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-serializer-vue": "^3.1.0",
|
||||
"stylelint-webpack-plugin": "^5.0.1",
|
||||
"vue-template-compiler": "^2.7.16"
|
||||
"stylelint-webpack-plugin": "^5.0.1"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
@@ -106,4 +105,4 @@
|
||||
"<rootDir>/node_modules/jest-serializer-vue"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
213
src/App.vue
213
src/App.vue
@@ -1,15 +1,51 @@
|
||||
<!--
|
||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<script>
|
||||
import { NcAppContent, NcContent, NcModal } from '@nextcloud/vue'
|
||||
import CardMoveDialog from './CardMoveDialog.vue'
|
||||
import AppNavigation from './components/navigation/AppNavigation.vue'
|
||||
import KeyboardShortcuts from './components/KeyboardShortcuts.vue'
|
||||
import { BoardApi } from './services/BoardApi.js'
|
||||
|
||||
const boardApi = new BoardApi()
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
NcContent,
|
||||
AppNavigation,
|
||||
NcAppContent,
|
||||
KeyboardShortcuts,
|
||||
CardMoveDialog,
|
||||
NcModal,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
boardApi,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardDetailsInModal() {
|
||||
return this.$store.getters.config('cardDetailsInModal')
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$router.push({ name: 'board' })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcContent app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
|
||||
<NcContent app-name="deck">
|
||||
<AppNavigation />
|
||||
<NcAppContent :allow-swipe-navigation="false">
|
||||
<router-view />
|
||||
</NcAppContent>
|
||||
|
||||
<div v-if="$route.params.id || $route.params.cardId">
|
||||
<NcModal v-if="cardDetailsInModal && $route.params.cardId"
|
||||
:name="t('deck', 'Card details')"
|
||||
@@ -21,179 +57,10 @@
|
||||
<router-view name="sidebar" />
|
||||
</div>
|
||||
</NcModal>
|
||||
|
||||
<router-view name="sidebar" :visible="!cardDetailsInModal || !$route.params.cardId" />
|
||||
</div>
|
||||
|
||||
<KeyboardShortcuts />
|
||||
<CardMoveDialog />
|
||||
</NcContent>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import AppNavigation from './components/navigation/AppNavigation.vue'
|
||||
import KeyboardShortcuts from './components/KeyboardShortcuts.vue'
|
||||
import { NcModal, NcContent, NcAppContent, isMobile } from '@nextcloud/vue'
|
||||
import { BoardApi } from './services/BoardApi.js'
|
||||
import { emit, subscribe } from '@nextcloud/event-bus'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import CardMoveDialog from './CardMoveDialog.vue'
|
||||
|
||||
const boardApi = new BoardApi()
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
CardMoveDialog,
|
||||
AppNavigation,
|
||||
NcModal,
|
||||
NcContent,
|
||||
NcAppContent,
|
||||
KeyboardShortcuts,
|
||||
},
|
||||
mixins: [isMobile],
|
||||
provide() {
|
||||
return {
|
||||
boardApi,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
addButton: {
|
||||
icon: 'icon-add',
|
||||
classes: [],
|
||||
text: t('deck', 'Add board'),
|
||||
edit: {
|
||||
text: t('deck', 'Add board'),
|
||||
action: () => {
|
||||
},
|
||||
reset: () => {
|
||||
},
|
||||
},
|
||||
action: () => {
|
||||
this.addButton.classes.push('editing')
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
navShown: state => state.navShown,
|
||||
sidebarShownState: state => state.sidebarShown,
|
||||
currentBoard: state => state.currentBoard,
|
||||
}),
|
||||
// TODO: properly handle sidebar showing for route subview and board sidebar
|
||||
sidebarRouterView() {
|
||||
// console.log(this.$route)
|
||||
return this.$route.name === 'card' || this.$route.name === 'board.details'
|
||||
},
|
||||
sidebarShown() {
|
||||
return this.sidebarRouterView || this.sidebarShownState
|
||||
},
|
||||
cardDetailsInModal: {
|
||||
get() {
|
||||
return this.$store.getters.config('cardDetailsInModal')
|
||||
},
|
||||
set(newValue) {
|
||||
this.$store.dispatch('setConfig', { cardDetailsInModal: newValue })
|
||||
},
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const initialState = loadState('deck', 'initialBoards', null)
|
||||
if (initialState !== null) {
|
||||
this.$store.dispatch('loadBoards')
|
||||
}
|
||||
this.$store.dispatch('loadSharees')
|
||||
},
|
||||
mounted() {
|
||||
// Set navigation to initial state and update in case it gets toggled
|
||||
emit('toggle-navigation', { open: !this.isMobile && this.navShown, _initial: true })
|
||||
this.$nextTick(() => {
|
||||
subscribe('navigation-toggled', (navState) => {
|
||||
this.$store.dispatch('toggleNav', navState.open)
|
||||
})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$router.push({ name: 'board' })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
#content-vue {
|
||||
#app-content {
|
||||
transition: margin-left 100ms ease;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#app-sidebar {
|
||||
transition: max-width 100ms ease;
|
||||
}
|
||||
|
||||
&.nav-hidden {
|
||||
#app-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.sidebar-hidden {
|
||||
#app-sidebar {
|
||||
max-width: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../css/print';
|
||||
|
||||
.icon-activity {
|
||||
background-image: url(../img/activity-dark.svg);
|
||||
|
||||
body[data-theme-dark] & {
|
||||
background-image: url(../img/activity.svg);
|
||||
}
|
||||
}
|
||||
|
||||
.avatardiv.circles {
|
||||
background: var(--color-primary-element);
|
||||
}
|
||||
|
||||
.icon-circles {
|
||||
background-image: url(../img/circles-dark.svg);
|
||||
opacity: 1;
|
||||
background-size: 20px;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.icon-circles-white, .icon-circles.icon-white {
|
||||
background-image: url(../img/circles.svg);
|
||||
opacity: 1;
|
||||
background-size: 20px;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.icon-colorpicker {
|
||||
background-image: url('../img/color_picker.svg');
|
||||
}
|
||||
|
||||
.v-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal__card {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
height: calc(100% - 20px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
label="title" />
|
||||
</div>
|
||||
<template #actions>
|
||||
<NcButton :disabled="!isBoardAndStackChoosen" type="secondary" @click="moveCard">
|
||||
<NcButton :disabled="!isBoardAndStackChoosen" variant="secondary" @click="moveCard">
|
||||
{{ t('deck', 'Move card') }}
|
||||
</NcButton>
|
||||
<NcButton :disabled="!isBoardAndStackChoosen" type="primary" @click="cloneCard">
|
||||
<NcButton :disabled="!isBoardAndStackChoosen" variant="primary" @click="cloneCard">
|
||||
{{ t('deck', 'Copy card') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
@@ -72,7 +72,7 @@ export default {
|
||||
mounted() {
|
||||
subscribe('deck:card:show-move-dialog', this.openModal)
|
||||
},
|
||||
destroyed() {
|
||||
unmounted() {
|
||||
unsubscribe('deck:card:show-move-dialog', this.openModal)
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -10,9 +10,15 @@
|
||||
:key="activity.activity_id"
|
||||
:activity="activity" />
|
||||
<InfiniteLoading :identifier="objectId" @infinite="infiniteHandler" @change="changeObject">
|
||||
<div slot="spinner" class="icon-loading" />
|
||||
<div slot="no-more" />
|
||||
<div slot="no-results" />
|
||||
<template #spinner>
|
||||
<div class="icon-loading" />
|
||||
</template>
|
||||
<template #no-more>
|
||||
<div />
|
||||
</template>
|
||||
<template #no-results>
|
||||
<div />
|
||||
</template>
|
||||
</InfiniteLoading>
|
||||
</div>
|
||||
</template>
|
||||
@@ -21,7 +27,7 @@
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import ActivityEntry from './ActivityEntry.vue'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
// import InfiniteLoading from 'v3-infinite-loading'
|
||||
|
||||
const ACTIVITY_FETCH_LIMIT = 50
|
||||
|
||||
@@ -29,7 +35,6 @@ export default {
|
||||
name: 'ActivityList',
|
||||
components: {
|
||||
ActivityEntry,
|
||||
InfiniteLoading,
|
||||
},
|
||||
props: {
|
||||
filter: {
|
||||
@@ -87,7 +92,7 @@ export default {
|
||||
// to tell the backend to fetch all activites related to cards of a given board
|
||||
activities = activities.filter((activity) => {
|
||||
return (activity.object_type === 'deck_board' && activity.object_id === this.objectId)
|
||||
|| (activity.object_type === 'deck_card' && activity.subject_rich[1].board.id === this.objectId)
|
||||
|| (activity.object_type === 'deck_card' && activity.subject_rich[1].board.id === this.objectId.toString())
|
||||
})
|
||||
}
|
||||
this.activities.push(...activities)
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
:title="t('deck', 'Apply filter')"
|
||||
:aria-label="t('deck', 'Apply filter')"
|
||||
class="filter-button"
|
||||
:type="isFilterActive ? 'primary' : 'tertiary'">
|
||||
:variant="isFilterActive ? 'primary' : 'tertiary'">
|
||||
<template #icon>
|
||||
<FilterIcon v-if="isFilterActive" :size="20" decorative />
|
||||
<FilterOffIcon v-else :size="20" decorative />
|
||||
@@ -231,12 +231,16 @@
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="compactMode"
|
||||
@click="toggleCompactMode">
|
||||
<ArrowExpandVerticalIcon slot="icon" :size="20" decorative />
|
||||
<template #icon>
|
||||
<ArrowExpandVerticalIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ t('deck', 'Toggle compact mode') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-else
|
||||
@click="toggleCompactMode">
|
||||
<ArrowCollapseVerticalIcon slot="icon" :size="20" decorative />
|
||||
<template #icon>
|
||||
<ArrowCollapseVerticalIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ t('deck', 'Toggle compact mode') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton @click="toggleShowCardCover">
|
||||
@@ -274,6 +278,7 @@ import SessionList from './SessionList.vue'
|
||||
import { isNotifyPushEnabled } from '../sessions.js'
|
||||
import CreateNewCardCustomPicker from '../views/CreateNewCardCustomPicker.vue'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
|
||||
export default {
|
||||
name: 'Controls',
|
||||
@@ -359,6 +364,11 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
onClickOutside(() => {
|
||||
this.hideAddStack()
|
||||
})
|
||||
},
|
||||
beforeMount() {
|
||||
subscribe('deck:board:show-new-card', this.clickShowAddCardModel)
|
||||
subscribe('deck:board:toggle-filter-popover', this.triggerOpenFilters)
|
||||
@@ -366,7 +376,7 @@ export default {
|
||||
subscribe('deck:board:toggle-filter-by-me', this.triggerFilterByMe)
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
unsubscribe('deck:board:show-new-card', this.clickShowAddCardModel)
|
||||
unsubscribe('deck:board:toggle-filter-popover', this.triggerOpenFilters)
|
||||
unsubscribe('deck:board:clear-filter', this.triggerClearFilter)
|
||||
@@ -508,20 +518,6 @@ export default {
|
||||
|
||||
#stack-add form {
|
||||
display: flex;
|
||||
|
||||
#new-stack-input-main {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.icon-confirm {
|
||||
border: 2px solid var(--color-border-maxcontrast) !important;
|
||||
border-left: none !important;
|
||||
}
|
||||
&:focus-within, &:focus, &:focus-visible,
|
||||
&:hover {
|
||||
.icon-confirm {
|
||||
border-color: var(--color-main-text) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<!-- :style="{top:cardTop, left:cardLeft}" -->
|
||||
<div v-if="card && selector"
|
||||
ref="shortcutModal"
|
||||
v-click-outside="close"
|
||||
class="keyboard-shortcuts__modal"
|
||||
tabindex="0"
|
||||
@keydown.esc="close">
|
||||
@@ -18,6 +17,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import DueDateSelector from './card/DueDateSelector.vue'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
|
||||
import { mapState } from 'vuex'
|
||||
import TagSelector from './card/TagSelector.vue'
|
||||
@@ -50,8 +50,9 @@ export default {
|
||||
subscribe('deck:card:show-assignment-selector', this.handleShowAssignemnt)
|
||||
subscribe('deck:card:show-due-date-selector', this.handleShowDueDate)
|
||||
subscribe('deck:card:show-label-selector', this.handleShowLabel)
|
||||
onClickOutside(this.close)
|
||||
},
|
||||
destroyed() {
|
||||
unmounted() {
|
||||
document.removeEventListener('keydown', this.onKeydown)
|
||||
unsubscribe('deck:card:show-assignment-selector', this.handleShowAssignemnt)
|
||||
unsubscribe('deck:card:show-due-date-selector', this.handleShowDueDate)
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
{{ t('deck', 'Create a new list to add cards to this board') }}
|
||||
<form @submit.prevent="addNewStack()">
|
||||
<NcTextField ref="newStackInput"
|
||||
v-model="newStackTitle"
|
||||
:disable="loading"
|
||||
:value.sync="newStackTitle"
|
||||
:placeholder="t('deck', 'List name')"
|
||||
type="text" />
|
||||
<NcButton type="secondary"
|
||||
native-type="submit"
|
||||
<NcButton variant="secondary"
|
||||
type="submit"
|
||||
:disabled="loading"
|
||||
:title="t('deck', 'Add list')">
|
||||
<template #icon>
|
||||
@@ -63,7 +63,10 @@
|
||||
data-click-closes-sidebar="true"
|
||||
data-dragscroll-enabled
|
||||
class="stack-draggable-wrapper">
|
||||
<Stack :stack="stack" :dragging="draggingStack" data-click-closes-sidebar="true" />
|
||||
<Stack :stack="stack"
|
||||
:dragging="draggingStack"
|
||||
data-click-closes-sidebar="true"
|
||||
@open-card="openCard" />
|
||||
</Draggable>
|
||||
</Container>
|
||||
</div>
|
||||
@@ -82,7 +85,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||
import { Container, Draggable } from 'vue3-smooth-dnd'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Controls from '../Controls.vue'
|
||||
import DeckIcon from '../icons/DeckIcon.vue'
|
||||
@@ -165,14 +168,14 @@ export default {
|
||||
created() {
|
||||
this.session = createSession(this.id)
|
||||
this.fetchData()
|
||||
this.$root.$on('open-card', (cardId) => {
|
||||
this.localModal = cardId
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
this.session.close()
|
||||
},
|
||||
methods: {
|
||||
openCard(cardId) {
|
||||
|
||||
},
|
||||
async fetchData() {
|
||||
this.loading = true
|
||||
try {
|
||||
@@ -253,8 +256,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../css/animations';
|
||||
@import '../../css/variables';
|
||||
@use '../../css/animations';
|
||||
@use '../../css/variables';
|
||||
|
||||
form {
|
||||
text-align: center;
|
||||
@@ -282,7 +285,7 @@ export default {
|
||||
}
|
||||
|
||||
.board {
|
||||
padding-left: $board-spacing;
|
||||
padding-left: variables.$board-spacing;
|
||||
position: relative;
|
||||
max-height: calc(100% - var(--default-clickable-area));
|
||||
overflow: hidden;
|
||||
@@ -313,8 +316,8 @@ export default {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// Margin left instead of padidng to avoid jumps on dropping a card
|
||||
margin-left: $stack-spacing;
|
||||
padding-right: $stack-spacing;
|
||||
margin-left: variables.$stack-spacing;
|
||||
padding-right: variables.$stack-spacing;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-top: 15px;
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
non-drag-area-selector=".dragDisabled"
|
||||
:drag-handle-selector="dragHandleSelector"
|
||||
data-dragscroll-enabled
|
||||
@should-accept-drop="canEdit"
|
||||
:should-accept-drop="() => canEdit"
|
||||
@drag-start="draggingCard = true"
|
||||
@drag-end="draggingCard = false"
|
||||
@drop="($event) => onDropCard(stack.id, $event)">
|
||||
@@ -102,7 +102,10 @@
|
||||
<transition :appear="animate && !card.animated && (card.animated=true)"
|
||||
:appear-class="'zoom-appear-class'"
|
||||
:appear-active-class="'zoom-appear-active-class'">
|
||||
<CardItem :id="card.id" ref="card" :dragging="draggingCard" />
|
||||
<CardItem :id="card.id"
|
||||
ref="card"
|
||||
:dragging="draggingCard"
|
||||
@open-card="openCard" />
|
||||
</transition>
|
||||
</Draggable>
|
||||
</Container>
|
||||
@@ -136,7 +139,7 @@
|
||||
<script>
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||
import { Container, Draggable } from 'vue3-smooth-dnd'
|
||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||
import CardPlusOutline from 'vue-material-design-icons/CardPlusOutline.vue'
|
||||
import { NcActions, NcActionButton, NcModal } from '@nextcloud/vue'
|
||||
@@ -171,6 +174,9 @@ export default {
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
'open-card',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
editing: false,
|
||||
@@ -233,6 +239,9 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
openCard(cardId) {
|
||||
this.$emit('open-card', cardId)
|
||||
},
|
||||
stopCardCreation(e) {
|
||||
// For some reason the submit event triggers a MouseEvent that is bubbling to the outside
|
||||
// so we have to ignore it
|
||||
@@ -362,10 +371,10 @@ export default {
|
||||
|
||||
@use 'sass:math';
|
||||
|
||||
@import './../../css/variables';
|
||||
@use './../../css/variables';
|
||||
|
||||
.stack {
|
||||
width: $stack-width + $stack-spacing * 3;
|
||||
width: variables.$stack-width + variables.$stack-spacing * 3;
|
||||
}
|
||||
|
||||
.stack__header {
|
||||
@@ -373,8 +382,8 @@ export default {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
padding-left: $card-spacing;
|
||||
padding-right: $card-spacing;
|
||||
padding-left: variables.$card-spacing;
|
||||
padding-right: variables.$card-spacing;
|
||||
margin: 6px;
|
||||
margin-top: 0;
|
||||
cursor: grab;
|
||||
@@ -418,7 +427,7 @@ export default {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: calc($stack-width - 60px);
|
||||
max-width: calc(variables.$stack-width - 60px);
|
||||
border-radius: 3px;
|
||||
padding: 4px 4px;
|
||||
font-size: var(--default-font-size);
|
||||
@@ -459,8 +468,8 @@ export default {
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
margin-left: $stack-spacing;
|
||||
margin-right: $stack-spacing;
|
||||
margin-left: variables.$stack-spacing;
|
||||
margin-right: variables.$stack-spacing;
|
||||
width: 100%;
|
||||
border: 2px solid var(--color-border-maxcontrast);
|
||||
border-radius: var(--border-radius-large);
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<NcAppSidebar v-if="currentBoard && currentCard"
|
||||
ref="cardSidebar"
|
||||
v-model:name-editable="isEditingTitle"
|
||||
:active="tabId"
|
||||
:name="displayTitle"
|
||||
:subname="subtitle"
|
||||
:subtitle="subtitleTooltip"
|
||||
:name-editable.sync="isEditingTitle"
|
||||
@update:name="(value) => titleEditing = value"
|
||||
@update:name="value => titleEditing = value"
|
||||
@dismiss-editing="titleEditing = currentCard.title"
|
||||
@submit-name="handleSubmitTitle"
|
||||
@opened="focusHeader"
|
||||
@@ -24,35 +25,69 @@
|
||||
{{ t('deck', 'Open in bigger view') }}
|
||||
</NcActionButton>
|
||||
|
||||
<CardMenuEntries :card="currentCard" :hide-details-entry="true" />
|
||||
<NcActionButton v-if="canEdit && !isCurrentUserAssigned"
|
||||
icon="icon-user"
|
||||
:close-after-click="true"
|
||||
@click="assignCardToMe()">
|
||||
{{ t('deck', 'Assign to me') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit && isCurrentUserAssigned"
|
||||
icon="icon-user"
|
||||
:close-after-click="true"
|
||||
@click="unassignCardFromMe()">
|
||||
{{ t('deck', 'Unassign myself') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-checkmark"
|
||||
:close-after-click="true"
|
||||
@click="changeCardDoneStatus()">
|
||||
{{ currentCard.done ? t('deck', 'Mark as not done') : t('deck', 'Mark as done') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-external"
|
||||
:close-after-click="true"
|
||||
@click="openCardMoveDialog">
|
||||
{{ t('deck', 'Move/copy card') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-for="action in cardActions"
|
||||
:key="action.label"
|
||||
:close-after-click="true"
|
||||
:icon="action.icon"
|
||||
@click="action.callback(cardRichObject)">
|
||||
{{ action.label }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEditBoard" :close-after-click="true" @click="archiveUnarchiveCard()">
|
||||
<template #icon>
|
||||
<ArchiveIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ currentCard.archived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-delete"
|
||||
:close-after-click="true"
|
||||
@click="deleteCard()">
|
||||
{{ t('deck', 'Delete card') }}
|
||||
</NcActionButton>
|
||||
</template>
|
||||
<template #description>
|
||||
<NcReferenceList v-if="currentCard.referenceData"
|
||||
:text="currentCard.title"
|
||||
:interactive="false" />
|
||||
<NcReferenceList v-if="currentCard.referenceData" :text="currentCard.title" :interactive="false" />
|
||||
</template>
|
||||
|
||||
<NcAppSidebarTab id="details"
|
||||
:order="0"
|
||||
:name="t('deck', 'Details')">
|
||||
<NcAppSidebarTab id="details" :order="0" :name="t('deck', 'Details')">
|
||||
<CardSidebarTabDetails :card="currentCard" />
|
||||
<template #icon>
|
||||
<HomeIcon :size="20" />
|
||||
</template>
|
||||
</NcAppSidebarTab>
|
||||
|
||||
<NcAppSidebarTab id="attachments"
|
||||
:order="1"
|
||||
:name="t('deck', 'Attachments')">
|
||||
<NcAppSidebarTab id="attachments" :order="1" :name="t('deck', 'Attachments')">
|
||||
<template #icon>
|
||||
<AttachmentIcon :size="20" />
|
||||
</template>
|
||||
<CardSidebarTabAttachments :card="currentCard" />
|
||||
</NcAppSidebarTab>
|
||||
|
||||
<NcAppSidebarTab id="comments"
|
||||
:order="2"
|
||||
:name="t('deck', 'Comments')">
|
||||
<NcAppSidebarTab id="comments" :order="2" :name="t('deck', 'Comments')">
|
||||
<template #icon>
|
||||
<CommentIcon :size="20" />
|
||||
</template>
|
||||
@@ -73,7 +108,7 @@
|
||||
|
||||
<script>
|
||||
import { NcActionButton, NcAppSidebar, NcAppSidebarTab } from '@nextcloud/vue'
|
||||
import { NcReferenceList } from '@nextcloud/vue/dist/Components/NcRichText.js'
|
||||
import { NcReferenceList } from '@nextcloud/vue/components/NcRichText'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import CardSidebarTabDetails from './CardSidebarTabDetails.vue'
|
||||
@@ -87,9 +122,15 @@ import HomeIcon from 'vue-material-design-icons/Home.vue'
|
||||
import CommentIcon from 'vue-material-design-icons/Comment.vue'
|
||||
import ActivityIcon from 'vue-material-design-icons/LightningBolt.vue'
|
||||
|
||||
import { showError, showWarning } from '@nextcloud/dialogs'
|
||||
import { showError, showWarning, showUndo } from '@nextcloud/dialogs'
|
||||
import { getLocale } from '@nextcloud/l10n'
|
||||
import CardMenuEntries from '../cards/CardMenuEntries.vue'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
import '@nextcloud/dialogs/style.css'
|
||||
|
||||
const capabilities = getCapabilities()
|
||||
|
||||
@@ -108,7 +149,7 @@ export default {
|
||||
AttachmentIcon,
|
||||
CommentIcon,
|
||||
HomeIcon,
|
||||
CardMenuEntries,
|
||||
ArchiveIcon,
|
||||
},
|
||||
mixins: [relativeDate],
|
||||
props: {
|
||||
@@ -140,8 +181,17 @@ export default {
|
||||
isFullApp: (state) => state.isFullApp,
|
||||
currentBoard: (state) => state.currentBoard,
|
||||
hasCardSaveError: (state) => state.hasCardSaveError,
|
||||
showArchived: (state) => state.showArchived,
|
||||
}),
|
||||
...mapGetters(['canEdit', 'assignables', 'cardActions', 'stackById']),
|
||||
...mapGetters([
|
||||
'canEdit',
|
||||
'assignables',
|
||||
'cardActions',
|
||||
'stackById',
|
||||
'isArchived',
|
||||
'boards',
|
||||
'boardById',
|
||||
]),
|
||||
currentCard() {
|
||||
return this.$store.getters.cardById(this.id)
|
||||
},
|
||||
@@ -168,6 +218,31 @@ export default {
|
||||
return reference ? reference.openGraphObject.name : this.currentCard.title
|
||||
},
|
||||
},
|
||||
canEdit() {
|
||||
return !this.currentCard.archived
|
||||
},
|
||||
canEditBoard() {
|
||||
if (this.currentBoard) {
|
||||
return this.$store.getters.canEdit
|
||||
}
|
||||
const board = this.$store.getters.boards.find((item) => item.id === this.currentCard.boardId)
|
||||
return !!board?.permissions?.PERMISSION_EDIT
|
||||
},
|
||||
isCurrentUserAssigned() {
|
||||
return this.currentCard.assignedUsers.find((item) => item.type === 0 && item.participant.uid === getCurrentUser()?.uid)
|
||||
},
|
||||
boardId() {
|
||||
return this.card?.boardId ? this.currentCard.boardId : Number(this.$route.params.id)
|
||||
},
|
||||
cardRichObject() {
|
||||
return {
|
||||
id: '' + this.currentCard.id,
|
||||
name: this.currentCard.title,
|
||||
boardname: this.boardById(this.boardId)?.title,
|
||||
stackname: this.stackById(this.currentCard.stackId)?.title,
|
||||
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.currentCard.id}`,
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentCard() {
|
||||
@@ -216,12 +291,46 @@ export default {
|
||||
formatDate(timestamp) {
|
||||
return moment.unix(timestamp).locale(this.locale).format('LLLL')
|
||||
},
|
||||
deleteCard() {
|
||||
this.$store.dispatch('deleteCard', this.currentCard)
|
||||
const undoCard = { ...this.currentCard, deletedAt: 0 }
|
||||
showUndo(t('deck', 'Card deleted'), () => this.$store.dispatch('cardUndoDelete', undoCard))
|
||||
if (this.$router.currentRoute.name === 'card') {
|
||||
this.$router.push({ name: 'board' })
|
||||
}
|
||||
},
|
||||
changeCardDoneStatus() {
|
||||
this.$store.dispatch('changeCardDoneStatus', { ...this.currentCard, done: !this.currentCard.done })
|
||||
},
|
||||
archiveUnarchiveCard() {
|
||||
this.$store.dispatch('archiveUnarchiveCard', { ...this.currentCard, archived: !this.currentCard.archived })
|
||||
},
|
||||
assignCardToMe() {
|
||||
this.$store.dispatch('assignCardToUser', {
|
||||
card: this.currentCard,
|
||||
assignee: {
|
||||
userId: getCurrentUser()?.uid,
|
||||
type: 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
unassignCardFromMe() {
|
||||
this.$store.dispatch('removeUserFromCard', {
|
||||
card: this.currentCard,
|
||||
assignee: {
|
||||
userId: getCurrentUser()?.uid,
|
||||
type: 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
openCardMoveDialog() {
|
||||
emit('deck:card:show-move-dialog', this.currentCard)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
section.app-sidebar__tab--active {
|
||||
min-height: auto;
|
||||
display: flex;
|
||||
@@ -273,6 +382,7 @@ section.app-sidebar__tab--active {
|
||||
z-index: 100;
|
||||
background-color: var(--color-main-background);
|
||||
}
|
||||
|
||||
.app-sidebar-tabs__nav {
|
||||
position: sticky;
|
||||
top: 87px;
|
||||
@@ -285,10 +395,10 @@ section.app-sidebar__tab--active {
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
#emptycontent, .emptycontent {
|
||||
#emptycontent,
|
||||
.emptycontent {
|
||||
margin-top: 88px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
:key="comment.id"
|
||||
:comment="comment"
|
||||
@doReload="loadComments" />
|
||||
<InfiniteLoading :identifier="card.id" @infinite="infiniteHandler">
|
||||
<!-- <InfiniteLoading :identifier="card.id" @infinite="infiniteHandler">
|
||||
<div slot="spinner" class="icon-loading" />
|
||||
<div slot="no-more" />
|
||||
<div slot="no-results" />
|
||||
</InfiniteLoading>
|
||||
</InfiniteLoading> -->
|
||||
</ul>
|
||||
<div v-else-if="isLoading" class="icon icon-loading" />
|
||||
<div v-else class="emptycontent">
|
||||
@@ -42,7 +42,7 @@ import { mapState, mapGetters } from 'vuex'
|
||||
import { NcAvatar } from '@nextcloud/vue'
|
||||
import CommentItem from './CommentItem.vue'
|
||||
import CommentForm from './CommentForm.vue'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
// import InfiniteLoading from 'v3-infinite-loading'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
|
||||
export default {
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
NcAvatar,
|
||||
CommentItem,
|
||||
CommentForm,
|
||||
InfiniteLoading,
|
||||
// InfiniteLoading,
|
||||
},
|
||||
props: {
|
||||
card: {
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
- SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
THIS HAS AT
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- <template>
|
||||
<div class="comment-form">
|
||||
<form @submit.prevent="submit">
|
||||
<At ref="at"
|
||||
@@ -191,4 +196,4 @@ export default {
|
||||
.atwho-li--avatar {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
</style> -->
|
||||
|
||||
@@ -175,7 +175,7 @@ export default {
|
||||
mounted() {
|
||||
this.setupEditor()
|
||||
},
|
||||
async beforeDestroy() {
|
||||
async beforeUnmount() {
|
||||
await this.destroyEditor()
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
-->
|
||||
<template>
|
||||
<CardDetailEntry :label="t('deck', 'Assign a due date to this card…')" data-test="due-date-selector">
|
||||
<Calendar v-if="!card.done" slot="icon" :size="20" />
|
||||
<CalendarCheck v-else slot="icon" :size="20" />
|
||||
<template v-if="!card.done" #icon>
|
||||
<Calendar :size="20" />
|
||||
</template>
|
||||
<template v-else #icon>
|
||||
<CalendarCheck :size="20" />
|
||||
</template>
|
||||
<template v-if="!card.done && !card.archived">
|
||||
<NcDateTimePickerNative v-if="duedate"
|
||||
id="card-duedate-picker"
|
||||
@@ -15,7 +19,7 @@
|
||||
type="datetime-local" />
|
||||
<NcActions v-if="canEdit"
|
||||
:menu-title="!duedate ? t('deck', 'Add due date') : null"
|
||||
type="tertiary"
|
||||
variant="tertiary"
|
||||
data-cy-due-date-actions>
|
||||
<template v-if="!duedate" #icon>
|
||||
<Plus :size="20" />
|
||||
@@ -48,7 +52,7 @@
|
||||
</NcActions>
|
||||
|
||||
<NcButton v-if="!card.done"
|
||||
type="secondary"
|
||||
variant="secondary"
|
||||
class="completed-button"
|
||||
@click="changeCardDoneStatus()">
|
||||
<template #icon>
|
||||
@@ -69,14 +73,14 @@
|
||||
</div>
|
||||
<div class="due-actions">
|
||||
<NcButton v-if="!card.archived"
|
||||
type="tertiary"
|
||||
variant="tertiary"
|
||||
:name="t('deck', 'Not done')"
|
||||
@click="changeCardDoneStatus()">
|
||||
<template #icon>
|
||||
<ClearIcon :size="20" />
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton type="secondary" @click="archiveUnarchiveCard()">
|
||||
<NcButton variant="secondary" @click="archiveUnarchiveCard()">
|
||||
<template #icon>
|
||||
<ArchiveIcon :size="20" />
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="selector-wrapper--icon">
|
||||
<TagMultiple :size="20" />
|
||||
</div>
|
||||
<NcSelect :value="assignedLabels"
|
||||
<NcSelect v-model="assignedLabels"
|
||||
class="selector-wrapper--selector"
|
||||
:multiple="true"
|
||||
:disabled="disabled"
|
||||
|
||||
@@ -72,7 +72,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../css/variables';
|
||||
@use '../../css/variables';
|
||||
|
||||
.card-cover {
|
||||
height: 90px;
|
||||
|
||||
@@ -88,6 +88,7 @@ import CardMenu from './CardMenu.vue'
|
||||
import CardCover from './CardCover.vue'
|
||||
import DueDate from './badges/DueDate.vue'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
const TITLE_EDITING_STATE = {
|
||||
OFF: 0,
|
||||
@@ -120,6 +121,7 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['open-card'],
|
||||
data() {
|
||||
return {
|
||||
highlight: false,
|
||||
@@ -226,8 +228,9 @@ export default {
|
||||
this.$router.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => {})
|
||||
return
|
||||
}
|
||||
|
||||
this.$root.$emit('open-card', this.card.id)
|
||||
emit('open-card', {
|
||||
cardId: this.card.id,
|
||||
})
|
||||
},
|
||||
triggerEditTitle() {
|
||||
this.editingTitle = TITLE_EDITING_STATE.PENDING
|
||||
@@ -318,8 +321,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './../../css/animations';
|
||||
@import './../../css/variables';
|
||||
@use './../../css/animations';
|
||||
@use './../../css/variables';
|
||||
|
||||
@mixin dark-card {
|
||||
border: 2px solid var(--color-border-dark);
|
||||
@@ -331,8 +334,8 @@ export default {
|
||||
border-radius: var(--border-radius-large);
|
||||
font-size: 100%;
|
||||
background-color: var(--color-main-background);
|
||||
margin-bottom: $card-spacing;
|
||||
padding: var(--default-grid-baseline) $card-padding;
|
||||
margin-bottom: variables.$card-spacing;
|
||||
padding: var(--default-grid-baseline) variables.$card-padding;
|
||||
border: 2px solid var(--color-border-dark);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@@ -470,7 +473,7 @@ export default {
|
||||
width: 32px;
|
||||
}
|
||||
&.has-labels {
|
||||
padding-bottom: $card-padding;
|
||||
padding-bottom: variables.$card-padding;
|
||||
}
|
||||
.labels {
|
||||
height: 6px;
|
||||
|
||||
@@ -6,40 +6,190 @@
|
||||
<template>
|
||||
<div v-if="card" class="card-menu" @click.stop.prevent>
|
||||
<NcButton v-if="card.referenceData"
|
||||
type="tertiary"
|
||||
:title="t('deck','Open link')"
|
||||
variant="tertiary"
|
||||
:title="t('deck', 'Open link')"
|
||||
@click="openLink">
|
||||
<template #icon>
|
||||
<LinkIcon :size="20" />
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcActions>
|
||||
<CardMenuEntries :card="card" @edit-title="editTitle" />
|
||||
<NcActionButton v-if="!hideDetailsEntry" :close-after-click="true" @click="openCard">
|
||||
<template #icon>
|
||||
<CardBulletedIcon icon :size="20" decorative />
|
||||
</template>
|
||||
{{ t('deck', 'Card details') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit" :close-after-click="true" @click="editTitle">
|
||||
<template #icon>
|
||||
<PencilIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ t('deck', 'Edit title') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit && !isCurrentUserAssigned"
|
||||
icon="icon-user"
|
||||
:close-after-click="true"
|
||||
@click="assignCardToMe()">
|
||||
{{ t('deck', 'Assign to me') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit && isCurrentUserAssigned"
|
||||
icon="icon-user"
|
||||
:close-after-click="true"
|
||||
@click="unassignCardFromMe()">
|
||||
{{ t('deck', 'Unassign myself') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-checkmark"
|
||||
:close-after-click="true"
|
||||
@click="changeCardDoneStatus()">
|
||||
{{ card.done ? t('deck', 'Mark as not done') : t('deck', 'Mark as done') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-external"
|
||||
:close-after-click="true"
|
||||
@click="openCardMoveDialog">
|
||||
{{ t('deck', 'Move/copy card') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-for="action in cardActions"
|
||||
:key="action.label"
|
||||
:close-after-click="true"
|
||||
:icon="action.icon"
|
||||
@click="action.callback(cardRichObject)">
|
||||
{{ action.label }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEditBoard" :close-after-click="true" @click="archiveUnarchiveCard()">
|
||||
<template #icon>
|
||||
<ArchiveIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ card.archived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-delete"
|
||||
:close-after-click="true"
|
||||
@click="deleteCard()">
|
||||
{{ t('deck', 'Delete card') }}
|
||||
</NcActionButton>
|
||||
</NcActions>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { NcActions, NcButton } from '@nextcloud/vue'
|
||||
import { NcActions, NcButton, NcActionButton } from '@nextcloud/vue'
|
||||
import LinkIcon from 'vue-material-design-icons/Link.vue'
|
||||
import CardMenuEntries from './CardMenuEntries.vue'
|
||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import CardBulletedIcon from 'vue-material-design-icons/CardBulleted.vue'
|
||||
import PencilIcon from 'vue-material-design-icons/Pencil.vue'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { showUndo } from '@nextcloud/dialogs'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
import '@nextcloud/dialogs/style.css'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
export default {
|
||||
name: 'CardMenu',
|
||||
components: { NcActions, NcButton, LinkIcon, CardMenuEntries },
|
||||
components: { NcActions, NcButton, LinkIcon, NcActionButton, PencilIcon, CardBulletedIcon, ArchiveIcon },
|
||||
props: {
|
||||
card: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
hideDetailsEntry: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['edit-title'],
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'isArchived',
|
||||
'boards',
|
||||
'cardActions',
|
||||
'stackById',
|
||||
'boardById',
|
||||
]),
|
||||
...mapState({
|
||||
showArchived: state => state.showArchived,
|
||||
currentBoard: state => state.currentBoard,
|
||||
}),
|
||||
canEdit() {
|
||||
return !this.card.archived
|
||||
},
|
||||
canEditBoard() {
|
||||
if (this.currentBoard) {
|
||||
return this.$store.getters.canEdit
|
||||
}
|
||||
const board = this.$store.getters.boards.find((item) => item.id === this.card.boardId)
|
||||
return !!board?.permissions?.PERMISSION_EDIT
|
||||
},
|
||||
isCurrentUserAssigned() {
|
||||
return this.card.assignedUsers.find((item) => item.type === 0 && item.participant.uid === getCurrentUser()?.uid)
|
||||
},
|
||||
boardId() {
|
||||
return this.card?.boardId ? this.card.boardId : Number(this.$route.params.id)
|
||||
},
|
||||
cardRichObject() {
|
||||
return {
|
||||
id: '' + this.card.id,
|
||||
name: this.card.title,
|
||||
boardname: this.boardById(this.boardId)?.title,
|
||||
stackname: this.stackById(this.card.stackId)?.title,
|
||||
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.card.id}`,
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openLink() {
|
||||
window.open(this.card?.referenceData?.openGraphObject?.link)
|
||||
return false
|
||||
},
|
||||
editTitle(id) {
|
||||
this.$emit('edit-title', id)
|
||||
openCard() {
|
||||
const boardId = this.card?.boardId ? this.card.boardId : this.$route?.params.id ?? this.currentBoard.id
|
||||
|
||||
if (this.$router) {
|
||||
this.$router?.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => { })
|
||||
return
|
||||
}
|
||||
|
||||
this.$root.$emit('open-card', this.card.id)
|
||||
},
|
||||
editTitle() {
|
||||
this.$emit('edit-title', this.card.id)
|
||||
},
|
||||
deleteCard() {
|
||||
this.$store.dispatch('deleteCard', this.card)
|
||||
const undoCard = { ...this.card, deletedAt: 0 }
|
||||
showUndo(t('deck', 'Card deleted'), () => this.$store.dispatch('cardUndoDelete', undoCard))
|
||||
if (this.$router.currentRoute.name === 'card') {
|
||||
this.$router.push({ name: 'board' })
|
||||
}
|
||||
},
|
||||
changeCardDoneStatus() {
|
||||
this.$store.dispatch('changeCardDoneStatus', { ...this.card, done: !this.card.done })
|
||||
},
|
||||
archiveUnarchiveCard() {
|
||||
this.$store.dispatch('archiveUnarchiveCard', { ...this.card, archived: !this.card.archived })
|
||||
},
|
||||
assignCardToMe() {
|
||||
this.$store.dispatch('assignCardToUser', {
|
||||
card: this.card,
|
||||
assignee: {
|
||||
userId: getCurrentUser()?.uid,
|
||||
type: 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
unassignCardFromMe() {
|
||||
this.$store.dispatch('removeUserFromCard', {
|
||||
card: this.card,
|
||||
assignee: {
|
||||
userId: getCurrentUser()?.uid,
|
||||
type: 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
openCardMoveDialog() {
|
||||
emit('deck:card:show-move-dialog', this.card)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
<!--
|
||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<NcActionButton v-if="!hideDetailsEntry" :close-after-click="true" @click="openCard">
|
||||
<CardBulletedIcon slot="icon" :size="20" decorative />
|
||||
{{ t('deck', 'Card details') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit" :close-after-click="true" @click="editTitle">
|
||||
<template #icon>
|
||||
<PencilIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ t('deck', 'Edit title') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit && !isCurrentUserAssigned"
|
||||
icon="icon-user"
|
||||
:close-after-click="true"
|
||||
@click="assignCardToMe()">
|
||||
{{ t('deck', 'Assign to me') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit && isCurrentUserAssigned"
|
||||
icon="icon-user"
|
||||
:close-after-click="true"
|
||||
@click="unassignCardFromMe()">
|
||||
{{ t('deck', 'Unassign myself') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-checkmark"
|
||||
:close-after-click="true"
|
||||
@click="changeCardDoneStatus()">
|
||||
{{ card.done ? t('deck', 'Mark as not done') : t('deck', 'Mark as done') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-external"
|
||||
:close-after-click="true"
|
||||
@click="openCardMoveDialog">
|
||||
{{ t('deck', 'Move/copy card') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-for="action in cardActions"
|
||||
:key="action.label"
|
||||
:close-after-click="true"
|
||||
:icon="action.icon"
|
||||
@click="action.callback(cardRichObject)">
|
||||
{{ action.label }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEditBoard" :close-after-click="true" @click="archiveUnarchiveCard()">
|
||||
<template #icon>
|
||||
<ArchiveIcon :size="20" decorative />
|
||||
</template>
|
||||
{{ card.archived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton v-if="canEdit"
|
||||
icon="icon-delete"
|
||||
:close-after-click="true"
|
||||
@click="deleteCard()">
|
||||
{{ t('deck', 'Delete card') }}
|
||||
</NcActionButton>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { NcActionButton } from '@nextcloud/vue'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||
import CardBulletedIcon from 'vue-material-design-icons/CardBulleted.vue'
|
||||
import PencilIcon from 'vue-material-design-icons/Pencil.vue'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { showUndo } from '@nextcloud/dialogs'
|
||||
|
||||
import '@nextcloud/dialogs/style.css'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
export default {
|
||||
name: 'CardMenuEntries',
|
||||
components: { NcActionButton, ArchiveIcon, CardBulletedIcon, PencilIcon },
|
||||
props: {
|
||||
card: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
hideDetailsEntry: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['edit-title'],
|
||||
data() {
|
||||
return {
|
||||
modalShow: false,
|
||||
selectedBoard: '',
|
||||
selectedStack: '',
|
||||
stacksFromBoard: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'isArchived',
|
||||
'boards',
|
||||
'cardActions',
|
||||
'stackById',
|
||||
'boardById',
|
||||
]),
|
||||
...mapState({
|
||||
showArchived: state => state.showArchived,
|
||||
currentBoard: state => state.currentBoard,
|
||||
}),
|
||||
canEdit() {
|
||||
return !this.card.archived
|
||||
},
|
||||
canEditBoard() {
|
||||
if (this.currentBoard) {
|
||||
return this.$store.getters.canEdit
|
||||
}
|
||||
const board = this.$store.getters.boards.find((item) => item.id === this.card.boardId)
|
||||
return !!board?.permissions?.PERMISSION_EDIT
|
||||
},
|
||||
isCurrentUserAssigned() {
|
||||
return this.card.assignedUsers.find((item) => item.type === 0 && item.participant.uid === getCurrentUser()?.uid)
|
||||
},
|
||||
boardId() {
|
||||
return this.card?.boardId ? this.card.boardId : Number(this.$route.params.id)
|
||||
},
|
||||
cardRichObject() {
|
||||
return {
|
||||
id: '' + this.card.id,
|
||||
name: this.card.title,
|
||||
boardname: this.boardById(this.boardId)?.title,
|
||||
stackname: this.stackById(this.card.stackId)?.title,
|
||||
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.card.id}`,
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openCard() {
|
||||
const boardId = this.card?.boardId ? this.card.boardId : this.$route?.params.id ?? this.currentBoard.id
|
||||
|
||||
if (this.$router) {
|
||||
this.$router?.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => {})
|
||||
return
|
||||
}
|
||||
|
||||
this.$root.$emit('open-card', this.card.id)
|
||||
},
|
||||
editTitle() {
|
||||
this.$emit('edit-title', this.card.id)
|
||||
},
|
||||
deleteCard() {
|
||||
this.$store.dispatch('deleteCard', this.card)
|
||||
const undoCard = { ...this.card, deletedAt: 0 }
|
||||
showUndo(t('deck', 'Card deleted'), () => this.$store.dispatch('cardUndoDelete', undoCard))
|
||||
if (this.$router.currentRoute.name === 'card') {
|
||||
this.$router.push({ name: 'board' })
|
||||
}
|
||||
},
|
||||
changeCardDoneStatus() {
|
||||
this.$store.dispatch('changeCardDoneStatus', { ...this.card, done: !this.card.done })
|
||||
},
|
||||
archiveUnarchiveCard() {
|
||||
this.$store.dispatch('archiveUnarchiveCard', { ...this.card, archived: !this.card.archived })
|
||||
},
|
||||
assignCardToMe() {
|
||||
this.$store.dispatch('assignCardToUser', {
|
||||
card: this.card,
|
||||
assignee: {
|
||||
userId: getCurrentUser()?.uid,
|
||||
type: 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
unassignCardFromMe() {
|
||||
this.$store.dispatch('removeUserFromCard', {
|
||||
card: this.card,
|
||||
assignee: {
|
||||
userId: getCurrentUser()?.uid,
|
||||
type: 0,
|
||||
},
|
||||
})
|
||||
},
|
||||
openCardMoveDialog() {
|
||||
emit('deck:card:show-move-dialog', this.card)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -151,16 +151,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcModal, Tooltip } from '@nextcloud/vue'
|
||||
import { NcModal } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
name: 'HelpModal',
|
||||
components: {
|
||||
NcModal,
|
||||
},
|
||||
directives: {
|
||||
Tooltip,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
</template>
|
||||
</AppNavigationBoardCategory>
|
||||
<AppNavigationAddBoard v-if="canCreate" />
|
||||
<AppNavigationImportBoard v-if="canCreate" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<NcAppNavigationSettings :name="t('deck', 'Deck settings')">
|
||||
@@ -116,6 +117,7 @@ import DeckIcon from './../icons/DeckIcon.vue'
|
||||
import ShareVariantIcon from 'vue-material-design-icons/Share.vue'
|
||||
import HelpModal from './../modals/HelpModal.vue'
|
||||
import { subscribe } from '@nextcloud/event-bus'
|
||||
import AppNavigationImportBoard from './AppNavigationImportBoard.vue'
|
||||
|
||||
const canCreateState = loadState('deck', 'canCreate')
|
||||
|
||||
@@ -127,6 +129,7 @@ export default {
|
||||
NcButton,
|
||||
AppNavigationAddBoard,
|
||||
AppNavigationBoardCategory,
|
||||
AppNavigationImportBoard,
|
||||
NcSelect,
|
||||
NcAppNavigationItem,
|
||||
ArchiveIcon,
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
</NcColorPicker>
|
||||
<form @submit.prevent.stop="createBoard">
|
||||
<NcTextField ref="inputField"
|
||||
v-model="value"
|
||||
:disable="loading"
|
||||
:value.sync="value"
|
||||
:placeholder="t('deck', 'Board name')"
|
||||
type="text"
|
||||
required />
|
||||
<NcButton type="tertiary"
|
||||
<NcButton variant="tertiary"
|
||||
:disabled="loading"
|
||||
:title="t('deck', 'Cancel edit')"
|
||||
@click.stop.prevent="cancelEdit">
|
||||
@@ -26,8 +26,8 @@
|
||||
<CloseIcon :size="20" />
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton type="tertiary"
|
||||
native-type="submit"
|
||||
<NcButton variant="tertiary"
|
||||
type="submit"
|
||||
:disabled="loading"
|
||||
:title="t('deck', 'Save board')">
|
||||
<template #icon>
|
||||
@@ -77,6 +77,7 @@ export default {
|
||||
})
|
||||
},
|
||||
async createBoard(e) {
|
||||
alert('createBoard called')
|
||||
this.loading = true
|
||||
const title = this.value.trim()
|
||||
await this.$store.dispatch('createBoard', {
|
||||
@@ -88,6 +89,7 @@ export default {
|
||||
this.color = randomColor()
|
||||
},
|
||||
cancelEdit(e) {
|
||||
alert('cancelEdit called')
|
||||
this.editing = false
|
||||
this.color = randomColor()
|
||||
},
|
||||
|
||||
@@ -15,13 +15,17 @@
|
||||
<template #icon>
|
||||
<NcAppNavigationIconBullet :color="board.color" />
|
||||
<BoardCloneModal v-if="cloneModalOpen" :board-title="board.title" @close="onCloseCloneModal" />
|
||||
<BoardExportModal v-if="exportModalOpen"
|
||||
:board-title="board.title"
|
||||
@export="onExportBoard"
|
||||
@close="onCloseExportBoard" />
|
||||
</template>
|
||||
|
||||
<template #counter>
|
||||
<AccountIcon v-if="board.acl.length > 0" />
|
||||
</template>
|
||||
|
||||
<template v-if="!deleted" slot="actions">
|
||||
<template v-if="!deleted" #actions>
|
||||
<template v-if="!isDueSubmenuActive">
|
||||
<NcActionButton icon="icon-info"
|
||||
:close-after-click="true"
|
||||
@@ -125,17 +129,17 @@
|
||||
:placeholder="t('deck', 'Board name')"
|
||||
type="text"
|
||||
required />
|
||||
<NcButton type="tertiary"
|
||||
<NcButton variant="tertiary"
|
||||
:disabled="loading"
|
||||
native-type="submit"
|
||||
type="submit"
|
||||
:title="t('deck', 'Cancel edit')"
|
||||
@click.stop.prevent="cancelEdit">
|
||||
<template #icon>
|
||||
<CloseIcon :size="20" />
|
||||
</template>
|
||||
</NcButton>
|
||||
<NcButton type="tertiary"
|
||||
native-type="submit"
|
||||
<NcButton variant="tertiary"
|
||||
type="submit"
|
||||
:disabled="loading"
|
||||
:title="t('deck', 'Save board')">
|
||||
<template #icon>
|
||||
@@ -148,7 +152,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcAppNavigationIconBullet, NcAppNavigationItem, NcColorPicker, NcButton, NcTextField, NcActionButton } from '@nextcloud/vue'
|
||||
import { NcAppNavigationIconBullet, NcAppNavigationItem, NcColorPicker, NcButton, NcTextField, NcActionButton, NcLoadingIcon } from '@nextcloud/vue'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||
import CloneIcon from 'vue-material-design-icons/ContentDuplicate.vue'
|
||||
@@ -161,6 +165,8 @@ import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
import isTouchDevice from '../../mixins/isTouchDevice.js'
|
||||
import BoardCloneModal from './BoardCloneModal.vue'
|
||||
import BoardExportModal from './BoardExportModal.vue'
|
||||
import { showLoading } from '@nextcloud/dialogs'
|
||||
|
||||
const canCreateState = loadState('deck', 'canCreate')
|
||||
|
||||
@@ -179,6 +185,8 @@ export default {
|
||||
CloseIcon,
|
||||
CheckIcon,
|
||||
BoardCloneModal,
|
||||
BoardExportModal,
|
||||
NcLoadingIcon,
|
||||
},
|
||||
directives: {
|
||||
ClickOutside,
|
||||
@@ -207,6 +215,7 @@ export default {
|
||||
updateDueSetting: null,
|
||||
canCreate: canCreateState,
|
||||
cloneModalOpen: false,
|
||||
exportModalOpen: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -346,7 +355,16 @@ export default {
|
||||
this.updateDueSetting = null
|
||||
},
|
||||
actionExport() {
|
||||
this.boardApi.exportBoard(this.board)
|
||||
this.exportModalOpen = true
|
||||
},
|
||||
async onExportBoard(format) {
|
||||
this.exportModalOpen = false
|
||||
const loadingToast = showLoading(t('deck', 'Exporting board...'))
|
||||
await this.boardApi.exportBoard(this.board, format)
|
||||
loadingToast.hideToast()
|
||||
},
|
||||
onCloseExportBoard() {
|
||||
this.exportModalOpen = false
|
||||
},
|
||||
onNavigate() {
|
||||
if (this.isTouchDevice) {
|
||||
|
||||
@@ -31,10 +31,6 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
||||
55
src/components/navigation/AppNavigationImportBoard.vue
Normal file
55
src/components/navigation/AppNavigationImportBoard.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<!--
|
||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<NcAppNavigationItem :name="t('deck', 'Import board')" icon="icon-upload" @click.prevent.stop="startImportBoard" />
|
||||
<input ref="fileInput"
|
||||
type="file"
|
||||
accept="application/json"
|
||||
style="display: none;"
|
||||
@change="doImportBoard">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcAppNavigationItem } from '@nextcloud/vue'
|
||||
import { showError } from '../../helpers/errors.js'
|
||||
import { showSuccess, showLoading } from '@nextcloud/dialogs'
|
||||
|
||||
export default {
|
||||
name: 'AppNavigationImportBoard',
|
||||
components: { NcAppNavigationItem },
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startImportBoard() {
|
||||
this.$refs.fileInput.value = ''
|
||||
this.$refs.fileInput.click()
|
||||
},
|
||||
async doImportBoard(event) {
|
||||
const file = event.target.files[0]
|
||||
if (file) {
|
||||
const loadingToast = showLoading(t('deck', 'Importing board...'))
|
||||
const result = await this.$store.dispatch('importBoard', file)
|
||||
loadingToast.hideToast()
|
||||
if (result?.message) {
|
||||
showError(result)
|
||||
} else {
|
||||
showSuccess(t('deck', 'Board imported successfully'))
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -5,16 +5,16 @@
|
||||
<template>
|
||||
<NcDialog :name="t('deck', 'Clone {boardTitle}', {boardTitle: boardTitle})" :show="true" @close="close(false)">
|
||||
<div class="modal__content">
|
||||
<NcCheckboxRadioSwitch :checked.sync="withCards">
|
||||
<NcCheckboxRadioSwitch v-model="withCards">
|
||||
{{ t('deck', 'Clone cards') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withAssignments">
|
||||
<NcCheckboxRadioSwitch v-if="withCards" v-model="withAssignments">
|
||||
{{ t('deck', 'Clone assignments') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withLabels">
|
||||
<NcCheckboxRadioSwitch v-if="withCards" v-model="withLabels">
|
||||
{{ t('deck', 'Clone labels') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withDueDate">
|
||||
<NcCheckboxRadioSwitch v-if="withCards" v-model="withDueDate">
|
||||
{{ t('deck', 'Clone due dates') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<div v-if="withCards" class="accordion" :class="{ 'is-open': accordionOpen }">
|
||||
@@ -25,10 +25,10 @@
|
||||
{{ t('deck', 'Advanced options') }}
|
||||
</div>
|
||||
<div v-if="accordionOpen" class="accordion__content">
|
||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="moveCardsToLeftStack">
|
||||
<NcCheckboxRadioSwitch v-if="withCards" v-model="moveCardsToLeftStack">
|
||||
{{ t('deck', 'Move all cards to the first list') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="restoreArchivedCards">
|
||||
<NcCheckboxRadioSwitch v-if="withCards" v-model="restoreArchivedCards">
|
||||
{{ t('deck', 'Restore archived cards') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
<NcButton @click="cancel">
|
||||
{{ t('deck', 'Cancel') }}
|
||||
</NcButton>
|
||||
<NcButton type="primary" @click="save">
|
||||
<NcButton variant="primary" @click="save">
|
||||
{{ t('deck', 'Clone') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
|
||||
78
src/components/navigation/BoardExportModal.vue
Normal file
78
src/components/navigation/BoardExportModal.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<!--
|
||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<NcDialog :name="t('deck', 'Export {boardTitle}', {boardTitle: boardTitle})" @update:open="close">
|
||||
<div class="modal__content">
|
||||
<NcCheckboxRadioSwitch v-model="exportFormat"
|
||||
value="json"
|
||||
type="radio"
|
||||
name="board_export_format">
|
||||
{{ t('deck', 'Export as JSON') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcCheckboxRadioSwitch v-model="exportFormat"
|
||||
value="csv"
|
||||
type="radio"
|
||||
name="board_export_format">
|
||||
{{ t('deck', 'Export as CSV') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<p class="note">
|
||||
{{ t('deck', 'Note: Only the JSON format is supported for importing back into the Deck app.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<template #actions>
|
||||
<NcButton @click="close">
|
||||
{{ t('deck', 'Cancel') }}
|
||||
</NcButton>
|
||||
<NcButton variant="primary" @click="exportBoard">
|
||||
{{ t('deck', 'Export') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
</NcDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcButton, NcCheckboxRadioSwitch, NcDialog } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
name: 'BoardExportModal',
|
||||
components: {
|
||||
NcDialog,
|
||||
NcCheckboxRadioSwitch,
|
||||
NcButton,
|
||||
},
|
||||
props: {
|
||||
boardTitle: {
|
||||
type: String,
|
||||
default: 'Board',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportFormat: 'json',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
exportBoard() {
|
||||
this.$emit('export', this.exportFormat)
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$emit('close')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal__content {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
p.note {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -145,7 +145,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './../../css/variables';
|
||||
@use './../../css/variables';
|
||||
|
||||
.overview-wrapper {
|
||||
position: relative;
|
||||
@@ -162,16 +162,16 @@ export default {
|
||||
overflow-x: scroll;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
padding-left: $board-spacing;
|
||||
padding-right: $board-spacing;
|
||||
padding-left: variables.$board-spacing;
|
||||
padding-right: variables.$board-spacing;
|
||||
|
||||
.dashboard-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: $stack-width;
|
||||
width: $stack-width;
|
||||
margin-left: $stack-spacing;
|
||||
margin-right: $stack-spacing;
|
||||
min-width: variables.$stack-width;
|
||||
width: variables.$stack-width;
|
||||
margin-left: variables.$stack-spacing;
|
||||
margin-right: variables.$stack-spacing;
|
||||
|
||||
h3 {
|
||||
font-size: var(--default-font-size);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div v-if="searchQuery!==''" class="global-search">
|
||||
<div v-if="searchQuery !== ''" class="global-search">
|
||||
<h2>
|
||||
<NcRichText :text="t('deck', 'Search for {searchQuery} in all boards')" :arguments="queryStringArgs" />
|
||||
<div v-if="loading" class="icon-loading-small" />
|
||||
@@ -19,13 +19,13 @@
|
||||
:key="card.id"
|
||||
:standalone="true" />
|
||||
<Placeholder v-if="loading" />
|
||||
<InfiniteLoading :identifier="searchQuery" @infinite="infiniteHandler">
|
||||
<!-- <InfiniteLoading :identifier="searchQuery" @infinite="infiniteHandler">
|
||||
<div slot="spinner" />
|
||||
<div slot="no-more" />
|
||||
<div slot="no-results">
|
||||
{{ t('deck', 'No results found') }}
|
||||
</div>
|
||||
</InfiniteLoading>
|
||||
</InfiniteLoading> -->
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>{{ t('deck', 'No results found') }}</p>
|
||||
@@ -39,7 +39,7 @@ import CardItem from '../cards/CardItem.vue'
|
||||
import { mapState } from 'vuex'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import InfiniteLoading from 'vue-infinite-loading'
|
||||
// import InfiniteLoading from 'v3-infinite-loading'
|
||||
import Placeholder from './Placeholder.vue'
|
||||
import { NcActions, NcActionButton, NcRichText } from '@nextcloud/vue'
|
||||
|
||||
@@ -70,7 +70,14 @@ function search({ query, cursor }) {
|
||||
|
||||
export default {
|
||||
name: 'GlobalSearchResults',
|
||||
components: { CardItem, InfiniteLoading, NcRichText, Placeholder, NcActions, NcActionButton },
|
||||
components: {
|
||||
CardItem,
|
||||
// InfiniteLoading,
|
||||
NcRichText,
|
||||
Placeholder,
|
||||
NcActions,
|
||||
NcActionButton,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
results: [],
|
||||
@@ -155,11 +162,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../css/variables';
|
||||
@use '../../css/variables';
|
||||
|
||||
.global-search {
|
||||
width: 100%;
|
||||
padding: $board-spacing + $stack-spacing;
|
||||
padding: variables.$board-spacing + variables.$stack-spacing;
|
||||
padding-bottom: 0;
|
||||
overflow: hidden;
|
||||
min-height: 35vh;
|
||||
@@ -175,6 +182,7 @@ export default {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
@@ -182,13 +190,14 @@ export default {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h2 > div {
|
||||
h2>div {
|
||||
display: inline-block;
|
||||
|
||||
&.icon-loading-small {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
h2:deep(span) {
|
||||
background-color: var(--color-background-dark);
|
||||
padding: 3px;
|
||||
@@ -199,13 +208,14 @@ export default {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > div {
|
||||
&>div {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:deep(.card) {
|
||||
width: $stack-width;
|
||||
margin-right: $stack-spacing;
|
||||
width: variables.$stack-width;
|
||||
margin-right: variables.$stack-spacing;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -58,18 +58,18 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../css/variables';
|
||||
@use '../../css/variables';
|
||||
$clickable-area: var(--default-clickable-area);
|
||||
|
||||
.card--placeholder {
|
||||
width: $stack-width;
|
||||
margin-right: $stack-spacing;
|
||||
padding: $card-padding;
|
||||
width: variables.$stack-width;
|
||||
margin-right: variables.$stack-spacing;
|
||||
padding: variables.$card-padding;
|
||||
transition: box-shadow 0.1s ease-in-out;
|
||||
box-shadow: 0 0 2px 0 var(--color-box-shadow);
|
||||
border-radius: var(--border-radius-large);
|
||||
font-size: 100%;
|
||||
margin-bottom: $card-spacing;
|
||||
margin-bottom: variables.$card-spacing;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,38 +3,34 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
import './../css/collections.css'
|
||||
import FileSharingPicker from './views/FileSharingPicker.js'
|
||||
import { buildSelector } from './helpers/selector.js'
|
||||
|
||||
import './shared-init.js'
|
||||
|
||||
Vue.prototype.t = t
|
||||
Vue.prototype.n = n
|
||||
Vue.prototype.OC = OC
|
||||
export function initCollections() {
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
if (OCA.Sharing && OCA.Sharing.ShareSearch) {
|
||||
OCA.Sharing.ShareSearch.addNewResult(FileSharingPicker)
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
if (OCA.Sharing && OCA.Sharing.ShareSearch) {
|
||||
OCA.Sharing.ShareSearch.addNewResult(FileSharingPicker)
|
||||
}
|
||||
window.OCP.Collaboration.registerType('deck', {
|
||||
action: () => {
|
||||
const BoardSelector = () => import('./BoardSelector.vue')
|
||||
return buildSelector(BoardSelector)
|
||||
},
|
||||
typeString: t('deck', 'Link to a board'),
|
||||
typeIconClass: 'icon-deck',
|
||||
})
|
||||
|
||||
window.OCP.Collaboration.registerType('deck', {
|
||||
action: () => {
|
||||
const BoardSelector = () => import('./BoardSelector.vue')
|
||||
return buildSelector(BoardSelector)
|
||||
},
|
||||
typeString: t('deck', 'Link to a board'),
|
||||
typeIconClass: 'icon-deck',
|
||||
window.OCP.Collaboration.registerType('deck-card', {
|
||||
action: () => {
|
||||
const CardSelector = () => import('./CardSelector.vue')
|
||||
return buildSelector(CardSelector)
|
||||
},
|
||||
typeString: t('deck', 'Link to a card'),
|
||||
typeIconClass: 'icon-deck',
|
||||
})
|
||||
})
|
||||
|
||||
window.OCP.Collaboration.registerType('deck-card', {
|
||||
action: () => {
|
||||
const CardSelector = () => import('./CardSelector.vue')
|
||||
return buildSelector(CardSelector)
|
||||
},
|
||||
typeString: t('deck', 'Link to a card'),
|
||||
typeIconClass: 'icon-deck',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { registerWidget, registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue/dist/Functions/registerReference.js'
|
||||
import { registerWidget, registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue'
|
||||
import { translate, translatePlural } from '@nextcloud/l10n'
|
||||
|
||||
import './shared-init.js'
|
||||
|
||||
48
src/main.js
48
src/main.js
@@ -2,11 +2,10 @@
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import Vue from 'vue'
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router.js'
|
||||
import store from './store/main.js'
|
||||
import { sync } from 'vuex-router-sync'
|
||||
import { translate, translatePlural } from '@nextcloud/l10n'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { subscribe } from '@nextcloud/event-bus'
|
||||
@@ -14,27 +13,33 @@ import ClickOutside from 'vue-click-outside'
|
||||
import './shared-init.js'
|
||||
import './models/index.js'
|
||||
import './sessions.js'
|
||||
import { initCollections } from './init-collections.js'
|
||||
|
||||
// the server snap.js conflicts with vertical scrolling so we disable it
|
||||
document.body.setAttribute('data-snap-ignore', 'true')
|
||||
|
||||
sync(store, router)
|
||||
const app = createApp(App)
|
||||
|
||||
Vue.prototype.t = translate
|
||||
Vue.prototype.n = translatePlural
|
||||
app.config.globalProperties.t = translate
|
||||
app.config.globalProperties.n = translatePlural
|
||||
app.config.globalProperties.OC = OC
|
||||
|
||||
Vue.directive('click-outside', ClickOutside)
|
||||
initCollections({ t, n, OC })
|
||||
|
||||
Vue.directive('focus', {
|
||||
inserted(el) {
|
||||
app.directive('click-outside', ClickOutside)
|
||||
|
||||
app.directive('focus', {
|
||||
mounted(el) {
|
||||
el.focus()
|
||||
},
|
||||
})
|
||||
|
||||
Vue.config.errorHandler = (err, vm, info) => {
|
||||
app.config.errorHandler = (err, vm, info) => {
|
||||
if (err.response && err.response.data.message) {
|
||||
const errorMessage = t('deck', 'Something went wrong')
|
||||
showError(`${errorMessage}: ${err.response.data.status} ${err.response.data.message}`)
|
||||
const errorMessage = translate('deck', 'Something went wrong')
|
||||
showError(
|
||||
`${errorMessage}: ${err.response.data.status} ${err.response.data.message}`,
|
||||
)
|
||||
}
|
||||
console.error(err)
|
||||
}
|
||||
@@ -47,16 +52,14 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
window.OCA.Files = {}
|
||||
}
|
||||
// register unused client for the sidebar to have access to its parser methods
|
||||
Object.assign(window.OCA.Files, { App: { fileList: { filesClient: OC.Files.getClient() } } }, window.OCA.Files)
|
||||
Object.assign(
|
||||
window.OCA.Files,
|
||||
{ App: { fileList: { filesClient: OC.Files.getClient() } } },
|
||||
window.OCA.Files,
|
||||
)
|
||||
})
|
||||
|
||||
/* eslint-disable-next-line no-new */
|
||||
new Vue({
|
||||
el: '#content',
|
||||
// eslint-disable-next-line vue/match-component-file-name
|
||||
name: 'Deck',
|
||||
router,
|
||||
store,
|
||||
app.mixin({
|
||||
data() {
|
||||
return {
|
||||
time: Date.now(),
|
||||
@@ -75,7 +78,7 @@ new Vue({
|
||||
this.time = Date.now()
|
||||
}, 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
clearInterval(this.interval)
|
||||
},
|
||||
methods: {
|
||||
@@ -86,9 +89,12 @@ new Vue({
|
||||
this.$store.commit('setSearchQuery', '')
|
||||
},
|
||||
},
|
||||
render: h => h(App),
|
||||
})
|
||||
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
app.mount('#content')
|
||||
|
||||
if (!window.OCA.Deck) {
|
||||
window.OCA.Deck = {}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
this.$set(this.uploadQueue, file.name, { name: file.name, progress: 0 })
|
||||
this.uploadQueue[file.name] = { name: file.name, progress: 0 }
|
||||
|
||||
const bodyFormData = new FormData()
|
||||
bodyFormData.append('cardId', this.cardId)
|
||||
bodyFormData.append('type', type)
|
||||
@@ -39,7 +40,7 @@ export default {
|
||||
onUploadProgress: (e) => {
|
||||
const percentCompleted = Math.round((e.loaded * 100) / e.total)
|
||||
console.debug(percentCompleted)
|
||||
this.$set(this.uploadQueue[file.name], 'progress', percentCompleted)
|
||||
this.uploadQueue[file.name].progress = percentCompleted
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -50,7 +51,7 @@ export default {
|
||||
showError(err.response.data ? err.response.data.message : 'Failed to upload file')
|
||||
}
|
||||
}
|
||||
this.$delete(this.uploadQueue, file.name)
|
||||
delete this.uploadQueue[file.name]
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { generateUrl, getRootUrl } from '@nextcloud/router'
|
||||
import { BOARD_FILTERS } from './store/main.js'
|
||||
import Boards from './components/boards/Boards.vue'
|
||||
@@ -14,17 +13,14 @@ import BoardSidebar from './components/board/BoardSidebar.vue'
|
||||
import CardSidebar from './components/card/CardSidebar.vue'
|
||||
import Overview from './components/overview/Overview.vue'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
// We apply a dynamic base URL depending on the URL used in the browser
|
||||
const baseUrl = generateUrl('/apps/deck/')
|
||||
const webRootWithIndexPHP = getRootUrl() + '/index.php'
|
||||
const doesURLContainIndexPHP = window.location.pathname.startsWith(webRootWithIndexPHP)
|
||||
const currentBaseUrl = doesURLContainIndexPHP ? baseUrl : baseUrl.replace('/index.php/', '/')
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
base: currentBaseUrl,
|
||||
const router = createRouter({
|
||||
history: createWebHistory(currentBaseUrl),
|
||||
linkActiveClass: 'active',
|
||||
routes: [
|
||||
{
|
||||
|
||||
@@ -139,11 +139,30 @@ export class BoardApi {
|
||||
}
|
||||
}
|
||||
|
||||
exportBoard(board) {
|
||||
exportBoard(board, format) {
|
||||
return axios.get(this.url(`/boards/${board.id}/export`))
|
||||
.then(
|
||||
(response) => {
|
||||
const fields = { title: t('deck', 'Card title'), description: t('deck', 'Description'), stackId: t('deck', 'List name'), labels: t('deck', 'Tags'), duedate: t('deck', 'Due date'), createdAt: t('deck', 'Created'), lastModified: t('deck', 'Modified') }
|
||||
if (format === 'json') {
|
||||
const exportData = {
|
||||
boards: [response.data],
|
||||
}
|
||||
const stacks = {}
|
||||
for (const stack of response.data.stacks) {
|
||||
stacks[stack.id] = stack
|
||||
}
|
||||
exportData.boards[0].stacks = stacks
|
||||
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' })
|
||||
const blobUrl = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = blobUrl
|
||||
a.download = response.data.title + '.json'
|
||||
a.click()
|
||||
a.remove()
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const fields = { title: t('deck', 'Card title'), description: t('deck', 'Description'), stackId: t('deck', 'List name'), labels: t('deck', 'Tags'), assignedUsers: t('deck', 'Assigned users'), duedate: t('deck', 'Due date'), createdAt: t('deck', 'Created'), lastModified: t('deck', 'Modified') }
|
||||
let row = ''
|
||||
Object.keys(fields).forEach(field => {
|
||||
row += '"' + fields[field] + '"' + '\t'
|
||||
@@ -160,16 +179,27 @@ export class BoardApi {
|
||||
const date = new Date(Number(card[field]) * 1000)
|
||||
row += '"' + date.toLocaleDateString() + '"' + '\t'
|
||||
} else if (field === 'stackId') {
|
||||
row += '"' + stack.title + '"' + '\t'
|
||||
row += '"' + stack.title.replaceAll('"', '""') + '"' + '\t'
|
||||
} else if (field === 'labels') {
|
||||
row += '"'
|
||||
card[field].forEach(label => {
|
||||
row += label.title + ', '
|
||||
row += label.title.replaceAll('"', '""') + ', '
|
||||
})
|
||||
if (card[field].length > 0) {
|
||||
row = row.slice(0, -1)
|
||||
}
|
||||
row += '"' + '\t'
|
||||
} else if (field === 'assignedUsers') {
|
||||
row += '"'
|
||||
card[field].forEach(assignedUsers => {
|
||||
row += assignedUsers.participant.displayname.replaceAll('"', '""') + ', '
|
||||
})
|
||||
if (card[field].length > 0) {
|
||||
row = row.slice(0, -1)
|
||||
}
|
||||
row += '"' + '\t'
|
||||
} else if (field === 'description' || field === 'title') {
|
||||
row += '"' + card[field].replaceAll('"', '""') + '"' + '\t'
|
||||
} else {
|
||||
row += '"' + card[field] + '"' + '\t'
|
||||
}
|
||||
@@ -204,6 +234,27 @@ export class BoardApi {
|
||||
})
|
||||
}
|
||||
|
||||
importBoard(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return axios.post(this.url('/boards/import'), formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
})
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
// Label API Calls
|
||||
deleteLabel(id) {
|
||||
return axios.delete(this.url(`/labels/${id}`))
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import { AttachmentApi } from './../services/AttachmentApi.js'
|
||||
import Vue from 'vue'
|
||||
|
||||
const apiClient = new AttachmentApi()
|
||||
|
||||
@@ -24,20 +23,20 @@ export default {
|
||||
mutations: {
|
||||
createAttachment(state, { cardId, attachment }) {
|
||||
if (typeof state.attachments[cardId] === 'undefined') {
|
||||
Vue.set(state.attachments, cardId, [attachment])
|
||||
state.attachments[cardId] = [attachment]
|
||||
} else {
|
||||
state.attachments[cardId].push(attachment)
|
||||
}
|
||||
},
|
||||
|
||||
createAttachments(state, { cardId, attachments }) {
|
||||
Vue.set(state.attachments, cardId, attachments)
|
||||
state.attachments[cardId] = attachments
|
||||
},
|
||||
|
||||
updateAttachment(state, { cardId, attachment }) {
|
||||
const existingIndex = state.attachments[attachment.cardId].findIndex(a => a.id === attachment.id && a.type === attachment.type)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.attachments[cardId], existingIndex, attachment)
|
||||
state.attachments[cardId][existingIndex] = attachment
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { CardApi } from './../services/CardApi.js'
|
||||
import moment from 'moment'
|
||||
import Vue from 'vue'
|
||||
|
||||
const apiClient = new CardApi()
|
||||
|
||||
@@ -190,7 +189,7 @@ export default {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
const existingCard = state.cards.find(_card => _card.id === card.id)
|
||||
Vue.set(state.cards, existingIndex, Object.assign({}, existingCard, card))
|
||||
state.cards[existingIndex] = { ...existingCard, ...card }
|
||||
} else {
|
||||
state.cards.push(card)
|
||||
}
|
||||
@@ -204,15 +203,15 @@ export default {
|
||||
updateCard(state, card) {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.cards, existingIndex, Object.assign({}, state.cards[existingIndex], card))
|
||||
state.cards[existingIndex] = { ...state.cards[existingIndex], ...card }
|
||||
}
|
||||
},
|
||||
updateCardsReorder(state, cards) {
|
||||
for (const newCard of cards) {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === newCard.id)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.cards[existingIndex], 'order', newCard.order)
|
||||
Vue.set(state.cards[existingIndex], 'stackId', newCard.stackId)
|
||||
state.cards[existingIndex].order = newCard.order
|
||||
state.cards[existingIndex].stackId = newCard.stackId
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -234,26 +233,26 @@ export default {
|
||||
updateCardProperty(state, { card, property }) {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.cards[existingIndex], property, card[property])
|
||||
Vue.set(state.cards[existingIndex], 'lastModified', Date.now() / 1000)
|
||||
state.cards[existingIndex][property] = card[property]
|
||||
state.cards[existingIndex].lastModifiedBy = Date.now() / 1000
|
||||
}
|
||||
},
|
||||
cardSetAttachmentCount(state, { cardId, count }) {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.cards[existingIndex], 'attachmentCount', count)
|
||||
state.cards[existingIndex].attachmentCount = count
|
||||
}
|
||||
},
|
||||
cardIncreaseAttachmentCount(state, cardId) {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.cards[existingIndex], 'attachmentCount', state.cards[existingIndex].attachmentCount + 1)
|
||||
state.cards[existingIndex].attachmentCount = state.cards[existingIndex].attachmentCount + 1
|
||||
}
|
||||
},
|
||||
cardDecreaseAttachmentCount(state, cardId) {
|
||||
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
||||
if (existingIndex !== -1) {
|
||||
Vue.set(state.cards[existingIndex], 'attachmentCount', state.cards[existingIndex].attachmentCount - 1)
|
||||
state.cards[existingIndex].attachmentCount = state.cards[existingIndex].attachmentCount - 1
|
||||
}
|
||||
},
|
||||
addNewCard(state, card) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import { CommentApi } from '../services/CommentApi.js'
|
||||
import Vue from 'vue'
|
||||
|
||||
const apiClient = new CommentApi()
|
||||
|
||||
@@ -34,10 +33,10 @@ export default {
|
||||
},
|
||||
addComments(state, { comments, cardId }) {
|
||||
if (state.comments[cardId] === undefined) {
|
||||
Vue.set(state.comments, cardId, {
|
||||
hasMore: comments.length > 0,
|
||||
state.comments[cardId] = {
|
||||
hasMore: comments.length >= 0,
|
||||
comments: [...comments],
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const newComments = comments.filter((comment) => {
|
||||
return state.comments[cardId].comments.findIndex((item) => item.id === comment.id) === -1
|
||||
@@ -59,11 +58,11 @@ export default {
|
||||
},
|
||||
markCommentsAsRead(state, cardId) {
|
||||
state.comments[cardId].comments.forEach(_comment => {
|
||||
Vue.set(_comment, 'isUnread', false)
|
||||
_comment.isUnread = false
|
||||
})
|
||||
},
|
||||
setReplyTo(state, comment) {
|
||||
Vue.set(state, 'replyTo', comment)
|
||||
state.replyTo = comment
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
import 'url-search-params-polyfill'
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import { createStore } from 'vuex'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl, generateUrl } from '@nextcloud/router'
|
||||
import { BoardApi } from '../services/BoardApi.js'
|
||||
@@ -18,7 +17,6 @@ import comment from './comment.js'
|
||||
import trashbin from './trashbin.js'
|
||||
import attachment from './attachment.js'
|
||||
import overview from './overview.js'
|
||||
Vue.use(Vuex)
|
||||
|
||||
const apiClient = new BoardApi()
|
||||
const debug = process.env.NODE_ENV !== 'production'
|
||||
@@ -29,7 +27,7 @@ export const BOARD_FILTERS = {
|
||||
SHARED: 'shared',
|
||||
}
|
||||
|
||||
export default new Vuex.Store({
|
||||
const store = createStore({
|
||||
modules: {
|
||||
actions,
|
||||
stack,
|
||||
@@ -130,10 +128,10 @@ export default new Vuex.Store({
|
||||
},
|
||||
mutations: {
|
||||
setFullApp(state, isFullApp) {
|
||||
Vue.set(state, 'isFullApp', isFullApp)
|
||||
state.isFullApp = isFullApp
|
||||
},
|
||||
setHasCardSaveError(state, hasCardSaveError) {
|
||||
Vue.set(state, 'hasCardSaveError', hasCardSaveError)
|
||||
state.hasCardSaveError = hasCardSaveError
|
||||
},
|
||||
SET_CONFIG(state, { key, value }) {
|
||||
const [scope, id, configKey] = key.split(':', 3)
|
||||
@@ -145,11 +143,11 @@ export default new Vuex.Store({
|
||||
})
|
||||
|
||||
if (indexExisting > -1) {
|
||||
Vue.set(state.boards[indexExisting].settings, configKey, value)
|
||||
state.boards[indexExisting].settings[configKey] = value
|
||||
}
|
||||
break
|
||||
default:
|
||||
Vue.set(state.config, key, value)
|
||||
state.config[key] = value
|
||||
}
|
||||
},
|
||||
setSearchQuery(state, searchQuery) {
|
||||
@@ -162,7 +160,7 @@ export default new Vuex.Store({
|
||||
Object.keys(filter).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'due':
|
||||
Vue.set(state.filter, key, filter.due)
|
||||
state.filter[key] = filter.due
|
||||
break
|
||||
default:
|
||||
filter[key].forEach((item) => {
|
||||
@@ -189,7 +187,7 @@ export default new Vuex.Store({
|
||||
})
|
||||
|
||||
if (indexExisting > -1) {
|
||||
Vue.set(state.boards, indexExisting, board)
|
||||
state.boards[indexExisting] = board
|
||||
} else {
|
||||
state.boards.push(board)
|
||||
}
|
||||
@@ -201,7 +199,7 @@ export default new Vuex.Store({
|
||||
})
|
||||
|
||||
if (indexExisting > -1) {
|
||||
Vue.set(state.boards, indexExisting, board)
|
||||
state.boards[indexExisting] = board
|
||||
} else {
|
||||
state.boards.push(board)
|
||||
}
|
||||
@@ -234,7 +232,7 @@ export default new Vuex.Store({
|
||||
state.boards = boards
|
||||
},
|
||||
setSharees(state, shareesUsersAndGroups) {
|
||||
Vue.set(state, 'sharees', shareesUsersAndGroups.exact.users)
|
||||
state.sharees = shareesUsersAndGroups.exact.users
|
||||
state.sharees.push(...shareesUsersAndGroups.exact.groups)
|
||||
state.sharees.push(...shareesUsersAndGroups.exact.circles)
|
||||
|
||||
@@ -284,7 +282,7 @@ export default new Vuex.Store({
|
||||
updateAclFromCurrentBoard(state, acl) {
|
||||
for (const acl_ in state.currentBoard.acl) {
|
||||
if (state.currentBoard.acl[acl_].participant.uid === acl.participant.uid) {
|
||||
Vue.set(state.currentBoard.acl, acl_, acl)
|
||||
state.currentBoard.acl[acl_] = acl
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -300,7 +298,7 @@ export default new Vuex.Store({
|
||||
}
|
||||
|
||||
if (removeIndex > -1) {
|
||||
Vue.delete(state.currentBoard.acl, removeIndex)
|
||||
state.currentBoard.acl.splice(removeIndex, 1)
|
||||
}
|
||||
},
|
||||
TOGGLE_SHORTCUT_LOCK(state, lock) {
|
||||
@@ -402,6 +400,14 @@ export default new Vuex.Store({
|
||||
return err
|
||||
}
|
||||
},
|
||||
async importBoard({ commit }, file) {
|
||||
try {
|
||||
const board = await apiClient.importBoard(file)
|
||||
commit('addBoard', board)
|
||||
} catch (err) {
|
||||
return err
|
||||
}
|
||||
},
|
||||
async cloneBoard({ commit }, { boardData, settings }) {
|
||||
const { withCards, withAssignments, withLabels, withDueDate, moveCardsToLeftStack, restoreArchivedCards } = settings
|
||||
|
||||
@@ -523,3 +529,5 @@ export default new Vuex.Store({
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default store
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import { OverviewApi } from '../services/OverviewApi.js'
|
||||
Vue.use(Vuex)
|
||||
|
||||
const apiClient = new OverviewApi()
|
||||
export default {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import Vue from 'vue'
|
||||
import { StackApi } from './../services/StackApi.js'
|
||||
import applyOrderToArray from './../helpers/applyOrderToArray.js'
|
||||
|
||||
@@ -26,7 +25,7 @@ export default {
|
||||
const existingIndex = state.stacks.findIndex(_stack => _stack.id === stack.id)
|
||||
if (existingIndex !== -1) {
|
||||
const existingStack = state.stacks.find(_stack => _stack.id === stack.id)
|
||||
Vue.set(state.stacks, existingIndex, Object.assign({}, existingStack, stack))
|
||||
state.stacks[existingIndex] = { ...existingStack, ...stack }
|
||||
} else {
|
||||
state.stacks.push(stack)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import DeckIcon from '../components/icons/DeckIcon.vue'
|
||||
import { BoardApi } from './../services/BoardApi.js'
|
||||
import store from './../store/main.js'
|
||||
|
||||
import NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'
|
||||
import { NcUserBubble } from '@nextcloud/vue'
|
||||
|
||||
import moment from '@nextcloud/moment'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
{{ t('deck', 'Cancel') }}
|
||||
</NcButton>
|
||||
<NcButton :disabled="loading || !isBoardAndStackChoosen"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
@click="createCard">
|
||||
{{ t('deck', 'Create card') }}
|
||||
</NcButton>
|
||||
@@ -212,8 +212,8 @@ export default {
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
this.$set(this.card, 'title', this.title)
|
||||
this.$set(this.card, 'description', this.description)
|
||||
this.card.title = this.title
|
||||
this.card.description = this.description
|
||||
this.fetchBoards()
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -1 +1 @@
|
||||
72460
|
||||
81091
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~9",
|
||||
"behat/behat": "~3.15.0",
|
||||
"behat/gherkin": "4.11.0",
|
||||
"behat/behat": "~3.21.1",
|
||||
"guzzlehttp/guzzle": "7.9.2",
|
||||
"jarnaiz/behat-junit-formatter": "^1.3",
|
||||
"sabre/dav": "4.7.0",
|
||||
|
||||
@@ -29,6 +29,7 @@ use OCA\Deck\Controller\PageController;
|
||||
use OCA\Deck\NoPermissionException;
|
||||
use OCA\Deck\NotFoundException;
|
||||
use OCA\Deck\Service\BoardService;
|
||||
use OCA\Deck\Service\Importer\BoardImportService;
|
||||
use OCA\Deck\Service\PermissionService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
@@ -86,7 +87,15 @@ class ExceptionMiddlewareTest extends \Test\TestCase {
|
||||
public function testAfterExceptionFail() {
|
||||
$this->request->expects($this->any())->method('getId')->willReturn('abc123');
|
||||
// BoardService $boardService, PermissionService $permissionService, $userId
|
||||
$boardController = new BoardController('deck', $this->createMock(IRequest::class), $this->createMock(BoardService::class), $this->createMock(PermissionService::class), 'admin');
|
||||
$boardController = new BoardController(
|
||||
'deck',
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(BoardService::class),
|
||||
$this->createMock(PermissionService::class),
|
||||
$this->createMock(BoardImportService::class),
|
||||
$this->createMock(\OCP\IL10N::class),
|
||||
'admin'
|
||||
);
|
||||
$result = $this->exceptionMiddleware->afterException($boardController, 'bar', new \Exception('other exception message'));
|
||||
$this->assertEquals('Internal server error: Please contact the server administrator if this error reappears multiple times, please include the request ID "abc123" below in your report.', $result->getData()['message']);
|
||||
$this->assertEquals(500, $result->getData()['status']);
|
||||
|
||||
@@ -475,6 +475,9 @@ class CardServiceTest extends TestCase {
|
||||
$label->setBoardId(1);
|
||||
$this->cardMapper->expects($this->once())->method('find')->willReturn($card);
|
||||
$this->cardMapper->expects($this->once())->method('removeLabel');
|
||||
$this->cardMapper->expects($this->once())
|
||||
->method('findBoardId')
|
||||
->willReturn(1);
|
||||
$this->labelMapper->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn($label);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user