From e5d7ffda4059f7490e78d3562cc4593d1eb010dc Mon Sep 17 00:00:00 2001 From: tangoyankee Date: Tue, 12 Nov 2024 12:11:26 -0500 Subject: [PATCH 01/12] 1254 Create Artifact Record as part of Project Creation - Added artifact creation endpoint and returning artifact id from server to client - Create a form through which users may submit and attach documents (client) - Added validation for required submission of project documents Co-authored-by: horatio Co-authored-by: tangoyankee --- .github/workflows/production.yml | 5 +- .github/workflows/qa.yml | 4 +- .github/workflows/staging.yml | 3 +- .../packages/pas-form/attached-documents.hbs | 1 - client/app/components/projects/new.hbs | 12 +- client/app/components/projects/new.js | 17 ++- .../projects-new-attached-documents.hbs | 23 +++ .../projects/projects-new-attachments.hbs | 133 ++++++++++++++++++ .../projects/projects-new-attachments.js | 63 +++++++++ .../projects-new-project-description.hbs | 24 ++-- client/app/models/artifact.js | 3 + client/app/models/project-new.js | 124 ++++++++++++++++ client/app/routes/projects/new.js | 24 ++-- client/app/services/file-manager.js | 56 +++++--- .../submittable-projects-new-form.js | 6 + .../app/validators/validate-file-presence.js | 9 ++ server/src/artifacts/artifacts.service.ts | 31 ++++ server/src/projects/projects.controller.ts | 1 + server/src/projects/projects.module.ts | 6 +- server/src/projects/projects.service.ts | 8 ++ 20 files changed, 493 insertions(+), 60 deletions(-) create mode 100644 client/app/components/projects/projects-new-attached-documents.hbs create mode 100644 client/app/components/projects/projects-new-attachments.hbs create mode 100644 client/app/components/projects/projects-new-attachments.js create mode 100644 client/app/models/project-new.js create mode 100644 client/app/validators/validate-file-presence.js diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index d85a680f..06c9f178 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -28,7 +28,7 @@ jobs: deploy-server: name: 🚀 Deploy server needs: test-client - environment: + environment: name: production url: https://applicants-api.nycplanningdigital.com runs-on: ubuntu-latest @@ -75,6 +75,7 @@ jobs: HD_PAYMENT_IP_RANGE: ${{ secrets.PAYMENT_IP_RANGE }} HD_PAYMENT_STEP1_URL: ${{ secrets.PAYMENT_STEP1_URL }} HD_RER_FILETYPE_UUID: ${{ secrets.RER_FILETYPE_UUID }} + HD_LETTER_FILETYPE_UUID: ${{ secrets.LETTER_FILETYPE_UUID }} HD_SENTRY_DSN: ${{ secrets.SENTRY_DSN }} HD_SHAREPOINT_CLIENT_ID: ${{ secrets.SHAREPOINT_CLIENT_ID }} HD_SHAREPOINT_CLIENT_SECRET: ${{ secrets.SHAREPOINT_CLIENT_SECRET }} @@ -107,7 +108,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 12.x - - name: Install application dependencies + - name: Install application dependencies working-directory: client run: yarn install --immutable --immutable-cache --check-cache - name: Build client diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index b6c79aad..bb147417 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -72,6 +72,7 @@ jobs: HD_PAYMENT_IP_RANGE: ${{ secrets.PAYMENT_IP_RANGE }} HD_PAYMENT_STEP1_URL: ${{ secrets.PAYMENT_STEP1_URL }} HD_RER_FILETYPE_UUID: ${{ secrets.RER_FILETYPE_UUID }} + HD_LETTER_FILETYPE_UUID: ${{ secrets.LETTER_FILETYPE_UUID }} HD_SHAREPOINT_CLIENT_ID: ${{ secrets.SHAREPOINT_CLIENT_ID }} HD_SHAREPOINT_CLIENT_SECRET: ${{ secrets.SHAREPOINT_CLIENT_SECRET }} HD_SHAREPOINT_CRM_SITE: ${{ secrets.SHAREPOINT_CRM_SITE }} @@ -102,7 +103,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 12.x - - name: Install application dependencies + - name: Install application dependencies working-directory: client run: yarn install --immutable --immutable-cache --check-cache - name: Build client @@ -119,4 +120,3 @@ jobs: --site ${{secrets.NETLIFY_SITE_ID}} \ --auth ${{secrets.NETLIFY_AUTH_TOKEN}} \ --message "${{ github.event.head_commit.message }}" - diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index b2326ace..a36f397c 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -63,6 +63,7 @@ jobs: HD_NYCID_TOKEN_SECRET: ${{ secrets.NYCID_TOKEN_SECRET }} HD_PAPERTRAIL_API_TOKEN: ${{ secrets.PAPERTRAIL_API_TOKEN }} HD_RER_FILETYPE_UUID: ${{ secrets.RER_FILETYPE_UUID }} + HD_LETTER_FILETYPE_UUID: ${{ secrets.LETTER_FILETYPE_UUID }} HD_SHAREPOINT_CLIENT_ID: ${{ secrets.SHAREPOINT_CLIENT_ID }} HD_SHAREPOINT_CLIENT_SECRET: ${{ secrets.SHAREPOINT_CLIENT_SECRET }} HD_SHAREPOINT_CRM_SITE: ${{ secrets.SHAREPOINT_CRM_SITE }} @@ -94,7 +95,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 12.x - - name: Install application dependencies + - name: Install application dependencies working-directory: client run: yarn install - name: Build client diff --git a/client/app/components/packages/pas-form/attached-documents.hbs b/client/app/components/packages/pas-form/attached-documents.hbs index ab10012c..4c3020d7 100644 --- a/client/app/components/packages/pas-form/attached-documents.hbs +++ b/client/app/components/packages/pas-form/attached-documents.hbs @@ -7,7 +7,6 @@ @attribute='documents' @validation={{@form.errors.documents.validation}} /> - + + + + diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index 5ab960b9..54f40237 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -4,6 +4,7 @@ import { inject as service } from '@ember/service'; import SubmittableProjectsNewForm from '../../validations/submittable-projects-new-form'; import { optionset } from '../../helpers/optionset'; import config from '../../config/environment'; +import validateFileUpload from '../../validators/validate-file-presence'; export default class ProjectsNewFormComponent extends Component { validations = { @@ -42,6 +43,17 @@ export default class ProjectsNewFormComponent extends Component { const contactInputs = [primaryContactInput, applicantInput]; + const validationResult = validateFileUpload( + { + message: 'Please upload at least one file before submitting.', + }, + )('documents', this.args.package.documents); + + if (validationResult !== true) { + this.errorMessage = validationResult; + return; + } + try { const contactPromises = contactInputs.map((contact) => this.store.queryRecord('contact', { email: contact.email, @@ -96,13 +108,16 @@ export default class ProjectsNewFormComponent extends Component { dcpApplicanttype: this.args.package.applicantType.code, dcpProjectbrief: this.args.package.projectBrief, _dcpApplicantadministratorCustomerValue: - verifiedPrimaryContact.id, + verifiedPrimaryContact.id, _dcpApplicantCustomerValue: verifiedApplicant.id, }, }, }), }); const { data: project } = await response.json(); + + this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']); + this.router.transitionTo('project', project.id); } catch { /* eslint-disable-next-line no-console */ diff --git a/client/app/components/projects/projects-new-attached-documents.hbs b/client/app/components/projects/projects-new-attached-documents.hbs new file mode 100644 index 00000000..60642d84 --- /dev/null +++ b/client/app/components/projects/projects-new-attached-documents.hbs @@ -0,0 +1,23 @@ +{{#let @form as |form|}} +<@form.Section @title='Attached Documents'> +

+ Please attach the required items listed on the + + Informational Interest Meeting Checklist +  in at least one PDF document. The maximum + size for a document is 50MB. +

+ + + + + + +{{/let}} diff --git a/client/app/components/projects/projects-new-attachments.hbs b/client/app/components/projects/projects-new-attachments.hbs new file mode 100644 index 00000000..b0ed9444 --- /dev/null +++ b/client/app/components/projects/projects-new-attachments.hbs @@ -0,0 +1,133 @@ +
+
+ + Attachments + + + +
    + {{#each @fileManager.existingFiles as |file idx|}} +
  • +
    + +
    + + {{file.timeCreated}} + +
    +
    + +
    +
    +
  • + {{/each}} +
+ + {{#if (or @fileManager.filesToDelete @fileManager.filesToUpload.files)}} + {{#if @fileManager.existingFiles}} +
+ {{/if}} + +
+ To be + {{if @fileManager.filesToUpload.files 'uploaded'}} + {{if (and @fileManager.filesToDelete @fileManager.filesToUpload.files) '/'}} + {{if @fileManager.filesToDelete 'deleted'}} + when you save the project: +
+ {{/if}} + +
    + {{#each @fileManager.filesToDelete as |file idx|}} +
  • +
    +
    + + {{file.name}} + +
    +
    + + TO BE DELETED + +
    +
    + +
    +
    +
  • + {{/each}} +
+ +
    + {{#each @fileManager.filesToUpload.files as |file idx|}} +
  • +
    +
    + + {{file.name}} + +
    +
    + + TO BE ADDED + +
    +
    + +
    +
    +
  • + {{/each}} +
+ + + + Choose Files + + +

+ The size limit for each file is 50 MB. You can upload up to 1 GB of files. +

+
+
\ No newline at end of file diff --git a/client/app/components/projects/projects-new-attachments.js b/client/app/components/projects/projects-new-attachments.js new file mode 100644 index 00000000..5e81ff89 --- /dev/null +++ b/client/app/components/projects/projects-new-attachments.js @@ -0,0 +1,63 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import validateFileUpload from '../../validators/validate-file-presence'; + +/** + * This component wires a fileManager to the attachments UI. + * @param {Artifact Model} artifact + */ +export default class ProjectsNewAttachmentsComponent extends Component { + get fileManager() { + // should be an instance of FileManager + return this.args.fileManager; + } + + @action + markFileForDeletion(file) { + this.fileManager.markFileForDeletion(file); + + this.args.package.documents = this.fileManager.existingFiles; + } + + @action + unmarkFileForDeletion(file) { + this.fileManager.unMarkFileForDeletion(file); + } + + // This action doesn't perform any file selection. + // That part is automatically handled by the + // ember-file-upload addon. + // Here we manually increment the number of files to + // upload to update the fileManager isDirty state. + @action + trackFileForUpload() { + this.fileManager.trackFileForUpload(); + this.args.package.documents = [ + ...this.args.package.documents, + ...this.fileManager.filesToUpload.files, + ]; + } + + @action + deselectFileForUpload(file) { + this.fileManager.deselectFileForUpload(file); + + this.args.package.documents = this.args.package.documents.filter( + (document) => document !== file, + ); + } + + @action + validateFilePresence() { + const validationResult = validateFileUpload({ message: 'One or more document uploads is required.' })( + 'documents', + this.fileManager.filesToUpload.files, + ); + + if (validationResult !== true) { + this.errorMessage = validationResult; + } else { + this.errorMessage = null; + } + } +} diff --git a/client/app/components/projects/projects-new-project-description.hbs b/client/app/components/projects/projects-new-project-description.hbs index ecd8a503..f2a33b31 100644 --- a/client/app/components/projects/projects-new-project-description.hbs +++ b/client/app/components/projects/projects-new-project-description.hbs @@ -1,16 +1,16 @@ {{#let @form as |form|}} - - - - Please replace information in the brackets to the best of your ability. - + + + + Please replace information in the brackets to the best of your ability. + - - + + {{/let}} diff --git a/client/app/models/artifact.js b/client/app/models/artifact.js index f6baabaf..56667272 100644 --- a/client/app/models/artifact.js +++ b/client/app/models/artifact.js @@ -33,6 +33,9 @@ export default class ArtifactModel extends Model { @belongsTo('project', { async: false }) project; + @belongsTo('project-new', { async: false }) + projectNew + @attr() dcpName; diff --git a/client/app/models/project-new.js b/client/app/models/project-new.js new file mode 100644 index 00000000..d49936a4 --- /dev/null +++ b/client/app/models/project-new.js @@ -0,0 +1,124 @@ +import Model, { attr, belongsTo } from '@ember-data/model'; +import { inject as service } from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import FileManager from '../services/file-manager'; + +export default class ProjectNew extends Model { + createFileQueue() { + if (this.fileManager) { + this.fileManager.existingFiles = this.documents; + } else { + const fileQueue = this.fileQueue.create(`artifact${this.id}`); + + this.fileManager = new FileManager( + this.id, + 'artifact', + this.documents, + [], + fileQueue, + this.session, + ); + } + } + + // Since file upload doesn't perform requests through + // an Ember Model save() process, it doesn't automatically + // hydrate the package.adapterError property. When an error occurs + // during upload we have to manually hydrate a custom error property + // to trigger the error box displayed to the user. + @tracked + fileUploadErrors = null; + + @service + session; + + @service + fileQueue; + + @belongsTo('projects', { async: false }) + projects; + + @attr('string', { + defaultValue: '', + }) + projectName; + + @attr('string', { + defaultValue: '', + }) + borough; + + @attr('string', { + defaultValue: '', + }) + applicantType; + + @attr('string', { + defaultValue: '', + }) + primaryContactFirstName; + + @attr('string', { + defaultValue: '', + }) + primaryContactLastName; + + @attr('string', { + defaultValue: '', + }) + primaryContactEmail; + + @attr('string', { + defaultValue: '', + }) + primaryContactPhone; + + @attr('string', { + defaultValue: '', + }) + applicantFirstName; + + @attr('string', { + defaultValue: '', + }) + applicantLastName; + + @attr('string', { + defaultValue: '', + }) + applicantEmail; + + @attr('string', { + defaultValue: '', + }) + applicantPhone; + + @attr('string', { + defaultValue: '', + }) + projectBrief; + + @attr('string', { + defaultValue: () => [], + }) + documents; + + async saveAttachedFiles(instanceId) { + try { + await this.fileManager.save(instanceId); + } catch (e) { + console.log('Error saving files: ', e); // eslint-disable-line no-console + + // See comment on the tracked fileUploadError property + // definition above. + this.fileUploadErrors = [ + { + code: 'UPLOAD_DOC_FAILED', + title: 'Failed to upload documents', + detail: + 'An error occured while uploading your documents. Please refresh and retry.', + }, + ]; + } + } +} diff --git a/client/app/routes/projects/new.js b/client/app/routes/projects/new.js index 731aa2d8..f49105d6 100644 --- a/client/app/routes/projects/new.js +++ b/client/app/routes/projects/new.js @@ -1,25 +1,21 @@ import Route from '@ember/routing/route'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; +import { inject as service } from '@ember/service'; export default class ProjectsNewRoute extends Route.extend( AuthenticatedRouteMixin, ) { authenticationRoute = '/'; + @service + store + async model() { - return { - projectName: '', - borough: '', - applicantType: '', - primaryContactFirstName: '', - primaryContactLastName: '', - primaryContactEmail: '', - primaryContactPhone: '', - applicantFirstName: '', - applicantLastName: '', - applicantEmail: '', - applicantPhone: '', - projectBrief: '', - }; + const projectsNewForm = this.store.createRecord('project-new'); + + projectsNewForm.id = `new${self.crypto.randomUUID()}`; + projectsNewForm.createFileQueue(); + + return projectsNewForm; } } diff --git a/client/app/services/file-manager.js b/client/app/services/file-manager.js index c516b586..e27c748a 100644 --- a/client/app/services/file-manager.js +++ b/client/app/services/file-manager.js @@ -12,7 +12,10 @@ export default class FileManager { filesToUpload, // EmberFileUpload Queue Object session, ) { - console.assert(entityType === 'package' || entityType === 'artifact', "entityType must be 'package' or 'artifact'"); + console.assert( + entityType === 'package' || entityType === 'artifact', + "entityType must be 'package' or 'artifact'", + ); this.recordId = recordId; this.entityType = entityType; @@ -61,18 +64,24 @@ export default class FileManager { this.numFilesToUpload -= 1; } - async uploadFiles() { + async uploadFiles(instanceId = this.recordId) { for (let i = 0; i < this.filesToUpload.files.length; i += 1) { - await this.filesToUpload.files[i].upload(`${ENV.host}/documents/${this.entityType}`, { // eslint-disable-line - fileKey: 'file', - headers: { - Authorization: `Bearer ${this.session.data.authenticated.access_token}`, + // eslint-disable-next-line no-await-in-loop + await this.filesToUpload.files[i].upload( + `${ENV.host}/documents/${this.entityType}`, + { + // eslint-disable-line + fileKey: 'file', + headers: { + Authorization: `Bearer ${this.session.data.authenticated.access_token}`, + }, + data: { + instanceId, + entityName: + this.entityType === 'artifact' ? 'dcp_artifacts' : 'dcp_package', + }, }, - data: { - instanceId: this.recordId, - entityName: this.entityType === 'artifact' ? 'dcp_artifacts' : 'dcp_package', - }, - }); + ); } } @@ -82,21 +91,24 @@ export default class FileManager { // TODO: If this is not possible, rework this to be a // POST request to a differently named endpoint, like // deleteDocument - return Promise.all(this.filesToDelete.map((file) => fetch( - `${ENV.host}/documents?serverRelativeUrl=${file.serverRelativeUrl}`, { - method: 'DELETE', - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.session.data.authenticated.access_token}`, + return Promise.all( + this.filesToDelete.map((file) => fetch( + `${ENV.host}/documents?serverRelativeUrl=${file.serverRelativeUrl}`, + { + method: 'DELETE', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.session.data.authenticated.access_token}`, + }, }, - }, - ))); + )), + ); } - async save() { + async save(instanceId) { // See TODO at top of this file. - await this.uploadFiles(); + await this.uploadFiles(instanceId); await this.deleteFiles(); diff --git a/client/app/validations/submittable-projects-new-form.js b/client/app/validations/submittable-projects-new-form.js index 14603a18..498af0ad 100644 --- a/client/app/validations/submittable-projects-new-form.js +++ b/client/app/validations/submittable-projects-new-form.js @@ -3,6 +3,7 @@ import { validateLength, validatePresence, } from 'ember-changeset-validations/validators'; +import validateFileUpload from '../validators/validate-file-presence'; export default { primaryContactFirstName: [ @@ -137,4 +138,9 @@ export default { message: 'This field is required', }), ], + documents: [ + validateFileUpload({ + message: 'One or more document uploads is required.', + }), + ], }; diff --git a/client/app/validators/validate-file-presence.js b/client/app/validators/validate-file-presence.js new file mode 100644 index 00000000..293e41a0 --- /dev/null +++ b/client/app/validators/validate-file-presence.js @@ -0,0 +1,9 @@ +export default function validateFileUpload(options = {}) { + return (key, newValue) => { + if (!newValue || newValue.length === 0) { + return options.message || `${key} one or more document uploads is required.`; + } + + return true; + }; +} diff --git a/server/src/artifacts/artifacts.service.ts b/server/src/artifacts/artifacts.service.ts index 5d732ff1..d5d2fda9 100644 --- a/server/src/artifacts/artifacts.service.ts +++ b/server/src/artifacts/artifacts.service.ts @@ -7,6 +7,7 @@ import { ConfigService } from '../config/config.service'; @Injectable() export class ArtifactService { rerFiletypeUuid = ''; + letterFiletypeUuid= ''; constructor( private readonly crmService: CrmService, @@ -14,6 +15,7 @@ export class ArtifactService { private readonly config: ConfigService, ) { this.rerFiletypeUuid = this.config.get('RER_FILETYPE_UUID'); + this.letterFiletypeUuid = this.config.get('LETTER_FILETYPE_UUID'); } public async createEquityReport(projectId: string) { @@ -45,6 +47,35 @@ export class ArtifactService { return newArtifact; } + public async createProjectInitiationArtifacts(projectId: string) { + let newArtifact = null; + + try { + newArtifact = this.crmService.create('dcp_artifactses', { + dcp_name: `Project Initiation`, + dcp_isdcpinternal: false, + dcp_filecreator: 717170000, // Applicant + dcp_filecategory: 717170006, // Other + dcp_visibility: 717170002, // Applicant Only + 'dcp_applicantfiletype@odata.bind': `/dcp_filetypes(${this.letterFiletypeUuid})`, + ...(projectId + ? { 'dcp_project@odata.bind': `/dcp_projects(${projectId})` } + : {}), + }); + } catch (e) { + throw new HttpException( + { + code: 'CREATE_PROJECT_ARTIFACT_ERROR', + title: `Unable to create Project Initiation Artifact dcp_artifactses entity for project with UUID ${projectId}`, + detail: e, + }, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + return newArtifact; + } + async getArtifactSharepointDocuments(relativeUrl: string, dcp_name: string) { if (relativeUrl) { try { diff --git a/server/src/projects/projects.controller.ts b/server/src/projects/projects.controller.ts index 40e2d0ae..9c6370c7 100644 --- a/server/src/projects/projects.controller.ts +++ b/server/src/projects/projects.controller.ts @@ -38,6 +38,7 @@ import { INVOICE_ATTRS } from '../invoices/invoices.attrs'; 'team-members', 'contacts', 'milestones', + 'dcp_artifactsid', ], packages: { ref: 'dcp_packageid', diff --git a/server/src/projects/projects.module.ts b/server/src/projects/projects.module.ts index 97e4eb17..48b3b693 100644 --- a/server/src/projects/projects.module.ts +++ b/server/src/projects/projects.module.ts @@ -6,10 +6,12 @@ import { ConfigModule } from '../config/config.module'; import { AuthModule } from '../auth/auth.module'; import { ProjectsController } from './projects.controller'; import { ProjectApplicantController } from './project-applicants/project-applicant.controller'; +import { ArtifactService } from '../artifacts/artifacts.service'; +import { SharepointModule } from '../sharepoint/sharepoint.module'; @Module({ - imports: [CrmModule, ConfigModule, ContactModule, AuthModule], - providers: [ProjectsService], + imports: [CrmModule, SharepointModule, ConfigModule, ContactModule, AuthModule], + providers: [ProjectsService, ArtifactService], exports: [ProjectsService], controllers: [ProjectsController, ProjectApplicantController], }) diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index d1364fdd..4d9b2ce5 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -4,6 +4,7 @@ import { NycidService } from '../contact/nycid/nycid.service'; import { CrmService } from '../crm/crm.service'; import { overwriteCodesWithLabels } from '../_utils/overwrite-codes-with-labels'; import { MILESTONE_ATTRS, MILESTONE_NON_DATE_ATTRS } from './projects.attrs'; +import { ArtifactService } from '../artifacts/artifacts.service'; const APPLICANT_ACTIVE_STATUS_CODE = 1; const PROJECT_ACTIVE_STATE_CODE = 0; @@ -52,6 +53,7 @@ export class ProjectsService { constructor( private readonly crmService: CrmService, private readonly nycidService: NycidService, + private readonly artifactService: ArtifactService, ) {} public async findManyByContactId(contactId: string) { @@ -122,8 +124,14 @@ export class ProjectsService { 'dcp_projects', data, ); + const { dcp_artifactsid } = + await this.artifactService.createProjectInitiationArtifacts( + dcp_projectid, + ); + return { dcp_projectid, + dcp_artifactsid, }; } catch (e) { console.debug('error creating project', e); From 4006bf8211a1fe2e92b33fc21dc9e78bb257bbee Mon Sep 17 00:00:00 2001 From: horatio Date: Fri, 13 Dec 2024 15:01:09 -0500 Subject: [PATCH 02/12] some logging, will probs add much more --- client/app/components/projects/new.js | 8 +++++++ server/src/projects/projects.controller.ts | 5 ++++ server/src/projects/projects.service.ts | 28 +++++++++++++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index 54f40237..2ab6bc79 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -11,6 +11,8 @@ export default class ProjectsNewFormComponent extends Component { SubmittableProjectsNewForm, }; + requestCounter = 0; + @service router; @@ -27,6 +29,9 @@ export default class ProjectsNewFormComponent extends Component { @action async submitProject() { + const requestStartTime = Date.now(); + console.log(`[Total Requests Made in the service] ${this.requestCounter}`); + const primaryContactInput = { first: this.args.package.primaryContactFirstName, last: this.args.package.primaryContactLastName, @@ -115,6 +120,9 @@ export default class ProjectsNewFormComponent extends Component { }), }); const { data: project } = await response.json(); + const requestEndTime = Date.now(); + console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`); + console.debug('response in client controller: ', response, 'response status: ', response.status); this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']); diff --git a/server/src/projects/projects.controller.ts b/server/src/projects/projects.controller.ts index 9c6370c7..972314ef 100644 --- a/server/src/projects/projects.controller.ts +++ b/server/src/projects/projects.controller.ts @@ -83,6 +83,7 @@ import { INVOICE_ATTRS } from '../invoices/invoices.attrs'; @Controller('projects') export class ProjectsController { CRM_IMPOSTER_ID = ''; + requestCounter = 0; constructor( private readonly projectsService: ProjectsService, @@ -132,6 +133,8 @@ export class ProjectsController { @Post('/') async createProject(@Body() body) { + const requestStartTime = Date.now(); + console.log(`[Total Requests Made in the controller] ${this.requestCounter}`) const allowedAttrs = pick(body, PROJECT_ATTRS) as { dcp_projectname: string; dcp_borough: string; @@ -149,6 +152,8 @@ export class ProjectsController { HttpStatus.NOT_FOUND, ); } + const requestEndTime = Date.now(); + console.debug(`POST request in the controller to took ${requestEndTime - requestStartTime} ms`); return await this.projectsService.create(allowedAttrs); } diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index 4d9b2ce5..55ee048f 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -10,6 +10,7 @@ const APPLICANT_ACTIVE_STATUS_CODE = 1; const PROJECT_ACTIVE_STATE_CODE = 0; const PROJECT_VISIBILITY_APPLICANT_ONLY = 717170002; const PROJECT_VISIBILITY_GENERAL_PUBLIC = 717170003; +let requestCounter = 0; const PACKAGE_VISIBILITY = { APPLICANT_ONLY: 717170002, @@ -112,6 +113,11 @@ export class ProjectsService { _dcp_applicantadministrator_customer_value: string; }) { try { + const requestStartTime = Date.now(); + console.log(`[Total Requests Made in the service] ${requestCounter}`); + + requestCounter++; + const data = { dcp_projectname: attributes.dcp_projectname, dcp_borough: attributes.dcp_borough, @@ -120,15 +126,31 @@ export class ProjectsService { 'dcp_applicant_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicant_customer_value})`, 'dcp_applicantadministrator_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicantadministrator_customer_value})`, }; - const { dcp_projectid } = await this.crmService.create( + // const { dcp_projectid } = await this.crmService.create( + // 'dcp_projects', + // data, + // ); + const project = await this.crmService.create( 'dcp_projects', data, ); - const { dcp_artifactsid } = + console.debug("project", project); + console.debug("project response", project.response); + const { dcp_projectid } = project; + + // const { dcp_artifactsid } = + // await this.artifactService.createProjectInitiationArtifacts( + // dcp_projectid, + // ); + + const artifact = await this.artifactService.createProjectInitiationArtifacts( dcp_projectid, ); - + console.debug('artifact', artifact); + const { dcp_artifactsid } = artifact; + const requestEndTime = Date.now(); + console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`); return { dcp_projectid, dcp_artifactsid, From 7f10a9c0c5ec2ab3643e2dbc07eb42617b77f4e7 Mon Sep 17 00:00:00 2001 From: tangoyankee Date: Fri, 13 Dec 2024 16:04:43 -0500 Subject: [PATCH 03/12] manually install heroku cli --- .github/workflows/qa.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index bb147417..59c6860d 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -36,6 +36,8 @@ jobs: - uses: actions/checkout@v4 with: sparse-checkout: server + - name: 'Install Heroku CLI' + run: curl https://cli-assets.heroku.com/install.sh | sh - uses: akhileshns/heroku-deploy@v3.13.15 name: Deploy server to Heroku with: From ec1b5b38ff7c8a8cb5fce9b2fcbadccc42a5308e Mon Sep 17 00:00:00 2001 From: horatio Date: Wed, 18 Dec 2024 13:51:10 -0500 Subject: [PATCH 04/12] transition to projects list after submit --- client/app/components/projects/new.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index 2ab6bc79..80f9035b 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -126,7 +126,7 @@ export default class ProjectsNewFormComponent extends Component { this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']); - this.router.transitionTo('project', project.id); + this.router.transitionTo('projects'); } catch { /* eslint-disable-next-line no-console */ console.error('Error while creating project'); From d7070da5165bddd3685c5b5f08e7ee379794687d Mon Sep 17 00:00:00 2001 From: horatio Date: Fri, 27 Dec 2024 12:48:00 -0500 Subject: [PATCH 05/12] some tweaks to the logging --- client/app/components/projects/new.js | 5 +++-- server/src/projects/projects.controller.ts | 5 +++-- server/src/projects/projects.service.ts | 13 ++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index 80f9035b..b4753802 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -30,7 +30,8 @@ export default class ProjectsNewFormComponent extends Component { @action async submitProject() { const requestStartTime = Date.now(); - console.log(`[Total Requests Made in the service] ${this.requestCounter}`); + this.requestCounter++; + console.log(`LOGGER: [Total Requests Made in the client controller] ${this.requestCounter}`); const primaryContactInput = { first: this.args.package.primaryContactFirstName, @@ -121,7 +122,7 @@ export default class ProjectsNewFormComponent extends Component { }); const { data: project } = await response.json(); const requestEndTime = Date.now(); - console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`); + console.debug(`LOGGER: POST request in the client controller to took ${requestEndTime - requestStartTime} ms`); console.debug('response in client controller: ', response, 'response status: ', response.status); this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']); diff --git a/server/src/projects/projects.controller.ts b/server/src/projects/projects.controller.ts index 972314ef..ffcc65a3 100644 --- a/server/src/projects/projects.controller.ts +++ b/server/src/projects/projects.controller.ts @@ -134,7 +134,6 @@ export class ProjectsController { @Post('/') async createProject(@Body() body) { const requestStartTime = Date.now(); - console.log(`[Total Requests Made in the controller] ${this.requestCounter}`) const allowedAttrs = pick(body, PROJECT_ATTRS) as { dcp_projectname: string; dcp_borough: string; @@ -153,7 +152,9 @@ export class ProjectsController { ); } const requestEndTime = Date.now(); - console.debug(`POST request in the controller to took ${requestEndTime - requestStartTime} ms`); + this.requestCounter++; + console.log(`LOGGER: [Total Requests Made in the controller] ${this.requestCounter}`) + console.debug(`LOGGER: POST request in the controller to took ${requestEndTime - requestStartTime} ms`); return await this.projectsService.create(allowedAttrs); } diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index 55ee048f..ceff4675 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -114,9 +114,7 @@ export class ProjectsService { }) { try { const requestStartTime = Date.now(); - console.log(`[Total Requests Made in the service] ${requestCounter}`); - requestCounter++; const data = { dcp_projectname: attributes.dcp_projectname, @@ -134,8 +132,7 @@ export class ProjectsService { 'dcp_projects', data, ); - console.debug("project", project); - console.debug("project response", project.response); + console.debug("LOGGER: (service) project", project); const { dcp_projectid } = project; // const { dcp_artifactsid } = @@ -147,16 +144,18 @@ export class ProjectsService { await this.artifactService.createProjectInitiationArtifacts( dcp_projectid, ); - console.debug('artifact', artifact); + console.debug('LOGGER: (service) artifact', artifact); const { dcp_artifactsid } = artifact; const requestEndTime = Date.now(); - console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`); + console.debug(`LOGGER: POST (service) request in the service to took ${requestEndTime - requestStartTime} ms`); + requestCounter++; + console.log(`LOGGER: [Total Requests Made in the service] ${requestCounter}`); return { dcp_projectid, dcp_artifactsid, }; } catch (e) { - console.debug('error creating project', e); + console.debug('(service) error creating project', e); throw new HttpException( 'Unable to create project', HttpStatus.INTERNAL_SERVER_ERROR, From f49b954183baffa3802df0615038d8d97bc0670c Mon Sep 17 00:00:00 2001 From: horatio Date: Tue, 31 Dec 2024 10:28:39 -0500 Subject: [PATCH 06/12] added logging to cms service --- server/src/crm/crm.service.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/server/src/crm/crm.service.ts b/server/src/crm/crm.service.ts index 5f0f0d8e..8ef9aba5 100644 --- a/server/src/crm/crm.service.ts +++ b/server/src/crm/crm.service.ts @@ -19,6 +19,7 @@ export class CrmService { crmUrlPath = ''; crmHost = ''; host = ''; + requestCounter = 0; constructor(private readonly config: ConfigService) { ADAL.ADAL_CONFIG = { @@ -62,7 +63,21 @@ export class CrmService { } async create(query, data, headers = {}) { - return this._create(query, data, headers); + try { + console.debug( + "LOGGER CRM create query", query, + "LOGGER CRM create data", data, + "LOGGER CRM create headers", headers, + ); + const requestStartTime = Date.now(); + const response = this._create(query, data, headers); + const requestEndTime = Date.now(); + console.debug(`LOGGER: LOGGER CRM create request took ${requestEndTime - requestStartTime} ms`); + return response; + + } catch (e) { + console.debug(`LOGGER error in CRM create: ${e}`); + } } async update(entity, guid, data, headers = {}) { From a34d21851b58d829b41d6b34861fe2a937048687 Mon Sep 17 00:00:00 2001 From: horatio Date: Tue, 31 Dec 2024 11:10:00 -0500 Subject: [PATCH 07/12] removed logging of peoject and artifact to reduce noise in papertrail --- server/src/projects/projects.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index ceff4675..c5f67d24 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -132,7 +132,7 @@ export class ProjectsService { 'dcp_projects', data, ); - console.debug("LOGGER: (service) project", project); + // console.debug("LOGGER: (service) project", project); const { dcp_projectid } = project; // const { dcp_artifactsid } = @@ -144,7 +144,7 @@ export class ProjectsService { await this.artifactService.createProjectInitiationArtifacts( dcp_projectid, ); - console.debug('LOGGER: (service) artifact', artifact); + // console.debug('LOGGER: (service) artifact', artifact); const { dcp_artifactsid } = artifact; const requestEndTime = Date.now(); console.debug(`LOGGER: POST (service) request in the service to took ${requestEndTime - requestStartTime} ms`); From 580bb36c16468e98dc322c4f8509adcb500b713f Mon Sep 17 00:00:00 2001 From: horatio Date: Fri, 3 Jan 2025 14:05:58 -0500 Subject: [PATCH 08/12] some guard rails - projects.new and projects.service, first pass --- client/app/components/projects/new.js | 7 ++++++- server/src/projects/projects.service.ts | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index b4753802..9486e44c 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -125,7 +125,12 @@ export default class ProjectsNewFormComponent extends Component { console.debug(`LOGGER: POST request in the client controller to took ${requestEndTime - requestStartTime} ms`); console.debug('response in client controller: ', response, 'response status: ', response.status); - this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']); + const artifactsId = project.attributes['dcp-artifactsid']; + if (artifactsId === undefined) { + throw new Error('failed to create project with artifact'); + } + + await this.args.package.saveAttachedFiles(artifactsId); this.router.transitionTo('projects'); } catch { diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index c5f67d24..e3e90805 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -128,6 +128,19 @@ export class ProjectsService { // 'dcp_projects', // data, // ); + const crmResponse = await this.crmService.get( + 'dcp_projects', + // 3e5 = created within 5 minutes + + ` + $filter= + dcp_projectname eq '${data.dcp_projectname}' + and createdon ge '${new Date(Date.now() - 3e5).toISOString()}' + ` + ) + + console.debug('HELLO??: crm response', crmResponse); + const project = await this.crmService.create( 'dcp_projects', data, From 247514fdcb1d147a44be44e7089f2833ad4f3a09 Mon Sep 17 00:00:00 2001 From: horatio Date: Mon, 6 Jan 2025 12:12:54 -0500 Subject: [PATCH 09/12] needs error handling and/or redirect --- server/src/projects/projects.service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index e3e90805..fee58699 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -138,8 +138,17 @@ export class ProjectsService { and createdon ge '${new Date(Date.now() - 3e5).toISOString()}' ` ) + const { records } = crmResponse; + console.debug('LOGGER: crm response', crmResponse); + try { + if (records.length > 0) { + console.log('Project already exists'); + throw new Error('Project already exists'); + } + } catch (e) { + + } - console.debug('HELLO??: crm response', crmResponse); const project = await this.crmService.create( 'dcp_projects', From 33bc5aa161e09234cfc13d03248a3183a75dc6fd Mon Sep 17 00:00:00 2001 From: horatio Date: Wed, 8 Jan 2025 10:43:30 -0500 Subject: [PATCH 10/12] cherrypick changes from ty/1254/upload-documents --- .github/workflows/production.yml | 2 + .github/workflows/staging.yml | 2 + client/app/components/projects/new.hbs | 24 +++++---- client/app/components/projects/new.js | 5 ++ .../projects/projects-new-attachments.hbs | 2 +- server/src/projects/projects.module.ts | 8 ++- server/src/projects/projects.service.ts | 50 ++++++------------- 7 files changed, 48 insertions(+), 45 deletions(-) diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 06c9f178..f47bed15 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -36,6 +36,8 @@ jobs: - uses: actions/checkout@v4 with: sparse-checkout: server + - name: 'Install Heroku CLI' + run: curl https://cli-assets.heroku.com/install.sh | sh - uses: akhileshns/heroku-deploy@v3.13.15 name: Deploy server to Heroku with: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index a36f397c..7a2c58ba 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -40,6 +40,8 @@ jobs: with: ref: 'main' sparse-checkout: server + - name: 'Install Heroku CLI' + run: curl https://cli-assets.heroku.com/install.sh | sh - uses: akhileshns/heroku-deploy@v3.13.15 name: Deploy server to Heroku with: diff --git a/client/app/components/projects/new.hbs b/client/app/components/projects/new.hbs index a4c4416e..d9f8bf87 100644 --- a/client/app/components/projects/new.hbs +++ b/client/app/components/projects/new.hbs @@ -47,17 +47,23 @@ -
Confirm New Project Submission
-

- Are you sure? -

-

- Before submitting, ensure that your answers are accurate and complete, and that necessary attachments have been uploaded. If NYC Planning does not receive enough accurate information to provide guidance, the Lead Planner will notify you and request that this form be resubmitted with necessary materials, corrections, or - clarifications. -

+ {{#if this.submissionError}} +

Error while creating project. Lorem Ipsum.....

+ {{else}} +

+ Are you sure? +

+

+ Before submitting, ensure that your answers are accurate and complete, and that necessary attachments have been + uploaded. If NYC Planning does not receive enough accurate information to provide guidance, the Lead Planner will + notify you and request that this form be resubmitted with necessary materials, corrections, or + clarifications. +

+ {{/if}}
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index 9486e44c..ef264f66 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -1,5 +1,6 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import SubmittableProjectsNewForm from '../../validations/submittable-projects-new-form'; import { optionset } from '../../helpers/optionset'; @@ -11,6 +12,8 @@ export default class ProjectsNewFormComponent extends Component { SubmittableProjectsNewForm, }; + @tracked submissionError = false; + requestCounter = 0; @service @@ -29,6 +32,7 @@ export default class ProjectsNewFormComponent extends Component { @action async submitProject() { + this.submissionError = false; const requestStartTime = Date.now(); this.requestCounter++; console.log(`LOGGER: [Total Requests Made in the client controller] ${this.requestCounter}`); @@ -134,6 +138,7 @@ export default class ProjectsNewFormComponent extends Component { this.router.transitionTo('projects'); } catch { + this.submissionError = true; /* eslint-disable-next-line no-console */ console.error('Error while creating project'); } diff --git a/client/app/components/projects/projects-new-attachments.hbs b/client/app/components/projects/projects-new-attachments.hbs index b0ed9444..f771f631 100644 --- a/client/app/components/projects/projects-new-attachments.hbs +++ b/client/app/components/projects/projects-new-attachments.hbs @@ -130,4 +130,4 @@ The size limit for each file is 50 MB. You can upload up to 1 GB of files.

- \ No newline at end of file + diff --git a/server/src/projects/projects.module.ts b/server/src/projects/projects.module.ts index 48b3b693..6614eaf9 100644 --- a/server/src/projects/projects.module.ts +++ b/server/src/projects/projects.module.ts @@ -10,7 +10,13 @@ import { ArtifactService } from '../artifacts/artifacts.service'; import { SharepointModule } from '../sharepoint/sharepoint.module'; @Module({ - imports: [CrmModule, SharepointModule, ConfigModule, ContactModule, AuthModule], + imports: [ + CrmModule, + SharepointModule, + ConfigModule, + ContactModule, + AuthModule, + ], providers: [ProjectsService, ArtifactService], exports: [ProjectsService], controllers: [ProjectsController, ProjectApplicantController], diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index fee58699..0090a93f 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -124,60 +124,42 @@ export class ProjectsService { 'dcp_applicant_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicant_customer_value})`, 'dcp_applicantadministrator_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicantadministrator_customer_value})`, }; - // const { dcp_projectid } = await this.crmService.create( - // 'dcp_projects', - // data, - // ); - const crmResponse = await this.crmService.get( + + const { records } = await this.crmService.get( 'dcp_projects', // 3e5 = created within 5 minutes - ` $filter= - dcp_projectname eq '${data.dcp_projectname}' + dcp_projectname eq '${encodeURIComponent(data.dcp_projectname)}' and createdon ge '${new Date(Date.now() - 3e5).toISOString()}' ` ) - const { records } = crmResponse; - console.debug('LOGGER: crm response', crmResponse); - try { - if (records.length > 0) { - console.log('Project already exists'); - throw new Error('Project already exists'); - } - } catch (e) { - } + console.debug('LOGGER: crm records', records); + if (records.length > 0) throw new Error('Project already exists'); - - const project = await this.crmService.create( - 'dcp_projects', - data, - ); + const project = await this.crmService.create('dcp_projects', data); // console.debug("LOGGER: (service) project", project); - const { dcp_projectid } = project; - - // const { dcp_artifactsid } = - // await this.artifactService.createProjectInitiationArtifacts( - // dcp_projectid, - // ); + const dcpProjectId = project['dcp_projectid']; + if (dcpProjectId === undefined) throw new Error('Failed to create project'); const artifact = - await this.artifactService.createProjectInitiationArtifacts( - dcp_projectid, - ); + await this.artifactService.createProjectInitiationArtifacts(dcpProjectId); + // console.debug('LOGGER: (service) artifact', artifact); - const { dcp_artifactsid } = artifact; + const dcpArtifactsId = artifact['dcp_artifactsid']; + if (dcpArtifactsId === undefined) throw new Error('Failed to create artifact for project'); + const requestEndTime = Date.now(); console.debug(`LOGGER: POST (service) request in the service to took ${requestEndTime - requestStartTime} ms`); requestCounter++; console.log(`LOGGER: [Total Requests Made in the service] ${requestCounter}`); return { - dcp_projectid, - dcp_artifactsid, + dcp_projectid: dcpProjectId, + dcp_artifactsid: dcpArtifactsId, }; } catch (e) { - console.debug('(service) error creating project', e); + console.error('(service) error creating project', e); throw new HttpException( 'Unable to create project', HttpStatus.INTERNAL_SERVER_ERROR, From 20c57fa43a41566404a88b0297d75df5919a41cc Mon Sep 17 00:00:00 2001 From: horatio Date: Wed, 8 Jan 2025 15:08:56 -0500 Subject: [PATCH 11/12] prevent multiple clicks on submit button --- client/app/components/projects/new.hbs | 4 +++- client/app/components/projects/new.js | 10 +++++++++- server/src/projects/projects.service.ts | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/client/app/components/projects/new.hbs b/client/app/components/projects/new.hbs index d9f8bf87..f1b7a4d6 100644 --- a/client/app/components/projects/new.hbs +++ b/client/app/components/projects/new.hbs @@ -48,11 +48,13 @@
Confirm New Project Submission
{{#if this.submissionError}} +

Error while creating project. Lorem Ipsum.....

+
{{else}}

Are you sure? diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js index ef264f66..032b8c91 100644 --- a/client/app/components/projects/new.js +++ b/client/app/components/projects/new.js @@ -12,7 +12,11 @@ export default class ProjectsNewFormComponent extends Component { SubmittableProjectsNewForm, }; - @tracked submissionError = false; + @tracked + submissionError = false; + + @tracked + isSubmitting = false; requestCounter = 0; @@ -33,6 +37,8 @@ export default class ProjectsNewFormComponent extends Component { @action async submitProject() { this.submissionError = false; + if (this.isSubmitting) return; + this.isSubmitting = true; const requestStartTime = Date.now(); this.requestCounter++; console.log(`LOGGER: [Total Requests Made in the client controller] ${this.requestCounter}`); @@ -141,6 +147,8 @@ export default class ProjectsNewFormComponent extends Component { this.submissionError = true; /* eslint-disable-next-line no-console */ console.error('Error while creating project'); + } finally { + this.isSubmitting = false; } } } diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index 0090a93f..8a4c630b 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -115,7 +115,6 @@ export class ProjectsService { try { const requestStartTime = Date.now(); - const data = { dcp_projectname: attributes.dcp_projectname, dcp_borough: attributes.dcp_borough, From 239a985279670f84068d891aa286cd698e1d7667 Mon Sep 17 00:00:00 2001 From: horatio Date: Thu, 9 Jan 2025 10:35:12 -0500 Subject: [PATCH 12/12] remove filter in project service as not needed --- server/src/projects/projects.service.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts index 8a4c630b..86e86678 100644 --- a/server/src/projects/projects.service.ts +++ b/server/src/projects/projects.service.ts @@ -124,28 +124,14 @@ export class ProjectsService { 'dcp_applicantadministrator_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicantadministrator_customer_value})`, }; - const { records } = await this.crmService.get( - 'dcp_projects', - // 3e5 = created within 5 minutes - ` - $filter= - dcp_projectname eq '${encodeURIComponent(data.dcp_projectname)}' - and createdon ge '${new Date(Date.now() - 3e5).toISOString()}' - ` - ) - - console.debug('LOGGER: crm records', records); - if (records.length > 0) throw new Error('Project already exists'); - const project = await this.crmService.create('dcp_projects', data); - // console.debug("LOGGER: (service) project", project); + const dcpProjectId = project['dcp_projectid']; if (dcpProjectId === undefined) throw new Error('Failed to create project'); const artifact = await this.artifactService.createProjectInitiationArtifacts(dcpProjectId); - // console.debug('LOGGER: (service) artifact', artifact); const dcpArtifactsId = artifact['dcp_artifactsid']; if (dcpArtifactsId === undefined) throw new Error('Failed to create artifact for project');