feat: add a new CardModal component

Signed-off-by: Luka Trovic <luka@nextcloud.com>
This commit is contained in:
Luka Trovic
2022-01-25 15:54:36 +01:00
parent c73be045c3
commit 49dccb6199
5 changed files with 297 additions and 3 deletions

3
img/flash-black.svg Normal file
View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"><g><g><polygon points="426.667,213.333 288.36,213.333 333.706,0 148.817,0 85.333,298.667 227.556,298.667 227.556,512 " /></g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></svg>

After

Width:  |  Height:  |  Size: 594 B

1
img/plus.svg Normal file
View File

@@ -0,0 +1 @@
<svg fill="#26e07f" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px"><path fill-rule="evenodd" d="M 11 2 L 11 11 L 2 11 L 2 13 L 11 13 L 11 22 L 13 22 L 13 13 L 22 13 L 22 11 L 13 11 L 13 2 Z"/></svg>

After

Width:  |  Height:  |  Size: 235 B

View File

@@ -160,7 +160,7 @@ export default {
width: 50vw;
max-width: 800px;
min-height: 200px;
height: 80vh;
height: auto;
}
</style>
@@ -170,4 +170,7 @@ export default {
width: 100%;
}
.modal-wrapper--normal .modal-container{
width: 800px !important;
}
</style>

View File

@@ -0,0 +1,287 @@
<!--
- @copyright Copyright (c) 2018 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>
<div class="container">
<div class="top">
<h1 class="top-title">
Example task 3
</h1>
<p class="top-modified">
Modified: 2 days go. Created 3 days ago
</p>
</div>
<div class="tabs">
<div class="tab members">
<i class="icon-user icon" />
Members
</div>
<div class="tab tags">
<i class="icon icon-tag" />
Tags
</div>
<div class="tab due-date">
<i class="icon icon-calendar-dark" />
Due date
</div>
<div class="tab project">
<i class="icon icon-deck" />
Project
</div>
<div class="tab attachments">
<i class="icon-attach icon icon-attach-dark" />
Attachments
</div>
</div>
<div class="content">
<div class="description">
<h2>Description</h2>
<p>Write a description</p>
</div>
<div class="edit-btns">
<ActionButton icon="icon-info" class="action-btn" />
<ActionButton icon="icon-rename" class="action-btn" />
</div>
</div>
<div class="activities">
<h2 class="activities-title">
<div class="icon-flash-black" /> Activities
</h2>
<div class="comment">
<Avatar :user="currentUser.uid" />
<input v-model="comment"
type="text"
:placeholder="t('deck', 'Leave a comment')"
class="comment-input">
</div>
<div class="activities-logs">
<p class="log-item">
<i class="icon-plus" /> You have created card Example Task 3 in list To do on board Personal
</p>
</div>
</div>
</div>
</template>
<script>
import { ActionButton, Avatar } from '@nextcloud/vue'
import { generateUrl } from '@nextcloud/router'
import { mapState, mapGetters } from 'vuex'
import relativeDate from '../../mixins/relativeDate'
import { showError } from '@nextcloud/dialogs'
import { getCurrentUser } from '@nextcloud/auth'
const capabilities = window.OC.getCapabilities()
export default {
name: 'CardModal',
components: { Avatar, ActionButton },
mixins: [relativeDate],
props: {
id: {
type: Number,
required: true,
},
tabId: {
type: String,
required: false,
default: null,
},
tabQuery: {
type: String,
required: false,
default: null,
},
},
data() {
return {
titleEditable: false,
titleEditing: '',
hasActivity: capabilities && capabilities.activity,
currentUser: getCurrentUser(),
comment: '',
}
},
computed: {
...mapState({
currentBoard: state => state.currentBoard,
}),
...mapGetters(['canEdit', 'assignables', 'cardActions', 'stackById']),
title() {
return this.titleEditable ? this.titleEditing : this.currentCard.title
},
currentCard() {
return this.$store.getters.cardById(this.id)
},
subtitle() {
return t('deck', 'Modified') + ': ' + this.relativeDate(this.currentCard.lastModified * 1000) + ' ' + t('deck', 'Created') + ': ' + this.relativeDate(this.currentCard.createdAt * 1000)
},
cardRichObject() {
return {
id: '' + this.currentCard.id,
name: this.currentCard.title,
boardname: this.currentBoard.title,
stackname: this.stackById(this.currentCard.stackId)?.title,
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `#/board/${this.currentBoard.id}/card/${this.currentCard.id}`,
}
},
cardDetailsInModal: {
get() {
return this.$store.getters.config('cardDetailsInModal')
},
set(newValue) {
this.$store.dispatch('setConfig', { cardDetailsInModal: newValue })
},
},
},
methods: {
handleUpdateTitleEditable(value) {
this.titleEditable = value
if (value) {
this.titleEditing = this.currentCard.title
}
},
handleUpdateTitle(value) {
this.titleEditing = value
},
handleSubmitTitle(value) {
if (value.trim === '') {
showError(t('deck', 'The title cannot be empty.'))
return
}
this.titleEditable = false
this.$store.dispatch('updateCardTitle', { ...this.currentCard, title: this.titleEditing })
},
closeSidebar() {
this.$router.push({ name: 'board' })
},
showModal() {
this.$store.dispatch('setConfig', { cardDetailsInModal: true })
},
closeModal() {
this.$store.dispatch('setConfig', { cardDetailsInModal: false })
},
},
}
</script>
<style lang="scss" scoped>
.icon-flash-black{
background-image: url(../../../img/flash-black.svg);
width: 15px;
height: 15px;
margin-right: 5px;
}
.icon-plus{
background-image: url(../../../img/plus.svg);
width: 15px;
height: 15px;
margin-right: 5px;
}
.log-item{
display: flex;
justify-content: flex-start;
line-height: 45px;
align-items: center;
}
.activities{
&-title{
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 15px;
font-weight: bold;
}
margin-top: 100px;
}
.comment{
display: flex;
justify-content: space-between;
align-items: center;
&-input{
width: 100%;
margin-left: 10px;
}
}
.container{
padding: 20px;
}
.top{
&-title{
color: black;
font-size: 20px;
font-weight: bold;
}
&-modified{
color: #767676;
line-height: 40px;
}
}
.tabs{
margin-top: 20px;
margin-bottom: 20px;
display: flex;
justify-content: flex-start;
grid-gap: 30px;
}
.tab{
cursor: pointer;
font-weight: bold;
background-color: #ededed;
color: rgb(0, 0, 0);
flex-grow: 0;
flex-shrink: 1;
display: flex;
flex-direction: row;
overflow: hidden;
padding: 10px 10px;
border-radius: 5px;
font-size: 85%;
margin-bottom: 3px;
margin-right: 5px;
width: 100px;
}
.action-btn{
list-style: none;
}
.content{
display: flex;
justify-content: space-between;
}
.edit-btns{
display: flex;
align-items: center;
}
</style>

View File

@@ -28,7 +28,7 @@ import Boards from './components/boards/Boards'
import Board from './components/board/Board'
import Sidebar from './components/Sidebar'
import BoardSidebar from './components/board/BoardSidebar'
import CardSidebar from './components/card/CardSidebar'
import CardModal from './components/card/CardModal'
import Overview from './components/overview/Overview'
Vue.use(Router)
@@ -119,7 +119,7 @@ export default new Router({
path: 'card/:cardId/:tabId?/:tabQuery?',
name: 'card',
components: {
sidebar: CardSidebar,
sidebar: CardModal,
},
props: {
default: (route) => {