Browse Source

Update project page

pull/341/head
Hironsan 5 years ago
parent
commit
c9cbfb4be3
11 changed files with 406 additions and 192 deletions
  1. 1
      frontend/api/routes/projects.js
  2. 63
      frontend/components/BaseCard.vue
  3. 104
      frontend/components/FormProjectCreation.vue
  4. 9
      frontend/components/containers/ProjectCreationButton.vue
  5. 45
      frontend/components/containers/ProjectList.vue
  6. 37
      frontend/components/organisms/ProjectCreationButton.vue
  7. 63
      frontend/components/organisms/ProjectDeletionButton.vue
  8. 57
      frontend/components/organisms/ProjectList.vue
  9. 15
      frontend/layouts/projects.vue
  10. 143
      frontend/pages/projects/index.vue
  11. 61
      frontend/store/ProjectList.js

1
frontend/api/routes/projects.js

@ -29,6 +29,7 @@ router.post('/', (req, res) => {
updated_at: '2019-08-21T02:49:48.790813Z',
randomize_document_order: false
}
db.push(project)
res.json(project)
})

63
frontend/components/BaseCard.vue

@ -0,0 +1,63 @@
<template>
<v-card>
<v-card-title class="grey lighten-2">
{{ title }}
</v-card-title>
<v-container grid-list-sm>
<v-layout wrap>
<v-flex xs12>
<slot name="content" />
</v-flex>
</v-layout>
</v-container>
<v-card-actions>
<v-spacer />
<v-btn
class="text-capitalize"
text
color="primary"
@click="cancel"
>
Cancel
</v-btn>
<v-btn
:disabled="disabled"
class="text-none"
text
@click="agree"
>
{{ button }}
</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '',
required: true
},
button: {
type: String,
default: '',
required: true
},
disabled: {
type: Boolean,
default: false,
required: true
}
},
methods: {
cancel() {
this.$emit('cancel')
},
agree() {
this.$emit('agree')
}
}
}
</script>

104
frontend/components/FormProjectCreation.vue

@ -1,68 +1,51 @@
<template>
<v-card>
<v-card-title class="grey lighten-2">
Create Project
</v-card-title>
<v-container grid-list-sm>
<v-layout wrap>
<v-flex xs12>
<v-form
ref="form"
v-model="valid"
>
<v-text-field
v-model="name"
:rules="nameRules"
label="Project name"
prepend-icon="mdi-account-multiple"
required
autofocus
/>
<v-text-field
v-model="description"
:rules="descriptionRules"
label="Description"
prepend-icon="mdi-clipboard-text"
required
/>
<v-select
v-model="projectType"
:items="projectTypes"
:rules="[v => !!v || 'Type is required']"
label="projectType"
prepend-icon="mdi-keyboard"
required
/>
</v-form>
</v-flex>
</v-layout>
</v-container>
<v-card-actions>
<v-spacer />
<v-btn
class="text-capitalize"
text
color="primary"
@click="cancel"
<base-card
title="Create Project"
button="Add Project"
:disabled="!valid"
@cancel="cancel"
@agree="createProject"
>
<template #content>
<v-form
ref="form"
v-model="valid"
>
Cancel
</v-btn>
<v-btn
:disabled="!valid"
class="text-none"
text
@click="createProject"
>
Add Project
</v-btn>
</v-card-actions>
</v-card>
<v-text-field
v-model="name"
:rules="nameRules"
label="Project name"
prepend-icon="mdi-account-multiple"
required
autofocus
/>
<v-text-field
v-model="description"
:rules="descriptionRules"
label="Description"
prepend-icon="mdi-clipboard-text"
required
/>
<v-select
v-model="projectType"
:items="projectTypes"
:rules="[v => !!v || 'Type is required']"
label="projectType"
prepend-icon="mdi-keyboard"
required
/>
</v-form>
</template>
</base-card>
</template>
<script>
import ProjectService from '~/services/project.service'
import BaseCard from '~/components/BaseCard'
export default {
components: {
BaseCard
},
data: () => ({
valid: true,
name: '',
@ -88,16 +71,15 @@ export default {
this.$emit('cancel')
},
async createProject() {
createProject() {
const data = {
name: this.name,
description: this.description,
project_type: this.projectType
}
if (this.$refs.form.validate()) {
const response = await ProjectService.createProject(data)
this.$refs.form.reset()
this.$emit('create-project', response)
this.$emit('create-project', data)
}
}
}

9
frontend/components/containers/ProjectCreationButton.vue

@ -0,0 +1,9 @@
<template>
</template>
<script>
export default {
}
</script>

45
frontend/components/containers/ProjectList.vue

@ -0,0 +1,45 @@
<template>
<project-list :headers="headers" :projects="projects" :selected="selected" @update="update" />
</template>
<script>
import { mapState } from 'vuex'
import ProjectList from '@/components/organisms/ProjectList'
export default {
components: {
ProjectList
},
data: () => ({
headers: [
{
text: 'Name',
align: 'left',
value: 'name'
},
{
text: 'Description',
value: 'description'
},
{
text: 'Type',
value: 'project_type'
}
]
}),
computed: {
...mapState('ProjectList', ['projects', 'selected'])
},
async created() {
await this.$store.dispatch('ProjectList/getProjectList')
},
methods: {
update(selected) {
this.$store.commit('ProjectList/updateSelected', selected)
}
}
}
</script>

37
frontend/components/organisms/ProjectCreationButton.vue

@ -0,0 +1,37 @@
<template>
<div>
<v-btn
class="mb-2 text-capitalize"
color="primary"
@click="dialog=true"
>
Add Project
</v-btn>
<v-dialog
v-model="dialog"
width="800px"
>
<form-project-creation @cancel="dialog=false" @create-project="createProject" />
</v-dialog>
</div>
</template>
<script>
import FormProjectCreation from '@/components/FormProjectCreation'
export default {
components: {
FormProjectCreation
},
data: () => ({
dialog: false
}),
methods: {
createProject(project) {
this.$store.dispatch('ProjectList/createProject', project)
this.dialog = false
}
}
}
</script>

63
frontend/components/organisms/ProjectDeletionButton.vue

@ -0,0 +1,63 @@
<template>
<div>
<v-btn
class="mb-2 ml-2 text-capitalize"
outlined
:disabled="selected.length === 0"
@click="dialog=true"
>
Remove
</v-btn>
<v-dialog
v-model="dialog"
width="800px"
>
<base-card
title="Delete Project"
button="Yes, delete"
:disabled="disabled"
@cancel="dialog=false"
@agree="deleteProject"
>
<template #content>
Are you sure you want to remove these projects?
<v-list dense>
<v-list-item v-for="(item, i) in selected" :key="i">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</template>
</base-card>
</v-dialog>
</div>
</template>
<script>
import { mapState } from 'vuex'
import BaseCard from '@/components/BaseCard'
export default {
components: {
BaseCard
},
data: () => ({
dialog: false,
disabled: false
}),
computed: {
...mapState('ProjectList', ['projects', 'selected'])
},
methods: {
deleteProject() {
this.$store.dispatch('ProjectList/deleteProject')
// this.$emit('remove')
this.dialog = false
}
}
}
</script>

57
frontend/components/organisms/ProjectList.vue

@ -0,0 +1,57 @@
<template>
<v-data-table
:value="selected"
:headers="headers"
:items="projects"
:search="search"
item-key="id"
show-select
@input="update"
>
<template v-slot:top>
<v-text-field
v-model="search"
prepend-inner-icon="search"
label="Search"
single-line
hide-details
filled
/>
</template>
<template v-slot:item.name="{ item }">
<nuxt-link :to="`/projects/${item.id}`">
<span>{{ item.name }}</span>
</nuxt-link>
</template>
</v-data-table>
</template>
<script>
export default {
props: {
headers: {
type: Array,
default: () => [],
required: true
},
projects: {
type: Array,
default: () => [],
required: true
},
selected: {
type: Array,
default: () => [],
required: true
}
},
data: () => ({
search: ''
}),
methods: {
update(selected) {
this.$emit('update', selected)
}
}
}
</script>

15
frontend/layouts/projects.vue

@ -1,7 +1,20 @@
<template>
<base-layout>
<template #content>
<nuxt />
<v-content>
<v-container
fluid
fill-height
>
<v-layout
justify-center
>
<v-flex>
<nuxt />
</v-flex>
</v-layout>
</v-container>
</v-content>
</template>
</base-layout>
</template>

143
frontend/pages/projects/index.vue

@ -1,141 +1,24 @@
<template>
<v-content>
<v-container
fluid
fill-height
>
<v-layout
justify-center
>
<v-flex>
<v-card>
<v-card-title>
<v-btn
class="mb-2 text-capitalize"
color="primary"
@click="dialog=true"
>
Add Project
</v-btn>
<v-dialog
v-model="dialog"
width="800px"
>
<form-project-creation @cancel="dialog=false" @create-project="createProject" />
</v-dialog>
<v-btn
class="mb-2 ml-2 text-capitalize"
outlined
:disabled="selected.length === 0"
@click="openRemoveModal"
>
Remove
</v-btn>
<Modal
ref="removeDialogue"
:title="removeModal.title"
:button="removeModal.button"
@agree="deleteProject"
>
Are you sure you want to remove these projects?
<v-list dense>
<v-list-item v-for="(item, i) in selected" :key="i">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</Modal>
</v-card-title>
<v-data-table
v-model="selected"
:headers="headers"
:items="projects"
:search="search"
item-key="id"
show-select
>
<template v-slot:top>
<v-text-field
v-model="search"
prepend-inner-icon="search"
label="Search"
single-line
hide-details
filled
/>
</template>
<template v-slot:item.name="{ item }">
<nuxt-link :to="`/projects/${item.id}`">
<span>{{ item.name }}</span>
</nuxt-link>
</template>
</v-data-table>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-content>
<v-card>
<v-card-title>
<project-creation-button />
<project-deletion-button />
</v-card-title>
<project-list />
</v-card>
</template>
<script>
import Modal from '~/components/Modal'
import FormProjectCreation from '~/components/FormProjectCreation'
import ProjectService from '~/services/project.service'
import ProjectList from '@/components/containers/ProjectList'
import ProjectCreationButton from '@/components/organisms/ProjectCreationButton'
import ProjectDeletionButton from '@/components/organisms/ProjectDeletionButton'
export default {
layout: 'projects',
components: {
Modal,
FormProjectCreation
},
data: () => ({
dialog: false,
search: '',
selected: [],
selectedUser: null,
projects: [],
removeModal: {
title: 'Remove Project',
button: 'Yes, remove'
},
headers: [
{
text: 'Name',
align: 'left',
value: 'name'
},
{
text: 'Description',
value: 'description'
},
{
text: 'Type',
value: 'project_type'
}
]
}),
async created() {
this.projects = await ProjectService.getProjectList()
},
methods: {
createProject(project) {
this.projects.unshift(project)
this.dialog = false
},
async deleteProject() {
// Todo: bulk delete.
for (const project of this.selected) {
await ProjectService.deleteProject(project.id)
this.projects = this.projects.filter(item => item.id !== project.id)
}
this.selected = []
},
openRemoveModal() {
this.$refs.removeDialogue.open()
}
ProjectList,
ProjectCreationButton,
ProjectDeletionButton
}
}
</script>

61
frontend/store/ProjectList.js

@ -0,0 +1,61 @@
import ProjectService from '@/services/project.service'
export default {
namespaced: true,
state: () => ({
projects: [],
selected: []
}),
mutations: {
setProjectList(state, payload) {
state.projects = payload
},
createProject(state, project) {
state.projects.unshift(project)
},
deleteProject(state, projectId) {
state.projects = state.projects.filter(item => item.id !== projectId)
},
updateSelected(state, selected) {
state.selected = selected
},
resetSelected(state) {
state.selected = []
}
},
actions: {
getProjectList(context, config) {
return ProjectService.getProjectList()
.then((response) => {
context.commit('setProjectList', response)
})
.catch((error) => {
alert(error)
})
},
createProject({ commit }, project) {
ProjectService.createProject(project)
.then((response) => {
commit('createProject', response)
})
.catch((error) => {
alert(error)
})
},
deleteProject({ commit, state }, config) {
for (const project of state.selected) {
ProjectService.deleteProject(project.id)
.then((response) => {
commit('deleteProject', project.id)
})
.catch((error) => {
alert(error)
})
}
commit('resetSelected')
}
}
}
Loading…
Cancel
Save