diff --git a/api/src/application/application.entity.ts b/api/src/application/application.entity.ts index 0d8a7d6..612f136 100644 --- a/api/src/application/application.entity.ts +++ b/api/src/application/application.entity.ts @@ -5,6 +5,7 @@ import { Comment } from '../comments/comment.entity'; import { FormMetaData } from '../FormMetaData/formmetadata.entity'; import { RemovableBaseEntity } from '../common/removable-base.entity'; import { User } from '../user/user.entity'; +import { WorkshopScore } from '@/score/workshop-score.entity'; @Entity({ name: 'BCAT_APPLICATION', @@ -47,6 +48,10 @@ export class Application extends RemovableBaseEntity { @JoinColumn({ name: 'FORM_METADATA_ID' }) form: FormMetaData; + @OneToMany(() => WorkshopScore, (workshopScore) => workshopScore.application) + @JoinColumn() + workshopScores?: WorkshopScore[]; + // Might belong to multiple users in the future, so // change to ManyToMany accordingly if needed. @ManyToOne(() => User, (user) => user.applications) diff --git a/api/src/application/application.service.ts b/api/src/application/application.service.ts index 55f340f..361445f 100644 --- a/api/src/application/application.service.ts +++ b/api/src/application/application.service.ts @@ -18,7 +18,7 @@ import { FormMetaData } from '../FormMetaData/formmetadata.entity'; import { GenericException } from '../common/generic-exception'; import { GetApplicationsDto } from '../common/dto/get-applications.dto'; import { PaginationRO } from '../common/ro/pagination.ro'; -import { RawDataRo } from '@/score/ro/raw-data.ro'; +import { RawDataRo } from '../score/ro/raw-data.ro'; import { SaveApplicationDto } from '../common/dto/save-application.dto'; import { ScoreDto } from '../score/dto/score.dto'; import { UpdateStatusDto } from './dto/update-status.dto'; @@ -258,13 +258,19 @@ export class ApplicationService { 'a.projectTitle', 'a.totalEstimatedCost', 'a.updatedAt', + 'a.submission', 'applicationType.name', 'status.name', 'user.displayName', + 'workshopScore.data', ]) .leftJoin('a.assignedTo', 'user') .leftJoin('a.status', 'status') .leftJoin('a.applicationType', 'applicationType') + .leftJoin('a.workshopScores', 'workshopScore') + .where('status.name NOT ILIKE :rejectedStatus', { + rejectedStatus: `%${ApplicationStatus.DENIED}%`, + }) .getMany(); } } diff --git a/api/src/database/scripts/sync-chefs-data.service.ts b/api/src/database/scripts/sync-chefs-data.service.ts index 60146d6..7bb2bd8 100644 --- a/api/src/database/scripts/sync-chefs-data.service.ts +++ b/api/src/database/scripts/sync-chefs-data.service.ts @@ -111,6 +111,7 @@ export class SyncChefsDataService { responseType, }; const files = await this.attachmentService.getAllAttachments(false); + if (!token) Logger.error(`No TOKEN found`); for (const file of files) { try { @@ -118,7 +119,9 @@ export class SyncChefsDataService { // Get file data form server const url = FILE_URL + file.url; const fileRes = await axios({ ...options, url }); + Logger.log(`File fetched successfully - ${file.id}`); const fileData = Buffer.from(fileRes.data); + Logger.log(`Buffer extracted successfully - ${file.id}`); file.data = fileData; await this.attachmentService.updateAttachment(file); } catch (error) { @@ -126,7 +129,7 @@ export class SyncChefsDataService { `Error occurred fetching attachment - ${file.id} - `, JSON.stringify(getGenericError(error)) ); - throw new GenericException(SyncDataError.SYNC_ATTACHMENT_ERROR); + // throw new GenericException(SyncDataError.SYNC_ATTACHMENT_ERROR); } } } diff --git a/api/src/score/ro/raw-data.ro.ts b/api/src/score/ro/raw-data.ro.ts index 37c2054..55d761e 100644 --- a/api/src/score/ro/raw-data.ro.ts +++ b/api/src/score/ro/raw-data.ro.ts @@ -1,62 +1,262 @@ import dayjs from 'dayjs'; -import { Application } from '@/application/application.entity'; +import { Application } from '../../application/application.entity'; +import { ApplicationType } from '../../common/constants'; + +// { label: 'Withdrawn / Cancelled', value: 'emptyVal' }, const roHeaders = { sheet: 'Raw Data', columns: [ + { label: 'Funding Year', value: 'fundingYear' }, + { label: 'Electoral District', value: 'emptyVal' }, + { label: 'Regional District', value: 'emptyVal' }, + { label: 'Economic Development Region', value: 'emptyVal' }, { label: 'Applicant Name', value: 'applicantName' }, + { label: 'Applicant Type', value: 'applicantType' }, + { label: 'Indigenous Government', value: 'indigenousGovernment' }, + { label: 'Population', value: 'population', format: '#,##0' }, + { label: 'CGA', value: 'emptyVal' }, { label: 'Application Type', value: 'applicationType' }, { label: 'Project Title', value: 'projectTitle' }, - { label: 'Estimated Project Cost', value: 'totalEstimatedCost' }, - { label: 'Raw Ask', value: 'asks' }, + { label: 'Project Description', value: 'projectDescription' }, + { label: 'Project Type', value: 'emptyVal' }, + { label: 'Eligibility', value: 'eligibility' }, + { label: 'Project Length', value: 'emptyVal' }, + { label: 'Latitude Start', value: 'latitudeStart' }, + { label: 'Latitude End', value: 'latitudeEnd' }, + { label: 'Longitude Start', value: 'longitudeStart' }, + { label: 'Longitude End', value: 'longitudeEnd' }, + { label: 'Program', value: 'program' }, + { label: 'Estimated Project Cost', value: 'totalEstimatedCost', format: '$#,##0.00' }, + { + label: 'Total Eligible Project Cost', + value: 'totalEligibleProjectCost', + format: '$#,##0.00', + }, + { label: 'Raw Ask', value: 'asks', format: '$#,##0.00' }, { label: 'Assigned To', value: 'assignedTo' }, { label: 'Last Update', value: 'lastUpdated' }, { label: 'Status', value: 'status' }, { label: 'Confirmation ID', value: 'confirmationId' }, + { label: 'Workshop Score', value: 'workshopScore' }, + { label: 'Total Actual Payment', value: 'emptyVal', format: '$#,##0.00' }, + { label: 'Project Open', value: 'emptyVal' }, + ], +}; + +const usageCountHeaders = { + sheet: 'Usage Count Data', + columns: [ + { label: 'Municipality', value: 'municipality' }, + { label: 'Project Name', value: 'projectName' }, + { label: 'Funding Year', value: 'fundingYear' }, + { label: 'Usage Count - Date', value: 'usageCountDate' }, + { label: 'Usage Count - Time', value: 'usageCountTime' }, + { label: 'Usage Count - Location', value: 'usageCountLocation' }, + { label: 'Usage Count - Pedestrian Count', value: 'usageCountPedestrian' }, + { label: 'Usage Count - Bicycle Count', value: 'usageCountBicycle' }, + { label: 'Usage Count - Other', value: 'usageCountOther' }, ], }; +interface RawContent { + applicantName: string; + applicantType: string; + applicationType: string; + asks: string; + assignedTo: string; + confirmationId: string; + eligibility: string; + emptyVal: string; + fundingYear: string; + indigenousGovernment: string; + lastUpdated: string; + latitudeEnd: string; + latitudeStart: string; + longitudeEnd: string; + longitudeStart: string; + population: string; + program: string; + projectDescription: string; + projectTitle: string; + status: string; + totalEstimatedCost: string; + workshopScore: string; +} + +interface UsageCountContent { + fundingYear: string; + municipality: string; + projectName: string; + usageCountDate: string; + usageCountTime: string; + usageCountLocation: string; + usageCountPedestrian: string; + usageCountBicycle: string; + usageCountOther: string; +} + export interface RawData { sheet: string; - columns: { label: string; value: string }[]; - - content: { - applicantName: string; - applicationType: string; - projectTitle: string; - totalEstimatedCost: string; - asks: string; - assignedTo: string; - lastUpdated: string; - status: string; - confirmationId: string; - }[]; + columns: { label: string; value: string; format?: string }[]; + content: any[]; } export class RawDataRo { result: RawData[]; constructor(data: Application[]) { - this.result = [{ ...roHeaders, ...this.convertApplicationToContent(data) }]; + this.result = [ + { ...roHeaders, content: this.convertApplicationToContent(data) }, + { ...usageCountHeaders, content: this.convertUsageCountToContent(data) }, + ]; } - convertApplicationToContent(data: Application[]) { + + convertApplicationToContent(data: Application[]): RawContent[] { + const NO_VALUE = '-'; + + const formatDate = (itemDate: any) => { + return dayjs(itemDate).isValid() ? dayjs(itemDate).format('YYYY-MM-DD') : NO_VALUE; + }; + + const checkIfItemHasWorkshopScore = (item: any) => { + return item.workshopScores && item.workshopScores.length > 0; + }; + + const getFundingYear = (signedDate: any) => { + if (!signedDate) return NO_VALUE; + return `${dayjs(signedDate).format('YY')}/${dayjs(signedDate).add(1, 'year').format('YY')}`; + }; + + const checkGovernmentApplicant = (primaryApplicant: string, secondaryApplicant: string) => { + const YES = 'Y'; + const NO = 'N'; + const NA = '-'; + + if (!primaryApplicant) return NA; + + return primaryApplicant.includes('indigenous') || secondaryApplicant?.includes('indigenous') + ? YES + : NO; + }; + + const addScores = (array: any) => { + let sum = 0; + array.forEach((item: any) => { + if (Number.isInteger(item)) { + sum += Number(item); + } + }); + return sum.toString(); + }; + const content = data.map((item: Application) => { - const NO_VALUE = '-'; - const formattedDate = dayjs(item.updatedAt).isValid() - ? dayjs(item.updatedAt).format('YYYY-MM-DD') - : NO_VALUE; + const submission = item.submission || {}; return { + eligibility: checkIfItemHasWorkshopScore(item) + ? item.workshopScores[0].data?.eligibilityScore + : NO_VALUE, + fundingYear: getFundingYear(submission?.s10Container?.s10ProjectMangerApproverDate), applicantName: item.applicantName, + indigenousGovernment: checkGovernmentApplicant( + submission.s1Container?.s1GovernmentType, + submission.s1Container?.s1SecondaryGovernmentType + ), applicationType: item.applicationType?.name || NO_VALUE, + applicantType: submission.s1Container?.s1GovernmentType || NO_VALUE, + population: + submission.s1Container?.s1PrimaryCommunityPopulation || + submission.s1Container?.s1CommunityPopulation, + projectDescription: + submission.s5Container?.s5ProjectInformation || + submission.s3Container?.s3DescriptionHighLevelScopeOutline || + NO_VALUE, + latitudeStart: submission.s5Container?.s6ProjectStartLatitude, + latitudeEnd: submission.s5Container?.s6ProjectEndLatitude, + longitudeStart: submission.s5Container?.s6ProjectStartLongitude, + longitudeEnd: submission.s5Container?.s6ProjectEndLongitude, + program: 'AT', + totalEligibleProjectCost: submission.s8Container?.s8TotalEligibleProjectCost, + workshopScore: checkIfItemHasWorkshopScore(item) + ? addScores(Object.values(item.workshopScores[0].data)) + : NO_VALUE, asks: item.asks, assignedTo: item?.assignedTo?.displayName || NO_VALUE, confirmationId: item.confirmationId, - lastUpdated: formattedDate, + lastUpdated: formatDate(item.updatedAt), projectTitle: item.projectTitle, status: item.status?.name || NO_VALUE, totalEstimatedCost: item.totalEstimatedCost, + emptyVal: '', }; }); - return { content }; + + return content; + } + + convertUsageCountToContent(data: Application[]): UsageCountContent[] { + const NO_VALUE = '-'; + const content = []; + + const formatDate = (itemDate: any) => { + return dayjs(itemDate).isValid() ? dayjs(itemDate).format('YYYY-MM-DD') : NO_VALUE; + }; + + const getFundingYear = (signedDate: any) => { + if (!signedDate) return NO_VALUE; + return `${dayjs(signedDate).format('YY')}/${dayjs(signedDate).add(1, 'year').format('YY')}`; + }; + + for (const item of data) { + if (item.applicationType?.name === ApplicationType.NETWORK_FORM) continue; + + const submission = item.submission || {}; + const usageCountGrid = submission.s5Container?.s5UsageCountFormGrid; + + if (!usageCountGrid) { + const newItem = { + municipality: submission.s1Container?.s1GovernmentType || NO_VALUE, + projectName: + submission.s4Container?.s4ProjectTitle || + submission.s3Container?.s3ProjectTitle || + NO_VALUE, + fundingYear: getFundingYear( + submission.s10Container?.s10ProjectMangerApproverDate || + submission.s11Container?.s11ProjectMangerApproverDate + ), + usageCountDate: NO_VALUE, + usageCountTime: NO_VALUE, + usageCountLocation: NO_VALUE, + usageCountPedestrian: NO_VALUE, + usageCountBicycle: NO_VALUE, + usageCountOther: NO_VALUE, + }; + + content.push(newItem); + continue; + } + + usageCountGrid.forEach((row: any) => { + content.push({ + municipality: item.applicantName || NO_VALUE, + projectName: + submission.s4Container?.s4ProjectTitle || + submission.s3Container?.s3ProjectTitle || + NO_VALUE, + fundingYear: getFundingYear( + submission.s10Container?.s10ProjectMangerApproverDate || + submission.s11Container?.s11ProjectMangerApproverDate + ), + usageCountDate: formatDate(row.dateCell), + usageCountTime: row.countPeriodCell || NO_VALUE, + usageCountLocation: row.stationLocationCell || NO_VALUE, + usageCountPedestrian: row.pedestrianCell, + usageCountBicycle: row.bicycleCell, + usageCountOther: row.otherCell, + }); + }); + } + + return content; } } diff --git a/client/components/application/BroaderReview.tsx b/client/components/application/BroaderReview.tsx index 426ebbd..8a5f75f 100644 --- a/client/components/application/BroaderReview.tsx +++ b/client/components/application/BroaderReview.tsx @@ -121,7 +121,9 @@ export const BroaderReview: React.FC = ({
= ({
15k and <25k pop = 5pts', - 'If Local Govt (non Indigenous) > 25k pop = 0 pts', + 'Section 1 - Government applicant type: If Indigenous Govt or Indigenous Govt Partnership = 15 pts', + 'Section 1 - Government applicant type: If Local (non Indigenous) Govt
  • 15k and <25k pop = 5pts', + 'Section 1 - Government applicant type: If Local Govt (non Indigenous) > 25k pop = 0 pts', ], isAutomated: true, hiddenInput: true, @@ -14,30 +14,30 @@ export const INFRASTRUCTURE_REVIEW_QUESTIONS = [ }, { maxScore: 10, - label: 'Does this project meet community needs and safety guidelines? (manual)', + label: 'Section 6: Does this project meet community needs and safety guidelines? (manual)', descriptionList: [ - 'Listing of BCAT Design Guide or other publication = 1 pt', - 'Explanation of how project aligns with guide = up to 2 pts', - 'Safety addressed w/ supporting rationale = up to 3 pts', - 'Supporting data or anecdotal evidence is provided = up to 2 pts', - 'Safety monitoring plan = 2 pts', + 'Section 6 - Q29: Listing of BCAT Design Guide or other publication = 1 pt', + 'Section 6 - Q29: Explanation of how project aligns with guide = up to 2 pts', + 'Section 6 - Q30: Safety addressed w/ supporting rationale = up to 3 pts', + 'Section 6 - Q30: Supporting data or anecdotal evidence is provided = up to 2 pts', + 'Section 6 - Q31: Safety monitoring plan = 2 pts', ], name: 'communityNeedsAndSafetyGuidelinesScore', }, { maxScore: 23, - label: 'Safety Score', + label: 'Section 6: Safety Score', descriptionList: [ - 'Solid physical barrier, substantial distancing, valid other/alternative approach (traffic calming), end-of-trip facilities only = 5 pt (manual)', - 'Minimal physical barrier, minimal physical distancing, or minimal other/alternative approach = 2 pts (manual)', - 'Not physically separated = 0 pts (manual)', - 'Section 6. Question "The BC Active Transportation Design Guide recommends minimum widths for different types and contexts of active transportation infrastructure (see pg. 15 of the Program Guidelines). Does the proposed infrastructure align with the Design Guide recommendations?" - Only if Local context is selected = 0-4pts (manual)', - 'Section 6. Question "When the project encounters or transitions to another facility type (e.g., a bike lane crossing an intersection, a multi-use path ending at a sidewalk), are there design features in place to minimize conflicts and ensure a safe transition for all intended users?" = 2 pts for each design feature listed, up to 5 pts (manual)', + 'Section 6 - Q32: Solid physical barrier, substantial distancing, valid other/alternative approach (traffic calming), end-of-trip facilities only = 5 pt (manual)', + 'Section 6 - Q32: Minimal physical barrier, minimal physical distancing, or minimal other/alternative approach = 2 pts (manual)', + 'Section 6 - Q32: Not physically separated = 0 pts (manual)', + 'Section 6 - Q34: Question "The BC Active Transportation Design Guide recommends minimum widths for different types and contexts of active transportation infrastructure (see pg. 15 of the Program Guidelines). Does the proposed infrastructure align with the Design Guide recommendations?" - Only if Local context is selected = 0-4pts (manual)', + 'Section 6 - Q36: Question "When the project encounters or transitions to another facility type (e.g., a bike lane crossing an intersection, a multi-use path ending at a sidewalk), are there design features in place to minimize conflicts and ensure a safe transition for all intended users?" = 2 pts for each design feature listed, up to 5 pts (manual)', ], secondaryList: [ - 'Section 6. Question "Identify which additional safety measures exist within the design of your project" = 1 pt each for each box ticked (ex: Lighting, Signage, etc) (auto)', - 'Section 6. Question "The BC Active Transportation Design Guide recommends minimum widths for different types and contexts of active transportation infrastructure (see pg. 15 of the Program Guidelines). Does the proposed infrastructure align with the Design Guide recommendations?" - If Desired, or N/A is selected = 4pts (auto); if Constrained is selected = 2pts (auto);', - 'Section 6. Question "The B.C. Active Transportation Design Guide recommends certain facility types for different road contexts, e.g., speed and volume. Does the proposed infrastructure align with Design Guide recommendations?" - If Yes, or N/A is selected = 4pts (auto)', + 'Section 6 - Q33: Question "Identify which additional safety measures exist within the design of your project" = 1 pt each for each box ticked (ex: Lighting, Signage, etc) (auto)', + 'Section 6 - Q34: Question "The BC Active Transportation Design Guide recommends minimum widths for different types and contexts of active transportation infrastructure (see pg. 15 of the Program Guidelines). Does the proposed infrastructure align with the Design Guide recommendations?" - If Desired, or N/A is selected = 4pts (auto); if Constrained is selected = 2pts (auto);', + 'Section 6 - Q35: Question "The B.C. Active Transportation Design Guide recommends certain facility types for different road contexts, e.g., speed and volume. Does the proposed infrastructure align with Design Guide recommendations?" - If Yes, or N/A is selected = 4pts (auto)', ], isAutomated: true, tooltiptext: @@ -46,10 +46,10 @@ export const INFRASTRUCTURE_REVIEW_QUESTIONS = [ }, { maxScore: 5, - label: 'Economy and Tourism Score (manual)', + label: 'Section 7: Economy and Tourism Score (manual)', descriptionList: [ - 'What are the economic benefits? = up to 3 pts', - 'How will this project contribute to tourism? = up to 2 pts', + 'Section 7 - Q37: What are the economic benefits? = up to 3 pts', + 'Section 7 - Q38: How will this project contribute to tourism? = up to 2 pts', ], tooltiptext: 'Score on how the project connects people to businesses, new business areas, new industry, etc. for economic benefits; Score on how project will protect current tourism or provide new tourism opportunities for tourism;', @@ -57,11 +57,11 @@ export const INFRASTRUCTURE_REVIEW_QUESTIONS = [ }, { maxScore: 8, - label: 'Environment Score (manual)', + label: 'Section 7: Environment Score (manual)', descriptionList: [ - 'Environmental Benefits = up to 3 pts', - 'Will the project retain existing Trees? = up to 2 pts', - 'Environmental Best Practices = up to 3 pts', + 'Section 7 - Q39: Section 7 - Q39: Environmental Benefits = up to 3 pts', + 'Section 7 - Q40/41: Will the project retain existing Trees? = up to 2 pts', + 'Section 7 - Q42: Environmental Best Practices = up to 3 pts', ], tooltiptext: 'Score on: GHG reductions, local measurements of GHG share from transportation, local sustainability plans for Environmental Benefits; Score on: local materials and labour, climate adaptation measures, dust mitigation, using recycled materials, drought-friendly plantings, using less toxic materials for Environmental Best Practices;', @@ -69,25 +69,25 @@ export const INFRASTRUCTURE_REVIEW_QUESTIONS = [ }, { maxScore: 15, - label: 'Land Use Score', + label: 'Section 7: Land Use Score', descriptionList: [ - 'Does this project fill gaps between 2 or more AT Facilities = 2 pts (manual)', - 'Is this project a component of larger infrastructure project = 3 pts (manual)', + 'Section 7 - Q46: Does this project fill gaps between 2 or more AT Facilities = 2 pts (manual)', + 'Section 7 - Q47 / Explain how this project creates connections for your communitys active transportation network: Is this project a component of larger infrastructure project = 3 pts (manual)', ], secondaryList: [ - 'Multi-modal Integration = 1 pt for each box, max 3 (auto)', - 'Connects w/ community infrastructure = 1 pt for each box, max 4 (auto)', - 'Connect w/ AT infrastructure = 1 pt for each box, max 3 (auto)', + 'Section 7 - Q43: Multi-modal Integration = 1 pt for each box, max 3 (auto)', + 'Section 7 - Q44: Connects w/ community infrastructure = 1 pt for each box, max 4 (auto)', + 'Section 7 - Q45: Connect w/ AT infrastructure = 1 pt for each box, max 3 (auto)', ], isAutomated: true, name: 'landUseScore', }, { maxScore: 8, - label: 'Accessibility Score (manual)', + label: 'Section 7: Accessibility Score (manual)', descriptionList: [ - 'Does this Project Incorporate Universal Design? = up to 5 pts', - 'Does This Project Incorporate GBA+ Principles? = up to 3 pts', + 'Section 7 - Q48: Does this Project Incorporate Universal Design? = up to 5 pts', + 'Section 7 - Q49 (How does this project incorporate principles of GBA): Does This Project Incorporate GBA+ Principles? = up to 3 pts', ], tooltiptext: 'Score on: curb cuts, grading, smooth surfaces, ramps, width, accessible washrooms, lighting, handrails, TWSIs, audible crossing signals, etc.; Score on: lighting, gender-neutral and family friendly washrooms, economically disadvantaged area, GBA+ training by project team, age-friendly design, rainbow crosswalks, signage in other languages, Indigenous land acknowledgements/ names on wayfinding signage, etc.;', @@ -95,41 +95,43 @@ export const INFRASTRUCTURE_REVIEW_QUESTIONS = [ }, { maxScore: 3, - label: 'Promotion Score (manual)', + label: 'Section 7: Promotion Score (manual)', tooltiptext: 'Score on promotional & educational activities = media event, signage, advertising, bike/ped maps, cycling courses, targeted outreach', - descriptionList: ['1 pt for each promotional feature listed, max 3'], + descriptionList: ['Section 7 - Q50: 1 pt for each promotional feature listed, max 3'], name: 'promotionScore', }, { maxScore: 3, label: 'Letters of Support (manual)', - descriptionList: ['1 pt for each letter of support, max 3'], + descriptionList: [ + 'Section 10 Letter(s) of support (if applicable): 1 pt for each letter of support, max 3', + ], name: 'lettersOfSupportScore', }, { maxScore: 5, - label: 'Previous funding (manual)', + label: 'Section 8: Previous funding (manual)', descriptionList: [ - 'None = 5 pts', - 'less than (<) $500,000 = 3 pts', - 'more than (>) $500,000 = 0 pts', + 'Section 8: None = 5 pts', + 'Section 8: less than (<) $500,000 = 3 pts', + 'Section 8: more than (>) $500,000 = 0 pts', ], name: 'previousFundingScore', tooltiptext: 'Funding Amount over last 5 years', }, { maxScore: 5, - label: 'Regional Adjustment Scoring (manual)', + label: 'Section 1: Regional Adjustment Scoring (manual)', descriptionList: [ - 'Vancouver Island/Coast = 1', - 'Lower Mainland /Southwest = 0', - 'Thompson-Okanagan = 2', - 'Kootenay = 3', - 'Cariboo = 4', - 'North Coast = 5', - 'Nechako = 5', - 'Northeast = 5', + 'Section 1 - City: Vancouver Island/Coast = 1', + 'Section 1 - City: Lower Mainland /Southwest = 0', + 'Section 1 - City: Thompson-Okanagan = 2', + 'Section 1 - City: Kootenay = 3', + 'Section 1 - City: Cariboo = 4', + 'Section 1 - City: North Coast = 5', + 'Section 1 - City: Nechako = 5', + 'Section 1 - City: Northeast = 5', ], name: 'regionalAdjustmentScore', }, @@ -157,24 +159,25 @@ export const INITIAL_INFRASTRUCTURE_REVIEW_VALUES = { export const NETWORK_REVIEW_QUESTIONS = [ { maxScore: 5, - label: 'Is this project a reasonable cost for the community size? (manual)', + label: + 'Section 1 / Section 8: Is this project a reasonable cost for the community size? (manual)', name: 'reasonableCostforCommunitySize', descriptionList: [ - '5 points = <$75', - '4 points = $75-100', - '3 points= $100-150', - '1 point = $100-200', - '0 points = >$200', + 'Section 1 / Section 8: 5 points = <$75', + 'Section 1 / Section 8: 4 points = $75-100', + 'Section 1 / Section 8: 3 points= $100-150', + 'Section 1 / Section 8: 1 point = $100-200', + 'Section 1 / Section 8: 0 points = >$200', ], tooltiptext: 'Divide Total Estimated Project Cost by Community Population', }, { maxScore: 4, - label: 'Section 3. Components score (auto)', + label: 'Section 3: Components score (auto)', name: 's3ComponentsScore', secondaryList: [ - `1 point each for a 'yes' answer to questions 6-8 (automated)`, - '1 point for at least 2 boxes checked on question 9. Up to 4 points total', + `Section 3 - Q6-Q8: 1 point each for a 'yes' answer to questions 6-8 (automated)`, + 'Section 3 - Q9: 1 point for at least 2 boxes checked on question 9. Up to 4 points total', ], isAutomated: true, hiddenInput: true, @@ -183,46 +186,62 @@ export const NETWORK_REVIEW_QUESTIONS = [ maxScore: 1, label: 'Section 4: Describe how the ATNP aligns with community goals (manual)', name: 's4DescribeHowATNPAlignsWithCommunityGoals', - descriptionList: ['1 point for thoughtful, good-quality answer, 0 points otherwise'], + descriptionList: [ + 'Section 4 - Q10: 1 point for thoughtful, good-quality answer, 0 points otherwise', + ], }, { maxScore: 1, label: 'Section 4: Describe the potential economic benefits to your community (manual)', name: 's4DescribePotentialEconomicBenefits', - descriptionList: ['1 point for thoughtful, good-quality answer, 0 points otherwise'], + descriptionList: [ + 'Section 4 - Q11: 1 point for thoughtful, good-quality answer, 0 points otherwise', + ], }, { maxScore: 1, label: 'Section 5: Please provide details on how the Active Transportation Network Plan will address safety concerns (manual)', name: 's5DetailsHowATNPWillAddressSafetyConcerns', - descriptionList: ['1 point for thoughtful, good-quality answer, 0 points otherwise'], + descriptionList: [ + 'Section 5 - Q12: 1 point for thoughtful, good-quality answer, 0 points otherwise', + ], }, { maxScore: 1, label: 'Section 6: Describe any consultation and/or engagement you will be undertaking (manual)', name: 's6DescribeConsultationUndertaking', - descriptionList: ['1 point for thoughtful, good-quality answer, 0 points otherwise'], + descriptionList: [ + 'Section 6 - Q14: 1 point for thoughtful, good-quality answer, 0 points otherwise', + ], }, { maxScore: 1, label: 'Section 6: Describe any data collection you will be undertaking (manual)', name: 's6DescribeDataCollectionUndertaking', - descriptionList: ['1 point for thoughtful, good-quality answer, 0 points otherwise'], + descriptionList: [ + 'Section 6 - Q15: 1 point for thoughtful, good-quality answer, 0 points otherwise', + ], }, { maxScore: 1, label: 'Section 6: Describe how you will monitor the ATNP Implementation to ensure success (manual)', name: 's6DescribeHowATNPImplementationWillEnsureSuccess', - descriptionList: ['1 point for thoughtful, good-quality answer, 0 points otherwise'], + descriptionList: [ + 'Section 6 - Q16: 1 point for thoughtful, good-quality answer, 0 points otherwise', + ], }, { maxScore: 5, - label: 'Funding received over the last 5 years (manual)', + label: 'Section 8: Funding received over the last 5 years (manual)', name: 'fundingReceivedLastFiveYears', - descriptionList: ['None = 5 pts', 'less than (<) $500,000 = 3 pts', '$500,000 or more = 0 pts'], + descriptionList: [ + 'Section 8: None = 5 pts', + 'Section 8: less than (<) $500,000 = 3 pts', + 'Section 8: $500,000 or more = 0 pts', + ], }, ]; diff --git a/client/helpers/calculate-automated-scores.helpers.tsx b/client/helpers/calculate-automated-scores.helpers.tsx index 5d739d3..9b44818 100644 --- a/client/helpers/calculate-automated-scores.helpers.tsx +++ b/client/helpers/calculate-automated-scores.helpers.tsx @@ -29,6 +29,7 @@ const calculateSafetyScore = (s6Container: any) => { let score = 0; + if (s6Container.s6AdditionalSafetyMaintenancePlan) score++; if (s6Container.s6AdditionalSafetyLighting) score++; if (s6Container.s6AdditionalSafetyLoweredSpeedLimit) score++; if (s6Container.s6AdditionalSafetySignage) score++; diff --git a/client/helpers/render-elements.helpers.tsx b/client/helpers/render-elements.helpers.tsx index 232dcf7..e73d5ea 100644 --- a/client/helpers/render-elements.helpers.tsx +++ b/client/helpers/render-elements.helpers.tsx @@ -159,13 +159,20 @@ const renderFile = (e: any, data: any, fetchData: any) => { return (
    {label} - {files.length === 1 ? ( - - ) : ( - NO_DATA_LABEL - )} +
    + {files.length > 0 + ? files.map((item: any) => ( + + )) + : NO_DATA_LABEL} +
    ); }; @@ -289,7 +296,7 @@ const renderUsageCountForm = (e: any, data: any) => { {usageFormData && usageFormData.map((ad: any, index: number) => ( {formInfo.map((item, tdIndex) => { diff --git a/client/pages/applications/[id].tsx b/client/pages/applications/[id].tsx index 61651b9..f414d58 100644 --- a/client/pages/applications/[id].tsx +++ b/client/pages/applications/[id].tsx @@ -68,6 +68,13 @@ const ApplicationDetails: NextPage = () => { ); }; + const getColSpan = () => { + if (applicationStatus === ApplicationStatus.WORKSHOP) { + return showComments ? 'col-span-1' : 'col-span-2'; + } + return showComments ? 'col-span-2' : 'col-span-3'; + }; + return ( <> {details && id && typeof id === 'number' && ( @@ -139,15 +146,7 @@ const ApplicationDetails: NextPage = () => {
  • -
    +
    {schema?.length > 0 && formData && schema @@ -178,13 +177,8 @@ const ApplicationDetails: NextPage = () => { ))}
    - {showComments && id && typeof id === 'number' && ( -
    - setShowComments(false)} /> -
    - )} {details && applicationType && ( -
    +
    {
    )} {details && applicationType && applicationStatus === ApplicationStatus.WORKSHOP && ( -
    +
    { />
    )} + {showComments && id && typeof id === 'number' && ( +
    + setShowComments(false)} /> +
    + )}
    )}