Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/components/projects/Project/Project.vue
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
this.modalVisible = false;
},
deleteProject() {
this.$store.dispatch(DELETE_PROJECT, this.project.uniqueId);
this.$store.dispatch(DELETE_PROJECT, this.project.id);
},
openWithFilter(key: string, value: string) {
this.$router.push({name: Routes.Projects, query: {[key]: value.toString()}});
Expand Down
25 changes: 19 additions & 6 deletions client/src/components/projects/ProjectChange/ProjectChange.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
<template>
<CommonModal @exit="goBack">
<template slot="modal-title">{{ id ? 'Edit' : 'Create' }} project</template>
<template slot="modal-title">
{{ id ? 'Edit' : 'Create' }} project
<ErrorMessage v-if="errorMessage">
<p class="title is-size-18">
{{errorMessage}}
</p>
</ErrorMessage>
</template>
<p
class="manage-user-subtitle common-modal-subtitle has-text-centered is-size-5 is-size-6-mobile"
slot="modal-subtitle">
Please {{ id ? 'edit' : 'create' }} project information here
</p>
<template slot="modal-content">

<div class="form-container project-change">
<div class="field centered-margin">
<Stepper
Expand Down Expand Up @@ -325,16 +333,16 @@
} from '../../../store/modules/projects/action-types';
import {FETCH_ROLES, FETCH_EMPLOYEES} from '../../../store/modules/employees/action-types';
import {
ADDONS,
ADDONS, ERROR_MESSAGE,
PROJECT_CUSTOMERS, PROJECT_DESCRIPTION, PROJECT_DOMAIN_ID, PROJECT_EMPLOYEES, PROJECT_END_DATE, PROJECT_IMAGE,
PROJECT_NAME,
PROJECT_PROGRAM_ID, PROJECT_PSS, PROJECT_SCHEDULES, PROJECT_START_DATE, PROJECT_TECHNOLOGIES, PROJECT_TYPE_ID
} from '../../../store/modules/projects/getter-types';
} from "../../../store/modules/projects/getter-types";
import {ITechnology} from '../../../shared/interfaces/ITechnology';
import {TECHNOLOGIES} from '../../../store/modules/technologies/getter-types';
import {FETCH_TECHNOLOGIES} from '../../../store/modules/technologies/action-types';
import {
INCREMENT_VERSION,
INCREMENT_VERSION, SET_ERROR_MESSAGE,
SET_PROJECT,
SET_PROJECT_CUSTOMERS,
SET_PROJECT_DESCRIPTION,
Expand All @@ -347,7 +355,7 @@
SET_PROJECT_START_DATE,
SET_PROJECT_TECHNOLOGIES,
SET_PROJECT_TYPE
} from '../../../store/modules/projects/mutation-types';
} from "../../../store/modules/projects/mutation-types";
import {Util} from '../../../shared/classes/Util';
import {Types} from '../../../store/modules/projects/constant-types';
import {ICustomer} from '../../../shared/interfaces/ICustomer';
Expand Down Expand Up @@ -390,6 +398,9 @@
}
},
computed: {
errorMessage(): string {
return this.$store.getters[ERROR_MESSAGE];
},
name: Util.mapTwoWay<string>(PROJECT_NAME, SET_PROJECT_NAME),
description: Util.mapTwoWay<string>(PROJECT_DESCRIPTION, SET_PROJECT_DESCRIPTION),
pss: Util.mapTwoWay<number>(PROJECT_PSS, SET_PROJECT_PSS),
Expand Down Expand Up @@ -448,7 +459,7 @@
if (name === '') return true;

return this.id
? !await this.$store.dispatch(CHECK_PROJECT_EXISTENCE_UPDATE, {name, id: this.id})
? !await this.$store.dispatch(CHECK_PROJECT_EXISTENCE_UPDATE, name)
: !await this.$store.dispatch(CHECK_PROJECT_EXISTENCE, name);
}
},
Expand Down Expand Up @@ -523,6 +534,7 @@
}
},
goBack() {
this.$store.commit(SET_ERROR_MESSAGE, null);
if (this.id) {
this.$store.dispatch(UPDATE_PROJECT_IMAGE, {id: this.id, image: this.image});
this.$router.push({name: Routes.Project, params: {id: this.id}});
Expand All @@ -541,6 +553,7 @@
this.$store.dispatch(REMOVE_PROJECT_IMAGE, this.$store.getters[PROJECT_IMAGE]);
},
saveProject() {
this.$store.commit(SET_ERROR_MESSAGE, null);
if (this.id) {
this.$store.commit(INCREMENT_VERSION);
this.$store.dispatch(EDIT_PROJECT);
Expand Down
18 changes: 11 additions & 7 deletions client/src/store/modules/projects/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import {
FINISH_LOADING, RESET_FILTERS, SET_COMPLETION_VALUE,
SET_CUSTOMERS,
SET_DOMAINS,
SET_DOMAINS, SET_ERROR_MESSAGE,
SET_FILTER_VALUE,
SET_LINES,
SET_PROGRAMS,
Expand Down Expand Up @@ -84,19 +84,23 @@ export const actions: ActionTree<IProjectState, {}> = {
.then(response => response.data);
},

[CHECK_PROJECT_EXISTENCE_UPDATE]({commit}, payload: {name:string, id:string}) {
return ProjectService.doesProjectWithIdExist(payload.name, payload.id)
[CHECK_PROJECT_EXISTENCE_UPDATE]({commit, state}, name: string) {
return ProjectService.doesProjectWithIdExist(name, state.project.uniqueId)
.then(response => response.data);
},

[CREATE_PROJECT]({state}) {
[CREATE_PROJECT]({state, commit}) {
return ProjectService.createProject(state.project)
.then(() => router.push({name: Routes.Projects}));
.then(
() => router.push({name: Routes.Projects}),
error => {commit(SET_ERROR_MESSAGE, error.response.data.errors.latest.msg)});
},

[EDIT_PROJECT]({state}) {
[EDIT_PROJECT]({state, commit}) {
return ProjectService.editProject(state.project)
.then((response) => router.push({name: Routes.Project, params: { id: String(response.data.id) }}));
.then(
(response) => router.push({name: Routes.Project, params: { id: String(response.data.id) }}),
error => {commit(SET_ERROR_MESSAGE, error.response.data.errors.latest.msg)});
},

[DELETE_PROJECT]({commit}, id:string) {
Expand Down
2 changes: 2 additions & 0 deletions client/src/store/modules/projects/getter-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const SORT = 'SORT';
export const SORT_REVERSE = 'SORT_REVERSE';
export const SORT_FIELD_NAME = 'SORT_FIELD_NAME';

export const ERROR_MESSAGE = 'ERROR_MESSAGE';

export const PROJECT_NAME = 'PROJECT_NAME';
export const PROJECT_PROGRAM_ID = 'PROJECT_PROGRAM_ID';
export const PROJECT_LINE = 'PROJECT_LINE';
Expand Down
5 changes: 3 additions & 2 deletions client/src/store/modules/projects/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
PROJECT_DESCRIPTION,
PROJECT_CUSTOMERS,
PROJECT_SCHEDULES,
PROJECT_TECHNOLOGIES_GROUPED, PROJECT_PSS, COMPLETION, PROJECT_EMPLOYEES, PROJECT_IMAGE, FILTER_EMPTY
PROJECT_TECHNOLOGIES_GROUPED, PROJECT_PSS, COMPLETION, PROJECT_EMPLOYEES, PROJECT_IMAGE, FILTER_EMPTY, ERROR_MESSAGE
} from './getter-types';
import {TECHNOLOGIES} from '../technologies/getter-types';
import {IProjectFilter, IProjectFilterCheck} from '../../../shared/interfaces/shared/IProjectFilter';
Expand Down Expand Up @@ -174,5 +174,6 @@ export const getters: GetterTree<IProjectState, {}> = {
[PROJECT_SCHEDULES]: state => state.project.schedules,
[PROJECT_TECHNOLOGIES]: state => state.project.technologies,
[PROJECT_EMPLOYEES]: state => state.project.schedules.map(schedule => schedule.employee),
[PROJECT_IMAGE]: state => state.project.image
[PROJECT_IMAGE]: state => state.project.image,
[ERROR_MESSAGE]: state => state.errorMessage
};
4 changes: 3 additions & 1 deletion client/src/store/modules/projects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface IProjectState {
sort: string;
sortReverse: boolean;
projectExists: boolean;
errorMessage: string;
}


Expand All @@ -55,7 +56,8 @@ const projectState: Module<IProjectState, {}> = {
autocompleteSearch: '',
sort: 'name',
sortReverse: true,
projectExists: false
projectExists: false,
errorMessage: ''
},
mutations,
actions,
Expand Down
2 changes: 2 additions & 0 deletions client/src/store/modules/projects/mutation-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const SET_SORT_REVERSE_VALUE = 'SET_SORT_REVERSE_VALUE';
export const FINISH_LOADING = 'projects/FINISH_LOADING';
export const INCREMENT_VERSION = 'projects/INCREMENT_VERSION';

export const SET_ERROR_MESSAGE = 'projects/SET_ERROR_MESSAGE';

export const SET_PROJECT_NAME = 'SET_PROJECT_NAME';
export const SET_PROJECT_PROGRAM = 'SET_PROJECT_PROGRAM';
export const SET_SCHEDULE = 'SET_SCHEDULE';
Expand Down
5 changes: 4 additions & 1 deletion client/src/store/modules/projects/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
SET_SORT_VALUE,
SET_SORT_REVERSE_VALUE,
RESET_FILTERS,
SET_PROJECT_IMAGE, SET_SCHEDULE, INCREMENT_VERSION
SET_PROJECT_IMAGE, SET_SCHEDULE, INCREMENT_VERSION, SET_ERROR_MESSAGE

} from './mutation-types';

Expand Down Expand Up @@ -268,6 +268,9 @@ export const mutations: MutationTree<IProjectState> = {
[REMOVE_PROJECT_SCHEDULE](state, targetId: string) {
state.project.schedules = state.project.schedules.filter(schedule => schedule.employee.id !== targetId);
},
[SET_ERROR_MESSAGE](state, payload: string) {
state.errorMessage = payload;
},
[RESET_FILTERS](state) {
state.filter = {};
state.search = '';
Expand Down
4 changes: 2 additions & 2 deletions client/src/store/modules/projects/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export class ProjectService {
return axios.get<Boolean>(`${routes.DOES_PROJECT_EXIST}${name}`);
}

static doesProjectWithIdExist(name: string = '', id:string) {
return axios.get<Boolean>(`${routes.DOES_PROJECT_EXIST_WITH_ID}${name}/${id}`);
static doesProjectWithIdExist(name: string = '', uniqueId:string) {
return axios.get<Boolean>(`${routes.DOES_PROJECT_EXIST_WITH_ID}${name}/${uniqueId}`);
}

static getProjects() {
Expand Down
15 changes: 8 additions & 7 deletions server/features/project/project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ router.get('/projects', (req, res) =>
projectService.getProjects()
.then(Util.handleData(res)));


router.get('/projects/addons', (req, res) =>
projectAddonsService.getProjectFilterModel()
.then(Util.handleData(res)));
Expand All @@ -28,26 +29,26 @@ router.get('/projects/exists/:name', (req, res) =>
projectService.doesProjectWithNameExist(req.params.name)
.then(doesExist => res.status(200).send(doesExist)));

router.get('/projects/update/exists/:name/:id', (req, res) =>
projectService.doesProjectWithNameAndIdExist(req.params.name, req.params.id)
router.get('/projects/update/exists/:name/:uniqueId', (req, res) =>
projectService.doesProjectWithNameAndIdExist(req.params.name, req.params.uniqueId)
.then(doesExist => res.status(200).send(doesExist)));

// POST Requests
router.post('/projects/create', projectValidator.createValidators(), (req, res) =>
Util.handleValidation(req, res, () =>
projectService.doesProjectWithNameExist(req.body.name).then(doesExist =>
doesExist ?
Util.handleConflict(res, 'Project already exists or was archieved') :
Util.handleConflict(res, 'Project already exists') :
projectService.createProject(req.body)
.then(Util.handleData(res)))
));

router.post('/projects/update', projectValidator.createValidators(), (req, res) =>
Util.handleValidation(req, res, () =>
projectService.isProjectLatest(req.body.id).then(isLatest =>
projectService.isProjectLatest(req.body.id, req.body.uniqueId).then(isLatest =>
!isLatest ?
Util.handleConflict(res, 'Somebody has already updated the project in background') :
projectService.updateProject(req.body)
projectService.createProject(req.body)
.then(Util.handleData(res)))
));

Expand All @@ -67,9 +68,9 @@ router.put('/projects/update/image', (req, res) =>
)


router.delete('/projects/delete/:uniqueId', projectValidator.deleteValidators(), (req, res) =>
router.delete('/projects/delete/:id', projectValidator.deleteValidators(), (req, res) =>
Util.handleValidation(req, res, () =>
projectService.deleteProject(req.params.uniqueId)
projectService.deleteProject(req.params.id)
.then(() => res.status(200).json({message: 'ok'}))
));

Expand Down
68 changes: 40 additions & 28 deletions server/features/project/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ import {ProjectCustomer} from '../../models/ProjectCustomer';
import Guid from '../../shared/Guid';

const projectService = {

getProjects: () => Project.scope([
Scopes.PROJECT_LIST,
Scopes.ACTUAL_PROJECTS
])
.findAll({ order: [ ['updatedAt', 'DESC']]
}),

getProjects: () => sequelize.query('SELECT id from "Projects" as pr' +
' inner join (' +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add todo to possibly rewrite later

' Select "uniqueId", max("updatedAt") as maxDate' +
' from "Projects"' +
' group by "uniqueId"' +
' ) as innerP' +
' on pr."uniqueId" = innerP."uniqueId"' +
' AND pr."updatedAt" = innerP.maxDate', { type: sequelize.QueryTypes.SELECT})
.then(maxDateProjects => Project.scope([Scopes.PROJECT_LIST ])
.findAll({
where: {id: maxDateProjects.map(project => project.id)}
})),

// GET single project by id
getProject: id => Project.scope([
Scopes.FULL,
Scopes.ACTUAL_PROJECTS
Scopes.FULL
])
.findOne({
where: {
Expand All @@ -43,29 +48,36 @@ const projectService = {
console.log(error);
}),

doesProjectWithNameExist: name => Project.count({where: {name: name, ishistory: false }})
doesProjectWithNameExist: name => Project.count({where: {name: name }})
.then(count => count !== 0)
.catch(error => {
console.log(error);
}),

doesProjectWithNameAndIdExist: (name, id) => Project.count({
where: {name: name, ishistory: false, id: {[sequelize.Op.ne]: id}
doesProjectWithNameAndIdExist: (name, uniqueId) => Project.count({
where: {
name: name,
uniqueId: {[sequelize.Op.ne]: uniqueId}
}})
.then(count => count !== 0)
.catch(error => {
console.log(error);
}),

// GET Get latest project
isProjectLatest: id => Project.findOne({
where: {
id: id
}
}).then(project => !project.ishistory)
.catch(error => {
console.log(error);
}),
isProjectLatest: async (id, uniqueId) => {
const maxDate = await Project.max('updatedAt', {where: {uniqueId}});
const maxProject = await Project.findOne({
where: {
uniqueId,
updatedAt: maxDate
}
});


return maxProject.id === id;
},


// POST create new project
createProject: project => Project.create({
Expand Down Expand Up @@ -128,15 +140,15 @@ const projectService = {
}
},

deleteProject: async uniqueId => {
deleteProject: async id => {
try {
const projects = await projectService.getProjectsByUniqueId(uniqueId);
projects.forEach(project => {
project.$remove('technologies', project.technologies);
Schedule.destroy({where: {projectId: project.id}});
ProjectCustomer.destroy({where: {projectId: project.id}});
});
return await Project.destroy({where: {uniqueId: uniqueId}, cascade: true});
const project = await projectService.getProject(id);

project.$remove('technologies', project.technologies);
Schedule.destroy({where: {projectId: project.id}});
ProjectCustomer.destroy({where: {projectId: project.id}});

return await Project.destroy({where: {id}, cascade: true});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't cascade remove Schedule and ProjectCustomer itself?

} catch (error) {
throw new Error(error);
}
Expand Down
2 changes: 1 addition & 1 deletion server/features/project/project.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const projectValidator = {
],

deleteValidators: () => [
check('uniqueId', 'Project uniqueId is required').exists().isLength({min: 1, max: 100000})
check('id', 'Project id is required').exists().isLength({min: 1, max: 100000})
]
};

Expand Down