basic functions

Signed-off-by: Bernhard Kapelari <bernhard@kapelari.com>

First feedback

Signed-off-by: Bernhard Kapelari <bernhard@kapelari.com>

lint:fix

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
This commit is contained in:
Bernhard Kapelari
2020-12-16 18:37:20 +01:00
committed by Julius Härtl
parent 8aa4fdcec5
commit 7b57c92f12
9 changed files with 536 additions and 20 deletions

View File

@@ -325,4 +325,4 @@
"Limit deck usage of groups" : "Nutzung auf Gruppen einschränken",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Wenn Sie Deck einschränken, können Benutzer, die nicht zu diesen Gruppen gehören, keine eigenen Boards erstellen. Die Benutzer können weiterhin an Boards arbeiten, die für sie freigegeben wurden."
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
}

View File

@@ -30,7 +30,9 @@ use OCA\Deck\Activity\CommentEventHandler;
use OCA\Deck\Capabilities;
use OCA\Deck\Collaboration\Resources\ResourceProvider;
use OCA\Deck\Collaboration\Resources\ResourceProviderCard;
use OCA\Deck\Dashboard\DeckWidget;
use OCA\Deck\Dashboard\DeckWidgetUpcoming;
use OCA\Deck\Dashboard\DeckWidgetToday;
use OCA\Deck\Dashboard\DeckWidgetTomorrow;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Event\AclCreatedEvent;
@@ -135,7 +137,9 @@ class Application extends App implements IBootstrap {
$context->registerSearchProvider(DeckProvider::class);
$context->registerSearchProvider(CardCommentProvider::class);
$context->registerDashboardWidget(DeckWidget::class);
$context->registerDashboardWidget(DeckWidgetUpcoming::class);
$context->registerDashboardWidget(DeckWidgetToday::class);
$context->registerDashboardWidget(DeckWidgetTomorrow::class);
$context->registerReferenceProvider(CreateCardReferenceProvider::class);

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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\Deck\Dashboard;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
class DeckWidgetToday implements IWidget {
/**
*
* @var IL10N
*/
private $l10n;
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}
/**
* @inheritDoc
*/
public function getId(): string {
return 'deckToday';
}
/**
* @inheritDoc
*/
public function getTitle(): string {
return $this->l10n->t('Upcoming cards today');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 20;
}
/**
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-deck';
}
/**
* @inheritDoc
*/
public function getUrl(): ?string {
return null;
}
/**
* @inheritDoc
*/
public function load(): void {
\OCP\Util::addScript('deck', 'dashboard');
}
}

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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\Deck\Dashboard;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
class DeckWidgetTomorrow implements IWidget {
/**
*
* @var IL10N
*/
private $l10n;
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}
/**
* @inheritDoc
*/
public function getId(): string {
return 'deckTomorrow';
}
/**
* @inheritDoc
*/
public function getTitle(): string {
return $this->l10n->t('Upcoming cards tomorrow');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 20;
}
/**
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-deck';
}
/**
* @inheritDoc
*/
public function getUrl(): ?string {
return null;
}
/**
* @inheritDoc
*/
public function load(): void {
\OCP\Util::addScript('deck', 'dashboard');
}
}

View File

@@ -40,7 +40,7 @@ use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Util;
class DeckWidget implements IAPIWidget, IButtonWidget, IIconWidget {
class DeckWidgetUpcoming implements IAPIWidget, IButtonWidget, IIconWidget {
private IL10N $l10n;
private OverviewService $dashboardService;
private IURLGenerator $urlGenerator;

View File

@@ -26,25 +26,64 @@ import './shared-init.js'
const debug = process.env.NODE_ENV !== 'production'
let _imports = null
const getAsyncImports = async () => {
if (_imports) {
return _imports
}
const { default: Vue } = await import('vue')
const { default: Vuex } = await import('vuex')
const { default: dashboard } = await import('./store/dashboard.js')
Vue.prototype.t = t
Vue.prototype.n = n
Vue.prototype.OC = OC
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
dashboard,
},
strict: debug,
})
_imports = {
store, Vue,
}
return _imports
}
document.addEventListener('DOMContentLoaded', () => {
OCA.Dashboard.register('deck', async (el) => {
const { default: Vue } = await import('vue')
const { default: Vuex } = await import('vuex')
const { default: dashboard } = await import('./store/dashboard.js')
const { Vue, store } = await getAsyncImports()
const { default: DashboardUpcoming } = await import('./views/DashboardUpcoming.vue')
const { default: Dashboard } = await import('./views/Dashboard.vue')
Vue.prototype.t = t
Vue.prototype.n = n
Vue.prototype.OC = OC
Vue.use(Vuex)
const View = Vue.extend(DashboardUpcoming)
const vm = new View({
propsData: {},
store,
}).$mount(el)
return vm
})
const store = new Vuex.Store({
modules: {
dashboard,
},
strict: debug,
})
const View = Vue.extend(Dashboard)
OCA.Dashboard.register('deckToday', async (el) => {
const { Vue, store } = await getAsyncImports()
const { default: DashboardToday } = await import('./views/DashboardToday.vue')
const View = Vue.extend(DashboardToday)
const vm = new View({
propsData: {},
store,
}).$mount(el)
return vm
})
OCA.Dashboard.register('deckTomorrow', async (el) => {
const { Vue, store } = await getAsyncImports()
const { default: DashboardTomorrow } = await import('./views/DashboardTomorrow.vue')
const View = Vue.extend(DashboardTomorrow)
const vm = new View({
propsData: {},
store,

View File

@@ -0,0 +1,151 @@
<!--
- @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
-
- @author Julius Härtl <jus@bitgrid.net>
-
- @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/>.
-
-->
<template>
<DashboardWidget :items="cards"
empty-content-icon="icon-deck"
:empty-content-message="t('deck', 'No upcoming cards')"
:show-more-text="t('deck', 'upcoming cards today')"
:show-more-url="showMoreUrl"
:loading="loading"
@hide="() => {}"
@markDone="() => {}">
<template #default="{ item }">
<a :key="item.id"
:href="cardLink(item)"
target="_blank"
class="card">
<div class="card--header">
<DueDate class="right" :card="item" />
<span class="title">{{ item.title }}</span>
</div>
<ul v-if="item.labels && item.labels.length"
class="labels">
<li v-for="label in item.labels" :key="label.id" :style="labelStyle(label)">
<span>{{ label.title }}</span>
</li>
</ul>
</a>
</template>
</DashboardWidget>
</template>
<script>
import { DashboardWidget } from '@nextcloud/vue-dashboard'
import { mapGetters } from 'vuex'
import labelStyle from './../mixins/labelStyle.js'
import DueDate from '../components/cards/badges/DueDate.vue'
import { generateUrl } from '@nextcloud/router'
export default {
name: 'DashboardToday',
components: {
DueDate,
DashboardWidget,
},
mixins: [labelStyle],
data() {
return {
loading: false,
}
},
computed: {
...mapGetters([
'assignedCardsDashboard',
]),
cards() {
const today = new Date()
const list = [
...this.assignedCardsDashboard,
].filter((card) => {
return card.duedate !== null
}).filter((card) => {
return (new Date(card.duedate)).getDate() === (new Date(today)).getDate()
})
list.sort((a, b) => {
return (new Date(a.duedate)).getTime() - (new Date(b.duedate)).getTime()
})
return list
},
cardLink() {
return (card) => {
return generateUrl('/apps/deck') + `#/board/${card.boardId}/card/${card.id}`
}
},
showMoreUrl() {
return this.cards.length > 7 ? generateUrl('/apps/deck') : null
},
},
beforeMount() {
this.loading = true
this.$store.dispatch('loadUpcoming').then(() => {
this.loading = false
})
},
}
</script>
<style lang="scss" scoped>
@import './../css/labels';
#deck-widget-empty-content {
text-align: center;
margin-top: 5vh;
}
.card {
display: block;
border-radius: var(--border-radius-large);
padding: 8px;
height: 60px;
&:hover {
background-color: var(--color-background-hover);
}
}
.card--header {
overflow: hidden;
.title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
}
.labels {
margin-left: 0;
}
.duedate::v-deep {
.due {
margin: 0 0 0 10px;
padding: 2px 4px;
font-size: 90%;
}
}
.right {
float: right;
}
</style>

View File

@@ -0,0 +1,152 @@
<!--
- @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
-
- @author Julius Härtl <jus@bitgrid.net>
-
- @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/>.
-
-->
<template>
<DashboardWidget :items="cards"
empty-content-icon="icon-deck"
:empty-content-message="t('deck', 'No upcoming cards')"
:show-more-text="t('deck', 'upcoming cards tomorrow')"
:show-more-url="showMoreUrl"
:loading="loading"
@hide="() => {}"
@markDone="() => {}">
<template #default="{ item }">
<a :key="item.id"
:href="cardLink(item)"
target="_blank"
class="card">
<div class="card--header">
<DueDate class="right" :card="item" />
<span class="title">{{ item.title }}</span>
</div>
<ul v-if="item.labels && item.labels.length"
class="labels">
<li v-for="label in item.labels" :key="label.id" :style="labelStyle(label)">
<span>{{ label.title }}</span>
</li>
</ul>
</a>
</template>
</DashboardWidget>
</template>
<script>
import { DashboardWidget } from '@nextcloud/vue-dashboard'
import { mapGetters } from 'vuex'
import labelStyle from './../mixins/labelStyle.js'
import DueDate from '../components/cards/badges/DueDate.vue'
import { generateUrl } from '@nextcloud/router'
export default {
name: 'DashboardTomorrow',
components: {
DueDate,
DashboardWidget,
},
mixins: [labelStyle],
data() {
return {
loading: false,
}
},
computed: {
...mapGetters([
'assignedCardsDashboard',
]),
cards() {
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
const list = [
...this.assignedCardsDashboard,
].filter((card) => {
return card.duedate !== null
}).filter((card) => {
return (new Date(card.duedate)).getDate() === (new Date(tomorrow)).getDate()
})
list.sort((a, b) => {
return (new Date(a.duedate)).getTime() - (new Date(b.duedate)).getTime()
})
return list
},
cardLink() {
return (card) => {
return generateUrl('/apps/deck') + `#/board/${card.boardId}/card/${card.id}`
}
},
showMoreUrl() {
return this.cards.length > 7 ? generateUrl('/apps/deck') : null
},
},
beforeMount() {
this.loading = true
this.$store.dispatch('loadUpcoming').then(() => {
this.loading = false
})
},
}
</script>
<style lang="scss" scoped>
@import './../css/labels';
#deck-widget-empty-content {
text-align: center;
margin-top: 5vh;
}
.card {
display: block;
border-radius: var(--border-radius-large);
padding: 8px;
height: 60px;
&:hover {
background-color: var(--color-background-hover);
}
}
.card--header {
overflow: hidden;
.title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
}
.labels {
margin-left: 0;
}
.duedate::v-deep {
.due {
margin: 0 0 0 10px;
padding: 2px 4px;
font-size: 90%;
}
}
.right {
float: right;
}
</style>

View File

@@ -71,7 +71,7 @@ import { generateUrl } from '@nextcloud/router'
import CreateNewCardCustomPicker from './CreateNewCardCustomPicker.vue'
export default {
name: 'Dashboard',
name: 'DashboardUpcoming',
components: {
CreateNewCardCustomPicker,
NcModal,