Compare commits

...

1 Commits

Author SHA1 Message Date
Julius Härtl
d7b64ea17d Implement done state for due dates
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-11-05 16:24:14 +01:00
7 changed files with 114 additions and 21 deletions

View File

@@ -94,8 +94,8 @@ class CardController extends Controller {
* @param $deletedAt * @param $deletedAt
* @return \OCP\AppFramework\Db\Entity * @return \OCP\AppFramework\Db\Entity
*/ */
public function update($id, $title, $stackId, $type, $order, $description, $duedate, $deletedAt) { public function update($id, $title, $stackId, $type, $order, $description, $duedate, $deletedAt, $dueDone) {
return $this->cardService->update($id, $title, $stackId, $type, $order, $description, $this->userId, $duedate, $deletedAt); return $this->cardService->update($id, $title, $stackId, $type, $order, $description, $this->userId, $duedate, $deletedAt, null, $dueDone);
} }
/** /**

View File

@@ -36,17 +36,19 @@ class Card extends RelationalEntity {
protected $lastModified; protected $lastModified;
protected $lastEditor; protected $lastEditor;
protected $createdAt; protected $createdAt;
protected $labels;
protected $assignedUsers;
protected $attachments;
protected $attachmentCount;
protected $owner; protected $owner;
protected $order; protected $order;
protected $archived = false; protected $archived = false;
protected $duedate; protected $duedate;
protected $notified = false; protected $notified = false;
protected $deletedAt = 0; protected $deletedAt = 0;
protected $dueDone = false;
protected $labels;
protected $assignedUsers;
protected $commentsUnread = 0; protected $commentsUnread = 0;
protected $attachments;
protected $attachmentCount = 0;
private $databaseType = 'sqlite'; private $databaseType = 'sqlite';
@@ -64,6 +66,8 @@ class Card extends RelationalEntity {
$this->addType('archived', 'boolean'); $this->addType('archived', 'boolean');
$this->addType('notified', 'boolean'); $this->addType('notified', 'boolean');
$this->addType('deletedAt', 'integer'); $this->addType('deletedAt', 'integer');
$this->addType('dueDone', 'boolean');
$this->addRelation('labels'); $this->addRelation('labels');
$this->addRelation('assignedUsers'); $this->addRelation('assignedUsers');
$this->addRelation('attachments'); $this->addRelation('attachments');

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace OCA\Deck\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version10200Date20201104190344 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('deck_cards');
if (!$table->hasColumn('due_done')) {
$table->addColumn('due_done', 'boolean', [
'notnull' => false,
'default' => false
]);
}
return $schema;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
}

View File

@@ -261,7 +261,7 @@ class CardService {
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException * @throws BadRequestException
*/ */
public function update($id, $title, $stackId, $type, $order = 0, $description = '', $owner, $duedate = null, $deletedAt = null, $archived = null) { public function update($id, $title, $stackId, $type, $order = 0, $description = '', $owner, $duedate = null, ?int $deletedAt = null, ?bool $archived = null, ?bool $dueDone = null) {
if (is_numeric($id) === false) { if (is_numeric($id) === false) {
throw new BadRequestException('card id must be a number'); throw new BadRequestException('card id must be a number');
} }
@@ -320,6 +320,9 @@ class CardService {
if ($archived !== null) { if ($archived !== null) {
$card->setArchived($archived); $card->setArchived($archived);
} }
if ($dueDone !== null) {
$card->setDueDone($dueDone);
}
// Trigger update events before setting description as it is handled separately // Trigger update events before setting description as it is handled separately
@@ -590,7 +593,7 @@ class CardService {
*/ */
public function findAllWithDue($userId) { public function findAllWithDue($userId) {
$cards = $this->cardMapper->findAllWithDue($userId); $cards = $this->cardMapper->findAllWithDue($userId);
return $cards; return $cards;
} }
@@ -602,7 +605,7 @@ class CardService {
*/ */
public function findAssignedCards($userId) { public function findAssignedCards($userId) {
$cards = $this->cardMapper->findAssignedCards($userId); $cards = $this->cardMapper->findAssignedCards($userId);
return $cards; return $cards;
} }
} }

View File

@@ -24,22 +24,43 @@
<div v-if="card"> <div v-if="card">
<div @click.stop.prevent> <div @click.stop.prevent>
<Actions v-if="canEdit && !isArchived"> <Actions v-if="canEdit && !isArchived">
<ActionButton v-if="showArchived === false && !isCurrentUserAssigned" icon="icon-user" @click="assignCardToMe()"> <ActionButton v-if="showArchived === false && !isCurrentUserAssigned"
icon="icon-user"
:close-after-click="true"
@click="assignCardToMe()">
{{ t('deck', 'Assign to me') }} {{ t('deck', 'Assign to me') }}
</ActionButton> </ActionButton>
<ActionButton v-if="showArchived === false && isCurrentUserAssigned" icon="icon-user" @click="unassignCardFromMe()"> <ActionButton v-if="showArchived === false && isCurrentUserAssigned"
icon="icon-user"
:close-after-click="true"
@click="unassignCardFromMe()">
{{ t('deck', 'Unassign myself') }} {{ t('deck', 'Unassign myself') }}
</ActionButton> </ActionButton>
<ActionButton icon="icon-external" @click.stop="modalShow=true"> <ActionButton v-if="showArchived === false && card.duedate && !card.dueDone"
icon="icon-checkmark"
:close-after-click="true"
@click="toggleDoneState(true)">
{{ t('deck', 'Mark card as done') }}
</ActionButton>
<ActionButton v-if="showArchived === false && card.duedate && card.dueDone"
icon="icon-checkmark"
:close-after-click="true"
@click="toggleDoneState(false)">
{{ t('deck', 'Mark card as pending') }}
</ActionButton>
<ActionButton icon="icon-external" :close-after-click="true" @click.stop="modalShow=true">
{{ t('deck', 'Move card') }} {{ t('deck', 'Move card') }}
</ActionButton> </ActionButton>
<ActionButton icon="icon-settings-dark" @click="openCard"> <ActionButton icon="icon-settings-dark" :close-after-click="true" @click="openCard">
{{ t('deck', 'Card details') }} {{ t('deck', 'Card details') }}
</ActionButton> </ActionButton>
<ActionButton icon="icon-archive" @click="archiveUnarchiveCard()"> <ActionButton icon="icon-archive" :close-after-click="true" @click="archiveUnarchiveCard()">
{{ showArchived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }} {{ showArchived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
</ActionButton> </ActionButton>
<ActionButton v-if="showArchived === false" icon="icon-delete" @click="deleteCard()"> <ActionButton v-if="showArchived === false"
icon="icon-delete"
:close-after-click="true"
@click="deleteCard()">
{{ t('deck', 'Delete card') }} {{ t('deck', 'Delete card') }}
</ActionButton> </ActionButton>
</Actions> </Actions>
@@ -154,6 +175,9 @@ export default {
}, },
}) })
}, },
toggleDoneState(state) {
this.$store.dispatch('updateCardDueDone', { ...this.card, dueDone: state })
},
moveCard() { moveCard() {
this.copiedCard = Object.assign({}, this.card) this.copiedCard = Object.assign({}, this.card)
this.copiedCard.stackId = this.selectedStack.id this.copiedCard.stackId = this.selectedStack.id

View File

@@ -23,8 +23,8 @@
<template> <template>
<div v-if="card" class="duedate"> <div v-if="card" class="duedate">
<transition name="zoom"> <transition name="zoom">
<div v-if="card.duedate" :class="dueIcon"> <div v-if="card.duedate" v-tooltip="card.dueDone ? relativeDate : null" :class="dueIcon">
<span>{{ relativeDate }}</span> <span v-if="!card.dueDone">{{ relativeDate }}</span>
</div> </div>
</transition> </transition>
</div> </div>
@@ -45,15 +45,15 @@ export default {
dueIcon() { dueIcon() {
const days = Math.floor(moment(this.card.duedate).diff(this.$root.time, 'seconds') / 60 / 60 / 24) const days = Math.floor(moment(this.card.duedate).diff(this.$root.time, 'seconds') / 60 / 60 / 24)
if (days < 0) { if (days < 0) {
return 'icon-calendar due icon overdue' return `due icon ${!this.card.dueDone ? 'icon-calendar overdue' : 'icon-checkmark'}`
} }
if (days === 0) { if (days === 0) {
return 'icon-calendar-dark due icon now' return `due icon ${!this.card.dueDone ? 'icon-calendar-dark now' : 'icon-checkmark'}`
} }
if (days === 1) { if (days === 1) {
return 'icon-calendar-dark due icon next' return `due icon ${!this.card.dueDone ? 'con-calendar-dark next' : 'icon-checkmark'}`
} }
return 'icon-calendar-dark due icon' return `due icon ${!this.card.dueDone ? 'icon-calendar-dark' : 'icon-checkmark'}`
}, },
relativeDate() { relativeDate() {
const diff = moment(this.$root.time).diff(this.card.duedate, 'seconds') const diff = moment(this.$root.time).diff(this.card.duedate, 'seconds')
@@ -88,6 +88,10 @@ export default {
background-size: contain; background-size: contain;
} }
&.icon-checkmark {
width: 20px;
}
&.overdue { &.overdue {
background-color: var(--color-error); background-color: var(--color-error);
color: var(--color-primary-text); color: var(--color-primary-text);

View File

@@ -236,5 +236,9 @@ export default {
const updatedCard = await apiClient.updateCard(card) const updatedCard = await apiClient.updateCard(card)
commit('updateCardProperty', { property: 'duedate', card: updatedCard }) commit('updateCardProperty', { property: 'duedate', card: updatedCard })
}, },
async updateCardDueDone({ commit }, card) {
const updatedCard = await apiClient.updateCard(card)
commit('updateCardProperty', { property: 'dueDone', card: updatedCard })
},
}, },
} }