Add form for editing project entries
This commit is contained in:
parent
dda9f75011
commit
175b517e75
|
|
@ -0,0 +1,44 @@
|
|||
describe('Project Entry', () => {
|
||||
beforeEach(() => {
|
||||
cy.exec("python ../server/manage.py prepare_projects_for_cypress");
|
||||
|
||||
cy.viewport('macbook-15');
|
||||
cy.startGraphQLCapture();
|
||||
cy.login('rahel.cueni', 'test');
|
||||
});
|
||||
|
||||
it('should create a new project entry', () => {
|
||||
cy.visit('/portfolio');
|
||||
cy.get('[data-cy=project-link]:first-of-type').click();
|
||||
cy.get('[data-cy=add-project-entry]:first-of-type').click();
|
||||
cy.get('[data-cy=activity-input]').within(() => {
|
||||
cy.get('[data-cy=text-form-input]').type('Join the Guardians');
|
||||
});
|
||||
cy.get('[data-cy=reflection-input]').within(() => {
|
||||
cy.get('[data-cy=text-form-input]').type('They are cool!');
|
||||
});
|
||||
cy.get('[data-cy=next-steps-input]').within(() => {
|
||||
cy.get('[data-cy=text-form-input]').type('Stay with Rocket\nMeet Quill');
|
||||
});
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
cy.waitFor('AddProjectEntryMutation');
|
||||
cy.get('.project-entry:last-of-type').within(() => {
|
||||
cy.get('.project-entry__paragraph:first-of-type').contains('Join the Guardians')
|
||||
});
|
||||
});
|
||||
|
||||
it('should edit first entry', () => {
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.get('.project-entry__paragraph:first-of-type').contains('Kill Thanos');
|
||||
cy.get('.project-entry:first-of-type').within(() => {
|
||||
cy.get('[data-cy=project-entry-more]').click();
|
||||
cy.get('[data-cy=edit-project-entry]').click();
|
||||
});
|
||||
cy.get('[data-cy=activity-input]').within(() => {
|
||||
cy.get('[data-cy=text-form-input]').clear().type('Defeat Thanos');
|
||||
});
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
cy.waitFor('UpdateProjectEntry');
|
||||
cy.get('.project-entry__paragraph:first-of-type').contains('Defeat Thanos');
|
||||
})
|
||||
});
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
import NewObjectiveGroupWizard from '@/components/objective-groups/NewObjectiveGroupWizard';
|
||||
import EditObjectiveGroupWizard from '@/components/objective-groups/EditObjectiveGroupWizard';
|
||||
import NewProjectEntryWizard from '@/components/portfolio/NewProjectEntryWizard';
|
||||
import EditProjectEntryWizard from '@/components/portfolio/EditProjectEntryWizard';
|
||||
import FullscreenImage from '@/components/FullscreenImage';
|
||||
import FullscreenInfographic from '@/components/FullscreenInfographic';
|
||||
import FullscreenVideo from '@/components/FullscreenVideo';
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
NewObjectiveGroupWizard,
|
||||
EditObjectiveGroupWizard,
|
||||
NewProjectEntryWizard,
|
||||
EditProjectEntryWizard,
|
||||
FullscreenImage,
|
||||
FullscreenInfographic,
|
||||
FullscreenVideo
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div class="more-options">
|
||||
<a @click="showMenu = !showMenu" class="more-options__more-link">
|
||||
<ellipses class="more-options__ellipses"></ellipses>
|
||||
</a>
|
||||
<widget-popover @hide-me="showMenu = false"
|
||||
class="more-options__popover"
|
||||
v-if="showMenu">
|
||||
<slot></slot>
|
||||
</widget-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WidgetPopover from '@/components/rooms/WidgetPopover';
|
||||
import Ellipses from '@/components/icons/Ellipses.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WidgetPopover,
|
||||
Ellipses
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showMenu: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/styles/_variables.scss";
|
||||
|
||||
.more-options {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
&__ellipses {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
fill: $color-darkgrey-1;
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
&__more-link {
|
||||
background-color: rgba($color-white, 0.9);
|
||||
width: 35px;
|
||||
height: 15px;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__popover {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
computed: {
|
||||
text() {
|
||||
return this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '')
|
||||
return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '') : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<project-entry-form :project-entry="projectEntry" @save="saveEntry" @hide="hideModal">
|
||||
</project-entry-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProjectEntryForm from './ProjectEntryForm';
|
||||
|
||||
import {mapGetters} from 'vuex';
|
||||
|
||||
import PROJECT_ENTRY_QUERY from '@/graphql/gql/projectEntryQuery.gql';
|
||||
import UPDATE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/updateProjectEntry.gql';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProjectEntryForm
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
projectEntry: {}
|
||||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
projectEntry() {
|
||||
return {
|
||||
query: PROJECT_ENTRY_QUERY,
|
||||
variables: {
|
||||
id: this.currentProjectEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['currentProjectEntry'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
saveEntry(entry) {
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_PROJECT_ENTRY_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
projectEntry: entry
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
this.hideModal();
|
||||
});
|
||||
},
|
||||
hideModal() {
|
||||
this.$store.dispatch('hideModal');
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,37 +1,17 @@
|
|||
<template>
|
||||
<modal :hide-header="true">
|
||||
<div class="project-entry-modal">
|
||||
<text-form-with-help-text title="Tätigkeit" :value="activity" @change="activity = $event"
|
||||
help-text="Was? Wie? Mittel?">
|
||||
</text-form-with-help-text>
|
||||
<text-form-with-help-text title="Reflexion" :value="reflection" @change="reflection = $event"
|
||||
help-text="Nachdenken über die eigene Tätigkeit und das eigene Handeln. Was ging gut? Was hatte ich für Schwierigkeiten? Was habe ich gelernt?">
|
||||
</text-form-with-help-text>
|
||||
<text-form-with-help-text title="Nächste Schritte" :value="nextSteps" @change="nextSteps = $event"
|
||||
help-text="Wie geht es weiter? Wer macht was?">
|
||||
</text-form-with-help-text>
|
||||
<document-form :value="document" :index="0" @link-change-url="setDocumentUrl"></document-form>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<a class="button button--primary" data-cy="modal-save-button" v-on:click="save">Speichern</a>
|
||||
<a class="button" v-on:click="hideModal">Abbrechen</a>
|
||||
</div>
|
||||
</modal>
|
||||
<project-entry-form @save="save" @hide="hideModal" :project-entry="projectEntry">
|
||||
</project-entry-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '@/components/Modal';
|
||||
import TextFormWithHelpText from '@/components/content-forms/TextFormWithHelpText';
|
||||
import ProjectEntryForm from './ProjectEntryForm';
|
||||
|
||||
import NEW_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/addProjectEntry.gql';
|
||||
import PROJECT_QUERY from '@/graphql/gql/projectQuery.gql';
|
||||
import DocumentForm from '@/components/content-forms/DocumentForm';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DocumentForm,
|
||||
Modal,
|
||||
TextFormWithHelpText
|
||||
ProjectEntryForm
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
|
@ -40,30 +20,18 @@
|
|||
},
|
||||
slug() {
|
||||
return this.$route.params.slug;
|
||||
},
|
||||
document() {
|
||||
return this.documentUrl > '' ? {
|
||||
url: this.documentUrl
|
||||
} : {};
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setDocumentUrl(url) {
|
||||
this.documentUrl = url;
|
||||
},
|
||||
save() {
|
||||
save(entry) {
|
||||
this.$apollo.mutate({
|
||||
mutation: NEW_PROJECT_ENTRY_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
projectEntry: Object.assign({}, {
|
||||
nextSteps: this.nextSteps,
|
||||
activity: this.activity,
|
||||
reflection: this.reflection,
|
||||
documentUrl: this.documentUrl,
|
||||
projectEntry: Object.assign({
|
||||
project: this.project
|
||||
})
|
||||
}, entry)
|
||||
}
|
||||
},
|
||||
update: (store, {data: {addProjectEntry: {projectEntry}}}) => {
|
||||
|
|
@ -71,7 +39,7 @@
|
|||
const variables = {slug: this.slug};
|
||||
const data = store.readQuery({query, variables});
|
||||
if (data.project && data.project.entries) {
|
||||
data.project.entries.edges.unshift({
|
||||
data.project.entries.edges.push({
|
||||
node: projectEntry,
|
||||
__typename: 'ProjectEntryNode'
|
||||
});
|
||||
|
|
@ -90,10 +58,12 @@
|
|||
|
||||
data() {
|
||||
return {
|
||||
activity: '',
|
||||
reflection: '',
|
||||
nextSteps: '',
|
||||
documentUrl: ''
|
||||
projectEntry: {
|
||||
activity: '',
|
||||
reflection: '',
|
||||
nextSteps: '',
|
||||
documentUrl: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
<template>
|
||||
<div class="project-entry">
|
||||
<more-options-widget class="project-entry__more" data-cy="project-entry-more">
|
||||
<li class="popover-links__link"><a @click="editProjectEntry()" data-cy="edit-project-entry">Eintrag bearbeiten</a></li>
|
||||
</more-options-widget>
|
||||
|
||||
<h3 class="project-entry__heading">Tätigkeit</h3>
|
||||
<p class="project-entry__paragraph">
|
||||
<p class="project-entry__paragraph" data-cy="project-entry-activity">
|
||||
{{activity}}
|
||||
</p>
|
||||
<h3 class="project-entry__heading">Reflexion</h3>
|
||||
|
|
@ -25,12 +29,20 @@
|
|||
|
||||
<script>
|
||||
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
|
||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DocumentBlock
|
||||
DocumentBlock,
|
||||
MoreOptionsWidget
|
||||
},
|
||||
props: ['activity', 'reflection', 'nextSteps', 'documentUrl', 'created']
|
||||
props: ['activity', 'reflection', 'nextSteps', 'documentUrl', 'created', 'id'],
|
||||
|
||||
methods: {
|
||||
editProjectEntry() {
|
||||
this.$store.dispatch('editProjectEntry', this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -43,6 +55,7 @@
|
|||
background-color: $color-white;
|
||||
border-radius: $default-border-radius;
|
||||
padding: 30px 20px;
|
||||
position: relative;
|
||||
|
||||
&__heading {
|
||||
font-size: toRem(22px);
|
||||
|
|
@ -63,5 +76,12 @@
|
|||
cursor: pointer;
|
||||
@include heading-4;
|
||||
}
|
||||
|
||||
&__more {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<modal :hide-header="true">
|
||||
<div class="project-entry-modal">
|
||||
<text-form-with-help-text title="Tätigkeit" :value="localProjectEntry.activity"
|
||||
@change="localProjectEntry.activity = $event"
|
||||
data-cy="activity-input"
|
||||
help-text="Was? Wie? Mittel?">
|
||||
</text-form-with-help-text>
|
||||
<text-form-with-help-text title="Reflexion" :value="localProjectEntry.reflection"
|
||||
@change="localProjectEntry.reflection = $event"
|
||||
data-cy="reflection-input"
|
||||
help-text="Nachdenken über die eigene Tätigkeit und das eigene Handeln. Was ging gut? Was hatte ich für Schwierigkeiten? Was habe ich gelernt?">
|
||||
</text-form-with-help-text>
|
||||
<text-form-with-help-text title="Nächste Schritte" :value="localProjectEntry.nextSteps"
|
||||
@change="localProjectEntry.nextSteps = $event"
|
||||
data-cy="next-steps-input"
|
||||
help-text="Wie geht es weiter? Wer macht was?">
|
||||
</text-form-with-help-text>
|
||||
<document-form :value="document" :index="0" @link-change-url="setDocumentUrl"></document-form>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<a class="button button--primary" data-cy="modal-save-button" v-on:click="$emit('save', localProjectEntry)">Speichern</a>
|
||||
<a class="button" v-on:click="$emit('hide')">Abbrechen</a>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '@/components/Modal';
|
||||
import TextFormWithHelpText from '@/components/content-forms/TextFormWithHelpText';
|
||||
import DocumentForm from '@/components/content-forms/DocumentForm';
|
||||
|
||||
export default {
|
||||
props: ['project-entry'],
|
||||
|
||||
components: {
|
||||
DocumentForm,
|
||||
Modal,
|
||||
TextFormWithHelpText
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
localProjectEntry: Object.assign({}, {
|
||||
...this.projectEntry
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
document() {
|
||||
return this.localProjectEntry.documentUrl > '' ? {
|
||||
url: this.localProjectEntry.documentUrl
|
||||
} : {};
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setDocumentUrl(url) {
|
||||
this.localProjectEntry.documentUrl = url;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="project-widget" :class="widgetClass">
|
||||
<router-link :to="{name: 'project', params: {slug: slug}}" tag="div" class="project-widget__content">
|
||||
<router-link :to="{name: 'project', params: {slug: slug}}" tag="div" class="project-widget__content" data-cy="project-link">
|
||||
<h3 class="project-widget__title">{{title}}</h3>
|
||||
|
||||
<entry-count-widget :entry-count="entriesCount"></entry-count-widget>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,9 @@
|
|||
<template>
|
||||
<div class="room-entry">
|
||||
<div class="room-entry__more" v-if="myEntry">
|
||||
<a @click="showMenu = !showMenu" class="room-entry__more-link">
|
||||
<ellipses class="room-entry__ellipses"></ellipses>
|
||||
</a>
|
||||
<widget-popover @hide-me="showMenu = false"
|
||||
:id="id"
|
||||
class="room-entry__popover"
|
||||
v-if="showMenu">
|
||||
<li class="popover-links__link"><a @click="deleteRoomEntry(id)">Eintrag löschen</a></li>
|
||||
<li class="popover-links__link"><a @click="editRoomEntry(id)">Eintrag bearbeiten</a></li>
|
||||
</widget-popover>
|
||||
</div>
|
||||
<more-options-widget class="room-entry__more" v-if="myEntry">
|
||||
<li class="popover-links__link"><a @click="deleteRoomEntry(id)">Eintrag löschen</a></li>
|
||||
<li class="popover-links__link"><a @click="editRoomEntry(id)">Eintrag bearbeiten</a></li>
|
||||
</more-options-widget>
|
||||
<router-link :to="{name: 'article', params: { slug: slug }}" tag="div" class="room-entry__router-link">
|
||||
<div class="room-entry__header" v-if="image">
|
||||
<img class="room-entry__image" :src="image" :alt="title">
|
||||
|
|
@ -34,16 +26,14 @@
|
|||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
|
||||
import UserWidget from '@/components/UserWidget.vue';
|
||||
import Ellipses from '@/components/icons/Ellipses.vue';
|
||||
import WidgetPopover from '@/components/rooms/WidgetPopover';
|
||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||
|
||||
export default {
|
||||
props: ['title', 'author', 'contents', 'slug', 'id'],
|
||||
|
||||
components: {
|
||||
MoreOptionsWidget,
|
||||
UserWidget,
|
||||
Ellipses,
|
||||
WidgetPopover
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -110,12 +100,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showMenu: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -157,28 +141,7 @@
|
|||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__more-link {
|
||||
background-color: rgba($color-white, 0.9);
|
||||
width: 35px;
|
||||
height: 15px;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__ellipses {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
fill: $color-darkgrey-1;
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
&__popover {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ const cache = new InMemoryCache({
|
|||
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id}),
|
||||
objective: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveNode', id: args.id}),
|
||||
objectiveGroup: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveGroupNode', id: args.id}),
|
||||
module: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ModuleNode', id: args.id})
|
||||
module: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ModuleNode', id: args.id}),
|
||||
projectEntry: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ProjectEntryNode', id: args.id}),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@ fragment ProjectEntryParts on ProjectEntryNode {
|
|||
reflection
|
||||
nextSteps
|
||||
documentUrl
|
||||
created
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ mutation AddProjectEntryMutation($input: AddProjectEntryInput!) {
|
|||
addProjectEntry(input: $input) {
|
||||
projectEntry {
|
||||
...ProjectEntryParts
|
||||
created
|
||||
}
|
||||
errors
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/projectEntryParts.gql"
|
||||
mutation UpdateProjectEntry($input: UpdateProjectEntryInput!){
|
||||
updateProjectEntry(input: $input) {
|
||||
projectEntry {
|
||||
...ProjectEntryParts
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#import "./fragments/projectEntryParts.gql"
|
||||
query ProjectEntryQuery($id: ID!) {
|
||||
projectEntry(id: $id) {
|
||||
...ProjectEntryParts
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ query ProjectQuery($id: ID, $slug: String){
|
|||
edges {
|
||||
node {
|
||||
...ProjectEntryParts
|
||||
created
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
</div>
|
||||
<div class="project__content">
|
||||
<add-project-entry v-if="isOwner" class="project__add-entry" :project="project.id"></add-project-entry>
|
||||
<add-project-entry v-if="isOwner" class="project__add-entry" data-cy="add-project-entry" :project="project.id"></add-project-entry>
|
||||
<project-entry v-bind="entry" v-for="(entry, index) in project.entries" :key="index"></project-entry>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export default new Vuex.Store({
|
|||
objectiveGroupType: '',
|
||||
currentObjectiveGroup: '',
|
||||
parentProject: null,
|
||||
currentProjectEntry: null,
|
||||
imageUrl: '',
|
||||
infographic: {
|
||||
id: 0,
|
||||
|
|
@ -41,6 +42,7 @@ export default new Vuex.Store({
|
|||
scrollToAssignmentId: state => state.scrollToAssignmentId,
|
||||
scrollToAssignmentReady: state => state.scrollToAssignmentReady,
|
||||
scrollingToAssignment: state => state.scrollingToAssignment,
|
||||
currentProjectEntry: state => state.currentProjectEntry,
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
@ -61,6 +63,7 @@ export default new Vuex.Store({
|
|||
commit('setObjectiveGroupType', '');
|
||||
commit('setCurrentObjectiveGroup', '');
|
||||
commit('setParentProject', null);
|
||||
commit('setCurrentProjectEntry', null);
|
||||
commit('setImageUrl', '');
|
||||
commit('setInfographic', {
|
||||
id: 0,
|
||||
|
|
@ -110,6 +113,10 @@ export default new Vuex.Store({
|
|||
commit('setParentProject', payload);
|
||||
dispatch('showModal', 'new-project-entry-wizard');
|
||||
},
|
||||
editProjectEntry({commit, dispatch}, payload) {
|
||||
commit('setCurrentProjectEntry', payload);
|
||||
dispatch('showModal', 'edit-project-entry-wizard');
|
||||
},
|
||||
showFullscreenImage({commit, dispatch}, payload) {
|
||||
commit('setImageUrl', payload);
|
||||
dispatch('showModal', 'fullscreen-image');
|
||||
|
|
@ -183,6 +190,9 @@ export default new Vuex.Store({
|
|||
setParentProject(state, payload) {
|
||||
state.parentProject = payload;
|
||||
},
|
||||
setCurrentProjectEntry(state, payload) {
|
||||
state.currentProjectEntry = payload;
|
||||
},
|
||||
setImageUrl(state, payload) {
|
||||
state.imageUrl = payload;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
from django.core.management import BaseCommand
|
||||
|
||||
from books.models import Module
|
||||
from portfolio.factories import ProjectFactory
|
||||
from portfolio.models import ProjectEntry
|
||||
from users.models import User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write("Preparing projects")
|
||||
user = User.objects.get(username='rahel.cueni')
|
||||
self.stdout.write("Deleting all projects")
|
||||
for project in user.projects.all():
|
||||
project.delete()
|
||||
|
||||
self.stdout.write("Creating new project")
|
||||
project = ProjectFactory(
|
||||
title='Groot',
|
||||
description='I am Groot',
|
||||
student=user,
|
||||
objectives='Be Groot\nBe awesome'
|
||||
)
|
||||
|
||||
self.stdout.write("Creating project entries")
|
||||
ProjectEntry.objects.create(
|
||||
activity='Kill Thanos',
|
||||
reflection='He sucks',
|
||||
next_steps='Go for the head',
|
||||
project=project
|
||||
)
|
||||
|
||||
ProjectEntry.objects.create(
|
||||
activity='Grow up again',
|
||||
reflection='Being a teenager sucks',
|
||||
next_steps='Grow',
|
||||
project=project
|
||||
)
|
||||
|
|
@ -10,27 +10,8 @@ from portfolio.schema import ProjectNode, ProjectEntryNode
|
|||
from portfolio.serializers import ProjectSerializer, ProjectEntrySerializer
|
||||
|
||||
|
||||
# class Mutation(relay.ClientIDMutation):
|
||||
# class Meta:
|
||||
# pass
|
||||
#
|
||||
# @classmethod
|
||||
# def mutate_and_get_payload(cls, *args, **kwargs):
|
||||
# data = kwargs.get(cls.meta.property)
|
||||
# if data.get('id') is not None:
|
||||
# project = get_object(cls.meta.serializer_class.model, data['id'])
|
||||
# serializer = cls.meta.serializer_class(project, data=data)
|
||||
# else:
|
||||
# serializer = cls.meta.serializer_class(data=data)
|
||||
# if serializer.is_valid():
|
||||
# serializer.save()
|
||||
# props = {
|
||||
# cls.meta.property: serializer.instance,
|
||||
# 'errors': None
|
||||
# }
|
||||
# return cls(**props)
|
||||
#
|
||||
# return cls(errors=['{}: {}'.format(key, value) for key, value in serializer.errors.items()])
|
||||
def check_owner(user, project):
|
||||
return user.id != project.student.id
|
||||
|
||||
class MutateProject(relay.ClientIDMutation):
|
||||
errors = graphene.List(graphene.String)
|
||||
|
|
@ -97,12 +78,13 @@ class MutateProjectEntry(relay.ClientIDMutation):
|
|||
if data.get('project') is not None:
|
||||
project = get_object(Project, data.get('project'))
|
||||
data['project'] = project.id
|
||||
|
||||
if info.context.user.id != project.student.id:
|
||||
return cls(project_entry=None, errors=['not allowed'])
|
||||
if check_owner(info.context.user, project):
|
||||
return cls(project_entry=None, errors=['not allowed'])
|
||||
|
||||
if data.get('id') is not None:
|
||||
entity = get_object(ProjectEntry, data['id'])
|
||||
if check_owner(info.context.user, entity.project):
|
||||
return cls(project_entry=None, errors=['not allowed'])
|
||||
serializer = ProjectEntrySerializer(entity, data=data, partial=True)
|
||||
else:
|
||||
serializer = ProjectEntrySerializer(data=data)
|
||||
|
|
|
|||
Loading…
Reference in New Issue