diff --git a/app/controllers/client.js b/app/controllers/client.js index aa772c6..a05cc65 100644 --- a/app/controllers/client.js +++ b/app/controllers/client.js @@ -47,6 +47,13 @@ export default class ClientController extends SiteController { router.param('clientId', this.populateClientId.bind(this)); router.param('projectId', this.populateProjectId.bind(this)); + router.post( + '/:clientId/project/:projectId', + limiterService.create(limiterConfig.postProjectUpdate), + checkClientOwnership, + this.postProjectUpdate.bind(this), + ); + router.post( '/:clientId/project', limiterService.create(limiterConfig.postProjectCreate), @@ -67,6 +74,13 @@ export default class ClientController extends SiteController { this.getProjectCreate.bind(this), ); + router.get( + '/:clientId/project/:projectId/edit', + limiterService.create(limiterConfig.getProjectEditor), + checkProjectOwnership, + this.getProjectEditor.bind(this), + ); + router.get( '/:clientId/project/:projectId', limiterService.create(limiterConfig.getProjectView), @@ -130,6 +144,17 @@ export default class ClientController extends SiteController { } } + async postProjectUpdate (req, res, next) { + const { client: clientService } = this.dtp.services; + try { + await clientService.updateProject(res.locals.project, req.body); + res.redirect(`/client/${res.locals.client._id}/project/${res.locals.project._id}`); + } catch (error) { + this.log.error('failed to update client project', { error }); + return next(error); + } + } + async postProjectCreate (req, res, next) { const { client: clientService } = this.dtp.services; try { @@ -153,7 +178,11 @@ export default class ClientController extends SiteController { } async getProjectCreate (req, res) { - res.render('client/project/create'); + res.render('client/project/editor'); + } + + async getProjectEditor (req, res) { + res.render('client/project/editor'); } async getProjectView (req, res, next) { diff --git a/app/models/client-project.js b/app/models/client-project.js index c12115c..48b9dc4 100644 --- a/app/models/client-project.js +++ b/app/models/client-project.js @@ -14,6 +14,7 @@ const ClientProjectSchema = new Schema({ name: { type: String, required: true }, description: { type: String }, hourlyRate: { type: Number, required: true }, + hoursLimit: { type: Number, default: 0, required: true }, }); export default mongoose.model('ClientProject', ClientProjectSchema); \ No newline at end of file diff --git a/app/services/client.js b/app/services/client.js index d641109..849c5d7 100644 --- a/app/services/client.js +++ b/app/services/client.js @@ -79,17 +79,37 @@ export default class ClientService extends SiteService { const project = new ClientProject(); project.user = client.user._id; project.client = client._id; + project.name = textService.filter(projectDefinition.name); if (projectDefinition.description && (projectDefinition.description.length > 0)) { project.description = textService.filter(projectDefinition.description); } + project.hourlyRate = projectDefinition.hourlyRate; + project.hoursLimit = projectDefinition.hoursLimit; await project.save(); return project.toObject(); } + async updateProject (project, projectDefinition) { + const { text: textService } = this.dtp.services; + const update = { $set: { }, $unset: { } }; + + if (projectDefinition.name !== project.name) { + update.$set.name = textService.filter(projectDefinition.name); + } + if (projectDefinition.description !== project.description) { + update.$set.description = textService.filter(projectDefinition.description); + } + + update.$set.hourlyRate = projectDefinition.hourlyRate; + update.$set.hoursLimit = projectDefinition.hoursLimit; + + await ClientProject.updateOne({ _id: project._id }, update); + } + async getProjectById (projectId) { const project = ClientProject .findOne({ _id: projectId }) diff --git a/app/views/client/project/create.pug b/app/views/client/project/create.pug deleted file mode 100644 index e200717..0000000 --- a/app/views/client/project/create.pug +++ /dev/null @@ -1,29 +0,0 @@ -extends ../../layout/main -block view-content - - section.uk-section.uk-section-default.uk-section-small - .uk-container - - .uk-margin - form(method="POST", action= `/client/${client._id}/project`).uk-form - .uk-card.uk-card-default.uk-card-small - .uk-card-header - div(uk-grid).uk-grid-small.uk-flex-middle - .uk-width-expand - h1.uk-card-title New Project - .uk-width-auto - div client: #{client.name} - - .uk-card-body - .uk-margin - label(for="name").uk-form-label Project name - input(id="name", name="name", type="text", placeholder="Enter client name").uk-input - .uk-margin - label(for="description").uk-form-label Project description - textarea(id="name", name="description", rows=4, placeholder="Enter client description").uk-textarea.uk-resize-vertical - .uk-margin - label(for="hourly-rate").uk-form-label Hourly rate - input(id="hourly-rate", name="hourlyRate", type="number", placeholder="Enter hourly rate").uk-textarea.uk-resize-vertical - - .uk-card-footer.uk-flex.uk-flex-right - button(type="submit").uk-button.uk-button-default.uk-border-rounded Create Project \ No newline at end of file diff --git a/app/views/client/project/editor.pug b/app/views/client/project/editor.pug new file mode 100644 index 0000000..6ecca2a --- /dev/null +++ b/app/views/client/project/editor.pug @@ -0,0 +1,58 @@ +extends ../../layout/main +block view-content + + section.uk-section.uk-section-default.uk-section-small + .uk-container + + .uk-margin + - var actionUrl = !!project ? `/client/${client._id}/project/${project._id}` : `/client/${client._id}/project`; + form(method="POST", action= actionUrl).uk-form + .uk-card.uk-card-default.uk-card-small + .uk-card-header + div(uk-grid).uk-grid-small.uk-flex-middle + .uk-width-expand + h1.uk-card-title #[span= !!project ? "Edit" : "New"] Project + .uk-width-auto + div #{client.name} + + .uk-card-body + .uk-margin + label(for="name").uk-form-label Project name + input( + id="name", + name="name", + type="text", + value= !!project ? project.name : undefined, + placeholder="Enter client name", + ).uk-input + .uk-margin + label(for="description").uk-form-label Project description + textarea( + id="name", + name="description", + rows=4, + placeholder="Enter client description" + ).uk-textarea.uk-resize-vertical= !!project ? project.description : "" + .uk-margin + div(uk-grid) + .uk-width-1-2 + label(for="hourly-rate").uk-form-label Hourly rate + input( + id="hourly-rate", + name="hourlyRate", + type="number", + value= !!project ? project.hourlyRate : undefined, + placeholder="Enter hourly rate", + ).uk-input + .uk-width-1-2 + label(for="hours-limit").uk-form-label Max. hours per week + input( + id="hours-limit", + name="hoursLimit", + type="number", + value= value= !!project ? project.hoursLimit : 40, + placeholder="Enter max. hours/week", + ).uk-input + + .uk-card-footer.uk-flex.uk-flex-right + button(type="submit").uk-button.uk-button-default.uk-border-rounded #[span= !!project ? "Update" : "Create"] Project \ No newline at end of file diff --git a/app/views/client/project/view.pug b/app/views/client/project/view.pug index e986db4..24c3968 100644 --- a/app/views/client/project/view.pug +++ b/app/views/client/project/view.pug @@ -10,6 +10,10 @@ block view-content div(uk-grid).uk-grid-small.uk-flex-middle .uk-width-expand h1.uk-margin-remove= project.name + .uk-width-auto + a(href=`/client/${client._id}/project/${project._id}/edit`).uk-button.uk-button-default.uk-button-small + i.fa-solid.fa-cog + span.uk-margin-small-left Settings .uk-width-auto .uk-text-bold a(href=`/client/${client._id}`).uk-link-reset= client.name diff --git a/app/views/client/view.pug b/app/views/client/view.pug index 0286d00..7904c65 100644 --- a/app/views/client/view.pug +++ b/app/views/client/view.pug @@ -11,7 +11,9 @@ block view-content if client.description div= client.description .uk-width-auto - a(href=`/client/${client._id}/project/create`).uk-button.uk-button-default.uk-border-rounded Add Project + a(href=`/client/${client._id}/project/create`).uk-button.uk-button-default.uk-button-small + i.fa-solid.fa-plus + span.uk-margin-small-left Add Project .uk-margin h2 Projects diff --git a/config/limiter.js b/config/limiter.js index dd4a343..f3cdc2a 100644 --- a/config/limiter.js +++ b/config/limiter.js @@ -71,6 +71,11 @@ export default { * ClientController */ client: { + postProjectUpdate: { + total: 100, + expire: ONE_HOUR, + message: "You are updating projects too quickly", + }, postProjectCreate: { total: 10, expire: ONE_HOUR, @@ -86,25 +91,30 @@ export default { expire: ONE_HOUR, message: "You are creating projects too quickly", }, + getProjectEditor: { + total: 250, + expire: ONE_HOUR, + message: "You are editing projects too quickly", + }, getProjectView: { - total: 100, + total: 250, expire: ONE_HOUR, message: "You are viewing projects too quickly", }, getClientCreate: { total: 10, expire: ONE_HOUR, - message: "You are too quickly", + message: "You are creating clients too quickly", }, getClientView: { - total: 10, + total: 250, expire: ONE_HOUR, - message: "You are too quickly", + message: "You are viewing clients too quickly", }, getHome: { - total: 10, + total: 250, expire: ONE_HOUR, - message: "You are too quickly", + message: "You are accessing the clients dashboard too quickly", }, },