Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 1245: Skeleton form for new project #1265

Merged
merged 1 commit into from
Oct 2, 2024
Merged
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
45 changes: 45 additions & 0 deletions client/app/components/packages/projects/new.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<SaveableForm
@model={{@package}}
@validators={{array (hash) this.validations.SubmittableProjectsNewForm }}
as |saveableProjectsNewForm|
>
<div class="grid-x grid-margin-x">
<div class="cell large-8">
<section class="form-section">
<h1 class="header-large">
Project Initiation Form
</h1>
<p>
The <b>Project Initiation Form</b> begins the project tracking process and gives NYC Planning the necessary
details before the Informational Interest meeting. By submitting this form, you confirm your intent to file a
Land Use Application with NYC Planning.
</p>
<p>
While some projects might still be in the early stages, all required fields (*) on this form must be completed.
Once submitted, your project will be available to view in this portal. After reviewing your submission, NYC
Planning will contact you with the next steps.
</p>
</section>
<Packages::Projects::ProjectsNewInformation @form={{saveableProjectsNewForm}} />
</div>

<div class="cell large-4 sticky-sidebar">
<saveableProjectsNewForm.PageNav>
<saveableProjectsNewForm.SubmitButton @isEnabled={{saveableProjectsNewForm.isSubmittable}} data-test-save-button />
</saveableProjectsNewForm.PageNav>
<saveableProjectsNewForm.ConfirmationModal @action={{component saveableProjectsNewForm.SubmitButton
isEnabled=saveableProjectsNewForm.isSubmittable onClick=this.submitPackage class="no-margin" }}
@continueButtonTitle="Continue Editing" data-test-confirm-submit-button={{true}}>
<h6>Confirm New Project Submission</h6>
<p class="header-large medium-margin-top small-margin-bottom">
Are you sure?
</p>
<p>
Before submitting, ensure that your answers are accurate and complete, and that necessary attachments (including
the
signature form) have been uploaded.
</p>
</saveableProjectsNewForm.ConfirmationModal>
</div>
</div>
</SaveableForm>
26 changes: 26 additions & 0 deletions client/app/components/packages/projects/new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import SubmittableProjectsNewForm from '../../../validations/submittable-projects-new-form';


export default class ProjectsNewFormComponent extends Component {
validations = {
SubmittableProjectsNewForm,
};

@service
router;

@service
store;

@action
async submitPackage() {
try {
await this.args.package.submit();
} catch (error) {
console.log('Save new project package error:', error);

Check warning on line 23 in client/app/components/packages/projects/new.js

View workflow job for this annotation

GitHub Actions / 🧪 Test client code

Unexpected console statement

Check warning on line 23 in client/app/components/packages/projects/new.js

View workflow job for this annotation

GitHub Actions / 🧪 Test client code

Unexpected console statement
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{{#let @form as |form|}}
<form.Section @title="Project Information">

<Ui::Question @required={{true}} as |Q|>
<Q.Label>
Project Name
</Q.Label>

<form.Field
@attribute="dcpProjectname"
@type="text-input"
id={{Q.questionId}}
@showCounter={{true}}
@maxlength="50"
/>
</Ui::Question>
</form.Section>
{{/let}}
75 changes: 43 additions & 32 deletions client/app/models/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ export default class PackageModel extends Model {
dcpVisibility;

@attr('number')
dcpPackageversion
dcpPackageversion;

@attr('string')
dcpPackagenotes
dcpPackagenotes;

@attr({ defaultValue: () => [] })
documents;
Expand All @@ -101,9 +101,11 @@ export default class PackageModel extends Model {
}

get isLUPackage() {
return this.dcpPackagetype === DCPPACKAGETYPE.DRAFT_LU_PACKAGE.code
|| this.dcpPackagetype === DCPPACKAGETYPE.FILED_LU_PACKAGE.code
|| this.dcpPackagetype === DCPPACKAGETYPE.POST_CERT_LU.code;
return (
this.dcpPackagetype === DCPPACKAGETYPE.DRAFT_LU_PACKAGE.code
|| this.dcpPackagetype === DCPPACKAGETYPE.FILED_LU_PACKAGE.code
|| this.dcpPackagetype === DCPPACKAGETYPE.POST_CERT_LU.code
);
}

setAttrsForSubmission() {
Expand Down Expand Up @@ -192,15 +194,18 @@ export default class PackageModel extends Model {
try {
await this.fileManager.save();
} catch (e) {
console.log('Error saving files: ', e);// eslint-disable-line no-console
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.',
}];
this.fileUploadErrors = [
{
code: 'UPLOAD_DOC_FAILED',
title: 'Failed to upload documents',
detail:
'An error occured while uploading your documents. Please refresh and retry.',
},
];
}

if (this.isLUPackage && this.project.artifact) {
Expand All @@ -211,11 +216,14 @@ export default class PackageModel extends Model {

// See comment on the tracked fileUploadError property
// definition above.
this.fileUploadErrors = [{
code: 'UPLOAD_DOC_FAILED',
title: 'Failed to upload artifact documents',
detail: 'An error occured while uploading your documents. Please refresh and retry.',
}];
this.fileUploadErrors = [
{
code: 'UPLOAD_DOC_FAILED',
title: 'Failed to upload artifact documents',
detail:
'An error occured while uploading your documents. Please refresh and retry.',
},
];
}
}

Expand All @@ -233,7 +241,8 @@ export default class PackageModel extends Model {
get isSingleCeqrInvoiceQuestionnaireDirty() {
if (this.singleCeqrInvoiceQuestionnaire) {
return this.singleCeqrInvoiceQuestionnaire.hasDirtyAttributes;
} return false;
}
return false;
}

async saveDirtySingleCeqrInvoiceQuestionnaire() {
Expand All @@ -250,10 +259,7 @@ export default class PackageModel extends Model {

async saveDeletedRecords(recordsToDelete) {
if (recordsToDelete) {
return Promise.all(
recordsToDelete
.map((record) => record.save()),
);
return Promise.all(recordsToDelete.map((record) => record.save()));
}
}

Expand All @@ -267,22 +273,28 @@ export default class PackageModel extends Model {

get isDirty() {
const isPackageDirty = this.hasDirtyAttributes
|| this.fileManager.isDirty || (this.isLUPackage && this.project.isDirty);
|| this.fileManager.isDirty
|| (this.isLUPackage && this.project.isDirty);

if (this.dcpPackagetype === DCPPACKAGETYPE.PAS_PACKAGE.code) {
return isPackageDirty
return (
isPackageDirty
|| this.pasForm.hasDirtyAttributes
|| this.pasForm.isBblsDirty
|| this.pasForm.isApplicantsDirty
|| this.pasForm.isProjectDirty;
|| this.pasForm.isProjectDirty
);
}
if (this.dcpPackagetype === DCPPACKAGETYPE.RWCDS.code) {
return isPackageDirty
return (
isPackageDirty
|| this.rwcdsForm.hasDirtyAttributes
|| this.rwcdsForm.isAffectedZoningResolutionsDirty;
|| this.rwcdsForm.isAffectedZoningResolutionsDirty
);
}
if (this.isLUPackage) {
return isPackageDirty
return (
isPackageDirty
|| this.landuseForm.hasDirtyAttributes
|| this.landuseForm.isBblsDirty
|| this.landuseForm.isApplicantsDirty
Expand All @@ -291,15 +303,14 @@ export default class PackageModel extends Model {
|| this.landuseForm.isLanduseGeographiesDirty
|| this.landuseForm.isRelatedActionsDirty
|| this.landuseForm.isAffectedZoningResolutionsDirty
|| this.landuseForm.isZoningMapChangesDirty;
|| this.landuseForm.isZoningMapChangesDirty
);
}
if (this.dcpPackagetype === DCPPACKAGETYPE.FILED_EAS.code) {
return isPackageDirty
|| this.isSingleCeqrInvoiceQuestionnaireDirty;
return isPackageDirty || this.isSingleCeqrInvoiceQuestionnaireDirty;
}
if (this.dcpPackagetype === DCPPACKAGETYPE.DRAFT_SCOPE_OF_WORK.code) {
return isPackageDirty
|| this.isSingleCeqrInvoiceQuestionnaireDirty;
return isPackageDirty || this.isSingleCeqrInvoiceQuestionnaireDirty;
}

return isPackageDirty;
Expand Down
4 changes: 4 additions & 0 deletions client/app/models/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,8 @@ export default class ProjectModel extends Model {
.sortBy('dcpPackageversion')
.reverse();
}

async submit() {
await super.save();
}
}
3 changes: 3 additions & 0 deletions client/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export default class Router extends EmberRouterScroll {
Router.map(function() {
// eslint-disable-line
this.route('projects');

this.route(config.featureFlagSelfService ? 'projects/new' : 'not-found', { path: 'projects/new' });

this.route('login');
this.route('logout');

Expand Down
11 changes: 11 additions & 0 deletions client/app/routes/projects/new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default class ProjectsNewRoute extends Route.extend(AuthenticatedRouteMixin) {
@service store;

async model() {
return await this.store.createRecord('project');
}
}
10 changes: 10 additions & 0 deletions client/app/templates/projects/new.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Ui::Breadcrumbs as | Crumb |>
<Crumb @text="My Projects" @route="projects" />
<Crumb @text="New" @current={{true}} />
</Ui::Breadcrumbs>

<Packages::Projects::New
@package={{@model}}
/>

{{outlet}}
18 changes: 18 additions & 0 deletions client/app/validations/submittable-projects-new-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
validateLength,
validatePresence,
} from 'ember-changeset-validations/validators';

export default {
dcpProjectname: [
validateLength({
min: 0,
max: 50,
message: 'Text is too long (max {max} characters)',
}),
validatePresence({
presence: true,
message: 'This field is required',
}),
],
};
11 changes: 11 additions & 0 deletions client/tests/unit/routes/projects/new-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Route | projects/new', function(hooks) {
setupTest(hooks);

test('it exists', function(assert) {
const route = this.owner.lookup('route:projects/new');
assert.ok(route);
});
});
17 changes: 16 additions & 1 deletion server/src/projects/projects.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {
Get,
Patch,
Body,
Query,
HttpException,
HttpStatus,
Session,
UseInterceptors,
UseGuards,
UsePipes,
Param,
Post,
} from '@nestjs/common';
import { ProjectsService } from './projects.service';
import { ConfigService } from '../config/config.service';
Expand Down Expand Up @@ -129,6 +129,21 @@ export class ProjectsController {
}
}

@Post('/')
async createProject(@Body() body) {
if (!this.config.featureFlag.selfService) {
throw new HttpException({
code: "NOT_FOUND",
title: "Not found",
},
HttpStatus.NOT_FOUND)
}

return {
...body
}
}

@Get('/:id')
async projectById(@Param('id') id) {
try {
Expand Down
Loading