Browse Source

added hoursLimit and project editor/update

develop
Rob Colbert 12 months ago
parent
commit
8d47f90d29
  1. 31
      app/controllers/client.js
  2. 1
      app/models/client-project.js
  3. 20
      app/services/client.js
  4. 29
      app/views/client/project/create.pug
  5. 58
      app/views/client/project/editor.pug
  6. 4
      app/views/client/project/view.pug
  7. 4
      app/views/client/view.pug
  8. 22
      config/limiter.js

31
app/controllers/client.js

@ -47,6 +47,13 @@ export default class ClientController extends SiteController {
router.param('clientId', this.populateClientId.bind(this)); router.param('clientId', this.populateClientId.bind(this));
router.param('projectId', this.populateProjectId.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( router.post(
'/:clientId/project', '/:clientId/project',
limiterService.create(limiterConfig.postProjectCreate), limiterService.create(limiterConfig.postProjectCreate),
@ -67,6 +74,13 @@ export default class ClientController extends SiteController {
this.getProjectCreate.bind(this), this.getProjectCreate.bind(this),
); );
router.get(
'/:clientId/project/:projectId/edit',
limiterService.create(limiterConfig.getProjectEditor),
checkProjectOwnership,
this.getProjectEditor.bind(this),
);
router.get( router.get(
'/:clientId/project/:projectId', '/:clientId/project/:projectId',
limiterService.create(limiterConfig.getProjectView), 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) { async postProjectCreate (req, res, next) {
const { client: clientService } = this.dtp.services; const { client: clientService } = this.dtp.services;
try { try {
@ -153,7 +178,11 @@ export default class ClientController extends SiteController {
} }
async getProjectCreate (req, res) { 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) { async getProjectView (req, res, next) {

1
app/models/client-project.js

@ -14,6 +14,7 @@ const ClientProjectSchema = new Schema({
name: { type: String, required: true }, name: { type: String, required: true },
description: { type: String }, description: { type: String },
hourlyRate: { type: Number, required: true }, hourlyRate: { type: Number, required: true },
hoursLimit: { type: Number, default: 0, required: true },
}); });
export default mongoose.model('ClientProject', ClientProjectSchema); export default mongoose.model('ClientProject', ClientProjectSchema);

20
app/services/client.js

@ -79,17 +79,37 @@ export default class ClientService extends SiteService {
const project = new ClientProject(); const project = new ClientProject();
project.user = client.user._id; project.user = client.user._id;
project.client = client._id; project.client = client._id;
project.name = textService.filter(projectDefinition.name); project.name = textService.filter(projectDefinition.name);
if (projectDefinition.description && (projectDefinition.description.length > 0)) { if (projectDefinition.description && (projectDefinition.description.length > 0)) {
project.description = textService.filter(projectDefinition.description); project.description = textService.filter(projectDefinition.description);
} }
project.hourlyRate = projectDefinition.hourlyRate; project.hourlyRate = projectDefinition.hourlyRate;
project.hoursLimit = projectDefinition.hoursLimit;
await project.save(); await project.save();
return project.toObject(); 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) { async getProjectById (projectId) {
const project = ClientProject const project = ClientProject
.findOne({ _id: projectId }) .findOne({ _id: projectId })

29
app/views/client/project/create.pug

@ -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

58
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

4
app/views/client/project/view.pug

@ -10,6 +10,10 @@ block view-content
div(uk-grid).uk-grid-small.uk-flex-middle div(uk-grid).uk-grid-small.uk-flex-middle
.uk-width-expand .uk-width-expand
h1.uk-margin-remove= project.name 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-width-auto
.uk-text-bold .uk-text-bold
a(href=`/client/${client._id}`).uk-link-reset= client.name a(href=`/client/${client._id}`).uk-link-reset= client.name

4
app/views/client/view.pug

@ -11,7 +11,9 @@ block view-content
if client.description if client.description
div= client.description div= client.description
.uk-width-auto .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 .uk-margin
h2 Projects h2 Projects

22
config/limiter.js

@ -71,6 +71,11 @@ export default {
* ClientController * ClientController
*/ */
client: { client: {
postProjectUpdate: {
total: 100,
expire: ONE_HOUR,
message: "You are updating projects too quickly",
},
postProjectCreate: { postProjectCreate: {
total: 10, total: 10,
expire: ONE_HOUR, expire: ONE_HOUR,
@ -86,25 +91,30 @@ export default {
expire: ONE_HOUR, expire: ONE_HOUR,
message: "You are creating projects too quickly", message: "You are creating projects too quickly",
}, },
getProjectEditor: {
total: 250,
expire: ONE_HOUR,
message: "You are editing projects too quickly",
},
getProjectView: { getProjectView: {
total: 100, total: 250,
expire: ONE_HOUR, expire: ONE_HOUR,
message: "You are viewing projects too quickly", message: "You are viewing projects too quickly",
}, },
getClientCreate: { getClientCreate: {
total: 10, total: 10,
expire: ONE_HOUR, expire: ONE_HOUR,
message: "You are too quickly", message: "You are creating clients too quickly",
}, },
getClientView: { getClientView: {
total: 10, total: 250,
expire: ONE_HOUR, expire: ONE_HOUR,
message: "You are too quickly", message: "You are viewing clients too quickly",
}, },
getHome: { getHome: {
total: 10, total: 250,
expire: ONE_HOUR, expire: ONE_HOUR,
message: "You are too quickly", message: "You are accessing the clients dashboard too quickly",
}, },
}, },

Loading…
Cancel
Save