Implement dashboard

Signed-off-by: Jakob Röhrl <jakob.roehrl@web.de>
This commit is contained in:
Jakob Röhrl 2021-06-14 13:28:56 +02:00 committed by Raimund Schlüßler
parent 1e938f3831
commit 1c4ea0e28b
No known key found for this signature in database
GPG Key ID: 036FA7EB1A599178
10 changed files with 592 additions and 8 deletions

View File

@ -91,6 +91,9 @@ clean:
rm -f ./js/tasks-main.js
rm -f ./js/tasks-main.js.map
rm -f ./js/tasks-main.js.LICENSE.txt
rm -f ./js/tasks-dashboard.js
rm -f ./js/tasks-dashboard.js.map
rm -f ./js/tasks-dashboard.js.LICENSE.txt
rm -rf $(build_directory)
# Same as clean but also removes dependencies installed by npm

4
img/tasks-dark.svg Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="32px" height="32px" enable-background="new 0 0 32 32" version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<path d="m14.383 26.764s0.259-0.716 0.563-1.367c4.908-10.57 11.212-17.53 14.008-20.69 2.264-2.559 1.378-2.156 0.375-1.503-4.09 2.659-11.742 11.206-14.668 14.169-0.542 0.545-1.769 1.833-2.045 1.833-0.313 0-1.358-0.481-1.955-0.833-2.282-1.338-4.464-2.643-5.834-3.136-2.917-1.049-2.092-0.052-1.926 0.281 0.521 1.042 7.566 6.689 9.899 9.356 0.722 0.824 1.583 1.89 1.583 1.89z" fill="#000" stroke="#000" stroke-miterlimit="10" stroke-width=".75"/>
</svg>

After

Width:  |  Height:  |  Size: 662 B

View File

@ -22,14 +22,28 @@
namespace OCA\Tasks\AppInfo;
use OCA\Tasks\Dashboard\TasksWidget;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
class Application extends App {
class Application extends App implements IBootstrap {
/** @var string */
public const APP_ID = 'tasks';
/**
* @param array $params
*/
public function __construct(array $params=[]) {
parent::__construct('tasks', $params);
parent::__construct(self::APP_ID, $params);
}
public function register(IRegistrationContext $context): void {
$context->registerDashboardWidget(TasksWidget::class);
}
public function boot(IBootContext $context): void {
}
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Jakob Röhrl <jakob.roehrl@web.de>
*
* @author Jakob Röhrl <jakob.roehrl@web.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Tasks\Dashboard;
use OCA\Tasks\AppInfo\Application;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
class TasksWidget implements IWidget {
/**
* @var IL10N
*/
private $l10n;
/**
* TasksWidget constructor.
* @param IL10N $l10n
*/
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}
/**
* @inheritDoc
*/
public function getId(): string {
return Application::APP_ID;
}
/**
* @inheritDoc
*/
public function getTitle(): string {
return $this->l10n->t('Upcoming tasks');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 20;
}
/**
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-tasks';
}
/**
* @inheritDoc
*/
public function getUrl(): ?string {
return null;
}
/**
* @inheritDoc
*/
public function load(): void {
\OCP\Util::addScript('tasks', 'tasks-dashboard');
}
}

234
package-lock.json generated
View File

@ -8,13 +8,16 @@
"version": "0.13.6",
"license": "AGPLv3",
"dependencies": {
"@nextcloud/auth": "^1.3.0",
"@nextcloud/axios": "^1.6.0",
"@nextcloud/dialogs": "^3.1.2",
"@nextcloud/event-bus": "^2.0.0",
"@nextcloud/initial-state": "1.2.0",
"@nextcloud/l10n": "^1.4.1",
"@nextcloud/moment": "^1.1.1",
"@nextcloud/router": "^2.0.0",
"@nextcloud/vue": "^4.0.2",
"@nextcloud/vue-dashboard": "^2.0.1",
"@vue/test-utils": "^1.2.1",
"cdav-library": "github:nextcloud/cdav-library",
"color-convert": "^2.0.1",
@ -2792,6 +2795,128 @@
"node": ">=10.0.0"
}
},
"node_modules/@nextcloud/vue-dashboard": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@nextcloud/vue-dashboard/-/vue-dashboard-2.0.1.tgz",
"integrity": "sha512-eLzdK8Ey5rrs3D6i2OAA5jkZ6lklrAbfnRgL40tZLIJ+MEKvRuPOjwrzhJKxHgVp3rU1rEgkaaPvSNXRVGS1mQ==",
"dependencies": {
"@nextcloud/vue": "^3.1.1",
"core-js": "^3.6.4",
"vue": "^2.6.11"
},
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"@nextcloud/vue": "^3.1.1",
"vue": "^2.6.11"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/@nextcloud/event-bus": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.3.0.tgz",
"integrity": "sha512-+U5MnCvfnNWvf0lvdqJg8F+Nm8wN+s9ayuBjtiEQxTAcootv7lOnlMgfreqF3l2T0Wet2uZh4JbFVUWf8l3w7g==",
"dependencies": {
"@types/semver": "^7.3.5",
"core-js": "^3.11.2",
"semver": "^7.3.5"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/@nextcloud/router": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-1.2.0.tgz",
"integrity": "sha512-kn9QsL9LuhkIMaSSgdiqRL3SZ6PatuAjXUiyq343BbSnI99Oc5eJH8kU6cT2AHije7wKy/tK8Xe3VQuVO32SZQ==",
"dependencies": {
"core-js": "^3.6.4"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/@nextcloud/vue": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-3.10.1.tgz",
"integrity": "sha512-DdnnEFxt5FuZOtAD1x7hSDFVQF9KVVgQtFKwzs2ySNbyIx8rfRfc6noC7JbMAPR1LyPegCst0bVwQIfqsASGog==",
"dependencies": {
"@nextcloud/auth": "^1.2.3",
"@nextcloud/axios": "^1.3.2",
"@nextcloud/browser-storage": "^0.1.1",
"@nextcloud/capabilities": "^1.0.2",
"@nextcloud/dialogs": "^3.0.0",
"@nextcloud/event-bus": "^1.1.4",
"@nextcloud/l10n": "^1.2.3",
"@nextcloud/router": "^1.0.2",
"core-js": "^3.6.5",
"debounce": "1.2.1",
"emoji-mart-vue-fast": "^7.0.7",
"escape-html": "^1.0.3",
"hammerjs": "^2.0.8",
"linkifyjs": "~2.1.9",
"md5": "^2.2.1",
"regenerator-runtime": "^0.13.5",
"string-length": "^4.0.1",
"striptags": "^3.1.1",
"style-loader": "^2.0.0",
"tributejs": "^5.1.3",
"v-click-outside": "^3.0.1",
"v-tooltip": "^2.0.3",
"vue": "^2.6.11",
"vue-color": "^2.7.1",
"vue-multiselect": "^2.1.6",
"vue-visible": "^1.0.2",
"vue2-datepicker": "^3.6.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/char-regex": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
"engines": {
"node": ">=10"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
"integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
"dependencies": {
"char-regex": "^1.0.2",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@nextcloud/vue-dashboard/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/@nextcloud/webpack-vue-config": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@nextcloud/webpack-vue-config/-/webpack-vue-config-4.0.3.tgz",
@ -3480,7 +3605,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -7502,6 +7626,7 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@ -14595,7 +14720,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.0"
},
@ -18850,6 +18974,105 @@
"vue2-datepicker": "^3.6.3"
}
},
"@nextcloud/vue-dashboard": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@nextcloud/vue-dashboard/-/vue-dashboard-2.0.1.tgz",
"integrity": "sha512-eLzdK8Ey5rrs3D6i2OAA5jkZ6lklrAbfnRgL40tZLIJ+MEKvRuPOjwrzhJKxHgVp3rU1rEgkaaPvSNXRVGS1mQ==",
"requires": {
"@nextcloud/vue": "^3.1.1",
"core-js": "^3.6.4",
"vue": "^2.6.11"
},
"dependencies": {
"@nextcloud/event-bus": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.3.0.tgz",
"integrity": "sha512-+U5MnCvfnNWvf0lvdqJg8F+Nm8wN+s9ayuBjtiEQxTAcootv7lOnlMgfreqF3l2T0Wet2uZh4JbFVUWf8l3w7g==",
"requires": {
"@types/semver": "^7.3.5",
"core-js": "^3.11.2",
"semver": "^7.3.5"
}
},
"@nextcloud/router": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-1.2.0.tgz",
"integrity": "sha512-kn9QsL9LuhkIMaSSgdiqRL3SZ6PatuAjXUiyq343BbSnI99Oc5eJH8kU6cT2AHije7wKy/tK8Xe3VQuVO32SZQ==",
"requires": {
"core-js": "^3.6.4"
}
},
"@nextcloud/vue": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-3.10.1.tgz",
"integrity": "sha512-DdnnEFxt5FuZOtAD1x7hSDFVQF9KVVgQtFKwzs2ySNbyIx8rfRfc6noC7JbMAPR1LyPegCst0bVwQIfqsASGog==",
"requires": {
"@nextcloud/auth": "^1.2.3",
"@nextcloud/axios": "^1.3.2",
"@nextcloud/browser-storage": "^0.1.1",
"@nextcloud/capabilities": "^1.0.2",
"@nextcloud/dialogs": "^3.0.0",
"@nextcloud/event-bus": "^1.1.4",
"@nextcloud/l10n": "^1.2.3",
"@nextcloud/router": "^1.0.2",
"core-js": "^3.6.5",
"debounce": "1.2.1",
"emoji-mart-vue-fast": "^7.0.7",
"escape-html": "^1.0.3",
"hammerjs": "^2.0.8",
"linkifyjs": "~2.1.9",
"md5": "^2.2.1",
"regenerator-runtime": "^0.13.5",
"string-length": "^4.0.1",
"striptags": "^3.1.1",
"style-loader": "^2.0.0",
"tributejs": "^5.1.3",
"v-click-outside": "^3.0.1",
"v-tooltip": "^2.0.3",
"vue": "^2.6.11",
"vue-color": "^2.7.1",
"vue-multiselect": "^2.1.6",
"vue-visible": "^1.0.2",
"vue2-datepicker": "^3.6.3"
}
},
"char-regex": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"requires": {
"lru-cache": "^6.0.0"
}
},
"string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
"integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
"requires": {
"char-regex": "^1.0.2",
"strip-ansi": "^6.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
"@nextcloud/webpack-vue-config": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@nextcloud/webpack-vue-config/-/webpack-vue-config-4.0.3.tgz",
@ -19451,8 +19674,7 @@
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "3.2.1",
@ -20357,7 +20579,7 @@
"cdav-library": {
"version": "git+ssh://git@github.com/nextcloud/cdav-library.git#a8e1b3fa505ee2e457b2318b16de3795cbca06c3",
"integrity": "sha512-BNN7nU2t60dyUCidZ97QjsmZNIvDaWHVET27OQP4X5Zbgq0aIENXlZMdJidEwNYQqQ1DWMtBr5tiOriNeI5ArA==",
"from": "cdav-library@github:nextcloud/cdav-library#a8e1b3fa505ee2e457b2318b16de3795cbca06c3",
"from": "cdav-library@github:nextcloud/cdav-library",
"requires": {
"core-js": "^3.14.0",
"regenerator-runtime": "^0.13.7"
@ -22634,6 +22856,7 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
@ -28044,7 +28267,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}

View File

@ -26,13 +26,16 @@
"bugs": "https://github.com/nextcloud/tasks/issues",
"contributors": [],
"dependencies": {
"@nextcloud/auth": "^1.3.0",
"@nextcloud/axios": "^1.6.0",
"@nextcloud/dialogs": "^3.1.2",
"@nextcloud/event-bus": "^2.0.0",
"@nextcloud/initial-state": "1.2.0",
"@nextcloud/l10n": "^1.4.1",
"@nextcloud/moment": "^1.1.1",
"@nextcloud/router": "^2.0.0",
"@nextcloud/vue": "^4.0.2",
"@nextcloud/vue-dashboard": "^2.0.1",
"@vue/test-utils": "^1.2.1",
"cdav-library": "github:nextcloud/cdav-library",
"color-convert": "^2.0.1",

7
src/css/dashboard.scss Normal file
View File

@ -0,0 +1,7 @@
.icon-tasks {
background-image: url(./../../img/tasks-dark.svg);
}
body.dark .icon-tasks {
background-image: url(./../../img/tasks.svg);
}

57
src/dashboard.js Normal file
View File

@ -0,0 +1,57 @@
/*
* @copyright Copyright (c) 2021 Jakob Röhrl <jakob.roehrl@web.de>
*
* @author Jakob Röhrl <jakob.roehrl@web.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import Dashboard from './views/Dashboard'
import store from './store/store.js'
import './css/dashboard.scss'
import { generateFilePath } from '@nextcloud/router'
import { getRequestToken } from '@nextcloud/auth'
import { translate, translatePlural } from '@nextcloud/l10n'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// eslint-disable-next-line
__webpack_nonce__ = btoa(getRequestToken())
// eslint-disable-next-line
__webpack_public_path__ = generateFilePath('tasks', '', 'js/')
Vue.prototype.t = translate
Vue.prototype.n = translatePlural
Vue.prototype.OC = OC
Vue.prototype.OCA = OCA
document.addEventListener('DOMContentLoaded', () => {
OCA.Dashboard.register('tasks', (el) => {
const View = Vue.extend(Dashboard)
const vm = new View({
propsData: {},
store,
}).$mount(el)
return vm
})
})

180
src/views/Dashboard.vue Normal file
View File

@ -0,0 +1,180 @@
<!--
Nextcloud - Tasks
@author Jakob Röhrl
@copyright 2021 Jakob Röhrl <jakob.roehrl@web.de>
@author Raimund Schlüßler
@copyright 2021 Raimund Schlüßler <raimund.schluessler@mailbox.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
License as published by the Free Software Foundation; either
version 3 of the License, or any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
You should have received a copy of the GNU Affero General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<DashboardWidget
id="tasks_panel"
:items="tasks"
empty-content-icon="icon-tasks"
:empty-content-message="t('tasks', 'No upcoming tasks')"
:show-more-text="t('tasks', 'upcoming tasks')"
:loading="loading"
:show-items-and-empty-content="!hasTaskToday"
:half-empty-content-message="t('tasks', 'No tasks today')">
<template #default="{ item }">
<DashboardWidgetItem
:main-text="item.summary"
:sub-text="formatSubtext(item)"
:target-url="getTasksAppUrl(item)">
<template #avatar>
<div
class="calendar-dot"
:style="{'background-color': item.calendar.color}"
:title="item.calendar.displayName" />
</template>
</DashboardWidgetItem>
</template>
</DashboardWidget>
</template>
<script>
import { sort, isTaskInList } from '../store/storeHelper.js'
import client from '../services/cdav.js'
import { generateUrl } from '@nextcloud/router'
import { DashboardWidget, DashboardWidgetItem } from '@nextcloud/vue-dashboard'
import { translate } from '@nextcloud/l10n'
import { mapState } from 'vuex'
export default {
name: 'Dashboard',
components: {
DashboardWidget,
DashboardWidgetItem,
},
data() {
return {
loading: true,
tasks: [],
}
},
computed: {
...mapState({
calendars: state => state.calendars.calendars,
}),
hasTaskToday() {
return this.tasks.some(task => isTaskInList(task, 'today'))
},
},
mounted() {
this.initializeEnvironment()
},
methods: {
async initializeEnvironment() {
await client.connect({ enableCalDAV: true })
await this.$store.dispatch('fetchCurrentUserPrincipal')
const calendars = await this.$store.dispatch('getCalendars')
const owners = []
calendars.forEach((calendar) => {
if (owners.indexOf(calendar.owner) === -1) {
owners.push(calendar.owner)
}
})
owners.forEach((owner) => {
this.$store.dispatch('fetchPrincipalByUrl', {
url: owner,
})
})
// No calendars? Create a new one!
if (calendars.length === 0) {
let color = '#0082C9'
if (this.$OCA.Theming) {
color = this.$OCA.Theming.color
}
await this.$store.dispatch('appendCalendar', { displayName: translate('tasks', 'Tasks'), color })
this.fetchTasks()
// else, let's get those tasks!
} else {
this.fetchTasks()
}
},
fetchTasks() {
Promise.all(this.calendars.map(calendar =>
this.$store.dispatch('getTasksFromCalendar', { calendar, completed: false, related: null })
)).then(results => {
this.tasks = sort([...results.flat().filter(task => !task.closed)])
this.loading = false
})
},
/**
* @param {Object} task The task to format
* @returns {String}
*/
formatSubtext(task) {
if (!task.dueMoment.isValid()) {
return translate('tasks', 'No due date assigned')
}
if (task.allDay) {
return task.dueMoment.calendar(null, {
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
lastDay: translate('tasks', '[Due yesterday]'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
sameDay: translate('tasks', '[Due today]'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
nextDay: translate('tasks', '[Due tomorrow]'),
lastWeek: translate('tasks', '[Due] L'),
nextWeek: translate('tasks', '[Due] L'),
sameElse: translate('tasks', '[Due] L'),
})
} else {
return task.dueMoment.calendar(null, {
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
lastDay: translate('tasks', '[Due yesterday at] LT'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
sameDay: translate('tasks', '[Due today at] LT'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
nextDay: translate('tasks', '[Due tomorrow at] LT'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
lastWeek: translate('tasks', '[Due] L [at] LT'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
nextWeek: translate('tasks', '[Due] L [at] LT'),
// TRANSLATORS This is a string for moment.js. The square brackets escape the string from moment.js. Please translate the string and keep the brackets.
sameElse: translate('tasks', '[Due] L [at] LT'),
})
}
},
/**
* @param {Object} task The task to generate the URL for
* @returns {string}
*/
getTasksAppUrl(task) {
return generateUrl('apps/tasks') + `/#/calendars/${task.calendar.id}/tasks/${task.uri}`
},
},
}
</script>
<style lang="scss">
#tasks_panel {
.calendar-dot {
height: 1rem;
width: 1rem;
margin-top: .2rem;
border-radius: 50%;
min-width: 1rem;
min-height: 1rem;
}
}
</style>

View File

@ -1,3 +1,9 @@
const webpackConfig = require('@nextcloud/webpack-vue-config')
const path = require('path')
webpackConfig.entry = {
...webpackConfig.entry,
dashboard: path.join(__dirname, 'src', 'dashboard.js'),
}
module.exports = webpackConfig