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 NewObjectiveGroupWizard from '@/components/objective-groups/NewObjectiveGroupWizard';
|
||||||
import EditObjectiveGroupWizard from '@/components/objective-groups/EditObjectiveGroupWizard';
|
import EditObjectiveGroupWizard from '@/components/objective-groups/EditObjectiveGroupWizard';
|
||||||
import NewProjectEntryWizard from '@/components/portfolio/NewProjectEntryWizard';
|
import NewProjectEntryWizard from '@/components/portfolio/NewProjectEntryWizard';
|
||||||
|
import EditProjectEntryWizard from '@/components/portfolio/EditProjectEntryWizard';
|
||||||
import FullscreenImage from '@/components/FullscreenImage';
|
import FullscreenImage from '@/components/FullscreenImage';
|
||||||
import FullscreenInfographic from '@/components/FullscreenInfographic';
|
import FullscreenInfographic from '@/components/FullscreenInfographic';
|
||||||
import FullscreenVideo from '@/components/FullscreenVideo';
|
import FullscreenVideo from '@/components/FullscreenVideo';
|
||||||
|
|
@ -41,6 +42,7 @@
|
||||||
NewObjectiveGroupWizard,
|
NewObjectiveGroupWizard,
|
||||||
EditObjectiveGroupWizard,
|
EditObjectiveGroupWizard,
|
||||||
NewProjectEntryWizard,
|
NewProjectEntryWizard,
|
||||||
|
EditProjectEntryWizard,
|
||||||
FullscreenImage,
|
FullscreenImage,
|
||||||
FullscreenInfographic,
|
FullscreenInfographic,
|
||||||
FullscreenVideo
|
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: {
|
computed: {
|
||||||
text() {
|
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>
|
<template>
|
||||||
<modal :hide-header="true">
|
<project-entry-form @save="save" @hide="hideModal" :project-entry="projectEntry">
|
||||||
<div class="project-entry-modal">
|
</project-entry-form>
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Modal from '@/components/Modal';
|
import ProjectEntryForm from './ProjectEntryForm';
|
||||||
import TextFormWithHelpText from '@/components/content-forms/TextFormWithHelpText';
|
|
||||||
|
|
||||||
import NEW_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/addProjectEntry.gql';
|
import NEW_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/addProjectEntry.gql';
|
||||||
import PROJECT_QUERY from '@/graphql/gql/projectQuery.gql';
|
import PROJECT_QUERY from '@/graphql/gql/projectQuery.gql';
|
||||||
import DocumentForm from '@/components/content-forms/DocumentForm';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DocumentForm,
|
ProjectEntryForm
|
||||||
Modal,
|
|
||||||
TextFormWithHelpText
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -40,30 +20,18 @@
|
||||||
},
|
},
|
||||||
slug() {
|
slug() {
|
||||||
return this.$route.params.slug;
|
return this.$route.params.slug;
|
||||||
},
|
|
||||||
document() {
|
|
||||||
return this.documentUrl > '' ? {
|
|
||||||
url: this.documentUrl
|
|
||||||
} : {};
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setDocumentUrl(url) {
|
save(entry) {
|
||||||
this.documentUrl = url;
|
|
||||||
},
|
|
||||||
save() {
|
|
||||||
this.$apollo.mutate({
|
this.$apollo.mutate({
|
||||||
mutation: NEW_PROJECT_ENTRY_MUTATION,
|
mutation: NEW_PROJECT_ENTRY_MUTATION,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
projectEntry: Object.assign({}, {
|
projectEntry: Object.assign({
|
||||||
nextSteps: this.nextSteps,
|
|
||||||
activity: this.activity,
|
|
||||||
reflection: this.reflection,
|
|
||||||
documentUrl: this.documentUrl,
|
|
||||||
project: this.project
|
project: this.project
|
||||||
})
|
}, entry)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update: (store, {data: {addProjectEntry: {projectEntry}}}) => {
|
update: (store, {data: {addProjectEntry: {projectEntry}}}) => {
|
||||||
|
|
@ -71,7 +39,7 @@
|
||||||
const variables = {slug: this.slug};
|
const variables = {slug: this.slug};
|
||||||
const data = store.readQuery({query, variables});
|
const data = store.readQuery({query, variables});
|
||||||
if (data.project && data.project.entries) {
|
if (data.project && data.project.entries) {
|
||||||
data.project.entries.edges.unshift({
|
data.project.entries.edges.push({
|
||||||
node: projectEntry,
|
node: projectEntry,
|
||||||
__typename: 'ProjectEntryNode'
|
__typename: 'ProjectEntryNode'
|
||||||
});
|
});
|
||||||
|
|
@ -90,10 +58,12 @@
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
activity: '',
|
projectEntry: {
|
||||||
reflection: '',
|
activity: '',
|
||||||
nextSteps: '',
|
reflection: '',
|
||||||
documentUrl: ''
|
nextSteps: '',
|
||||||
|
documentUrl: ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="project-entry">
|
<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>
|
<h3 class="project-entry__heading">Tätigkeit</h3>
|
||||||
<p class="project-entry__paragraph">
|
<p class="project-entry__paragraph" data-cy="project-entry-activity">
|
||||||
{{activity}}
|
{{activity}}
|
||||||
</p>
|
</p>
|
||||||
<h3 class="project-entry__heading">Reflexion</h3>
|
<h3 class="project-entry__heading">Reflexion</h3>
|
||||||
|
|
@ -25,12 +29,20 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
|
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
|
||||||
|
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
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>
|
</script>
|
||||||
|
|
||||||
|
|
@ -43,6 +55,7 @@
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-radius: $default-border-radius;
|
border-radius: $default-border-radius;
|
||||||
padding: 30px 20px;
|
padding: 30px 20px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&__heading {
|
&__heading {
|
||||||
font-size: toRem(22px);
|
font-size: toRem(22px);
|
||||||
|
|
@ -63,5 +76,12 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@include heading-4;
|
@include heading-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__more {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
<template>
|
||||||
<div class="project-widget" :class="widgetClass">
|
<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>
|
<h3 class="project-widget__title">{{title}}</h3>
|
||||||
|
|
||||||
<entry-count-widget :entry-count="entriesCount"></entry-count-widget>
|
<entry-count-widget :entry-count="entriesCount"></entry-count-widget>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="room-entry">
|
<div class="room-entry">
|
||||||
<div class="room-entry__more" v-if="myEntry">
|
<more-options-widget class="room-entry__more" v-if="myEntry">
|
||||||
<a @click="showMenu = !showMenu" class="room-entry__more-link">
|
<li class="popover-links__link"><a @click="deleteRoomEntry(id)">Eintrag löschen</a></li>
|
||||||
<ellipses class="room-entry__ellipses"></ellipses>
|
<li class="popover-links__link"><a @click="editRoomEntry(id)">Eintrag bearbeiten</a></li>
|
||||||
</a>
|
</more-options-widget>
|
||||||
<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>
|
|
||||||
<router-link :to="{name: 'article', params: { slug: slug }}" tag="div" class="room-entry__router-link">
|
<router-link :to="{name: 'article', params: { slug: slug }}" tag="div" class="room-entry__router-link">
|
||||||
<div class="room-entry__header" v-if="image">
|
<div class="room-entry__header" v-if="image">
|
||||||
<img class="room-entry__image" :src="image" :alt="title">
|
<img class="room-entry__image" :src="image" :alt="title">
|
||||||
|
|
@ -34,16 +26,14 @@
|
||||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||||
|
|
||||||
import UserWidget from '@/components/UserWidget.vue';
|
import UserWidget from '@/components/UserWidget.vue';
|
||||||
import Ellipses from '@/components/icons/Ellipses.vue';
|
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||||
import WidgetPopover from '@/components/rooms/WidgetPopover';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['title', 'author', 'contents', 'slug', 'id'],
|
props: ['title', 'author', 'contents', 'slug', 'id'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
MoreOptionsWidget,
|
||||||
UserWidget,
|
UserWidget,
|
||||||
Ellipses,
|
|
||||||
WidgetPopover
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -110,12 +100,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showMenu: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -157,28 +141,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 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>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ const cache = new InMemoryCache({
|
||||||
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id}),
|
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id}),
|
||||||
objective: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveNode', id: args.id}),
|
objective: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveNode', id: args.id}),
|
||||||
objectiveGroup: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveGroupNode', 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
|
reflection
|
||||||
nextSteps
|
nextSteps
|
||||||
documentUrl
|
documentUrl
|
||||||
created
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ mutation AddProjectEntryMutation($input: AddProjectEntryInput!) {
|
||||||
addProjectEntry(input: $input) {
|
addProjectEntry(input: $input) {
|
||||||
projectEntry {
|
projectEntry {
|
||||||
...ProjectEntryParts
|
...ProjectEntryParts
|
||||||
|
created
|
||||||
}
|
}
|
||||||
errors
|
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 {
|
edges {
|
||||||
node {
|
node {
|
||||||
...ProjectEntryParts
|
...ProjectEntryParts
|
||||||
|
created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project__content">
|
<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>
|
<project-entry v-bind="entry" v-for="(entry, index) in project.entries" :key="index"></project-entry>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ export default new Vuex.Store({
|
||||||
objectiveGroupType: '',
|
objectiveGroupType: '',
|
||||||
currentObjectiveGroup: '',
|
currentObjectiveGroup: '',
|
||||||
parentProject: null,
|
parentProject: null,
|
||||||
|
currentProjectEntry: null,
|
||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
infographic: {
|
infographic: {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
|
@ -41,6 +42,7 @@ export default new Vuex.Store({
|
||||||
scrollToAssignmentId: state => state.scrollToAssignmentId,
|
scrollToAssignmentId: state => state.scrollToAssignmentId,
|
||||||
scrollToAssignmentReady: state => state.scrollToAssignmentReady,
|
scrollToAssignmentReady: state => state.scrollToAssignmentReady,
|
||||||
scrollingToAssignment: state => state.scrollingToAssignment,
|
scrollingToAssignment: state => state.scrollingToAssignment,
|
||||||
|
currentProjectEntry: state => state.currentProjectEntry,
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -61,6 +63,7 @@ export default new Vuex.Store({
|
||||||
commit('setObjectiveGroupType', '');
|
commit('setObjectiveGroupType', '');
|
||||||
commit('setCurrentObjectiveGroup', '');
|
commit('setCurrentObjectiveGroup', '');
|
||||||
commit('setParentProject', null);
|
commit('setParentProject', null);
|
||||||
|
commit('setCurrentProjectEntry', null);
|
||||||
commit('setImageUrl', '');
|
commit('setImageUrl', '');
|
||||||
commit('setInfographic', {
|
commit('setInfographic', {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
|
@ -110,6 +113,10 @@ export default new Vuex.Store({
|
||||||
commit('setParentProject', payload);
|
commit('setParentProject', payload);
|
||||||
dispatch('showModal', 'new-project-entry-wizard');
|
dispatch('showModal', 'new-project-entry-wizard');
|
||||||
},
|
},
|
||||||
|
editProjectEntry({commit, dispatch}, payload) {
|
||||||
|
commit('setCurrentProjectEntry', payload);
|
||||||
|
dispatch('showModal', 'edit-project-entry-wizard');
|
||||||
|
},
|
||||||
showFullscreenImage({commit, dispatch}, payload) {
|
showFullscreenImage({commit, dispatch}, payload) {
|
||||||
commit('setImageUrl', payload);
|
commit('setImageUrl', payload);
|
||||||
dispatch('showModal', 'fullscreen-image');
|
dispatch('showModal', 'fullscreen-image');
|
||||||
|
|
@ -183,6 +190,9 @@ export default new Vuex.Store({
|
||||||
setParentProject(state, payload) {
|
setParentProject(state, payload) {
|
||||||
state.parentProject = payload;
|
state.parentProject = payload;
|
||||||
},
|
},
|
||||||
|
setCurrentProjectEntry(state, payload) {
|
||||||
|
state.currentProjectEntry = payload;
|
||||||
|
},
|
||||||
setImageUrl(state, payload) {
|
setImageUrl(state, payload) {
|
||||||
state.imageUrl = 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
|
from portfolio.serializers import ProjectSerializer, ProjectEntrySerializer
|
||||||
|
|
||||||
|
|
||||||
# class Mutation(relay.ClientIDMutation):
|
def check_owner(user, project):
|
||||||
# class Meta:
|
return user.id != project.student.id
|
||||||
# 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()])
|
|
||||||
|
|
||||||
class MutateProject(relay.ClientIDMutation):
|
class MutateProject(relay.ClientIDMutation):
|
||||||
errors = graphene.List(graphene.String)
|
errors = graphene.List(graphene.String)
|
||||||
|
|
@ -97,12 +78,13 @@ class MutateProjectEntry(relay.ClientIDMutation):
|
||||||
if data.get('project') is not None:
|
if data.get('project') is not None:
|
||||||
project = get_object(Project, data.get('project'))
|
project = get_object(Project, data.get('project'))
|
||||||
data['project'] = project.id
|
data['project'] = project.id
|
||||||
|
if check_owner(info.context.user, project):
|
||||||
if info.context.user.id != project.student.id:
|
return cls(project_entry=None, errors=['not allowed'])
|
||||||
return cls(project_entry=None, errors=['not allowed'])
|
|
||||||
|
|
||||||
if data.get('id') is not None:
|
if data.get('id') is not None:
|
||||||
entity = get_object(ProjectEntry, data['id'])
|
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)
|
serializer = ProjectEntrySerializer(entity, data=data, partial=True)
|
||||||
else:
|
else:
|
||||||
serializer = ProjectEntrySerializer(data=data)
|
serializer = ProjectEntrySerializer(data=data)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue