Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "neighborhood_amenities_enum" AS ENUM ('groceryStores', 'publicTransportation', 'schools', 'parksAndCommunityCenters', 'pharmacies', 'healthCareResources');

-- AlterTable
ALTER TABLE "jurisdictions" ADD COLUMN "visible_neighborhood_amenities" "neighborhood_amenities_enum"[] DEFAULT ARRAY['groceryStores', 'publicTransportation', 'schools', 'parksAndCommunityCenters', 'pharmacies', 'healthCareResources']::"neighborhood_amenities_enum"[];
88 changes: 50 additions & 38 deletions api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,12 @@ model ApplicationSelectionOptions {
addressHolderAddressId String? @unique() @map("address_holder_address_id") @db.Uuid
addressHolderName String? @map("address_holder_name")
addressHolderRelationship String? @map("address_holder_relationship")
applicationSelectionId String @map("application_selection_id") @db.Uuid
isGeocodingVerified Boolean? @map("is_geocoding_verified")
multiselectOptionId String @map("multiselect_option_id") @db.Uuid
addressHolderAddress Address? @relation("application_selection_address", fields: [addressHolderAddressId], references: [id], onDelete: NoAction, onUpdate: NoAction)
applicationSelections ApplicationSelections @relation(fields: [applicationSelectionId], references: [id], onDelete: NoAction, onUpdate: NoAction)
multiselectOption MultiselectOptions @relation(fields: [multiselectOptionId], references: [id], onDelete: NoAction, onUpdate: NoAction)
applicationSelectionId String @map("application_selection_id") @db.Uuid
isGeocodingVerified Boolean? @map("is_geocoding_verified")
multiselectOptionId String @map("multiselect_option_id") @db.Uuid
addressHolderAddress Address? @relation("application_selection_address", fields: [addressHolderAddressId], references: [id], onDelete: NoAction, onUpdate: NoAction)
applicationSelections ApplicationSelections @relation(fields: [applicationSelectionId], references: [id], onDelete: NoAction, onUpdate: NoAction)
multiselectOption MultiselectOptions @relation(fields: [multiselectOptionId], references: [id], onDelete: NoAction, onUpdate: NoAction)

@@map("application_selection_options")
}
Expand All @@ -195,9 +195,9 @@ model ApplicationSelections {
applicationId String @map("application_id") @db.Uuid
hasOptedOut Boolean? @map("has_opted_out")
multiselectQuestionId String @map("multiselect_question_id") @db.Uuid
application Applications @relation(fields: [applicationId], references: [id], onDelete: NoAction, onUpdate: NoAction)
multiselectQuestion MultiselectQuestions @relation(fields: [multiselectQuestionId], references: [id], onDelete: NoAction, onUpdate: NoAction)
selections ApplicationSelectionOptions[]
application Applications @relation(fields: [applicationId], references: [id], onDelete: NoAction, onUpdate: NoAction)
multiselectQuestion MultiselectQuestions @relation(fields: [multiselectQuestionId], references: [id], onDelete: NoAction, onUpdate: NoAction)
selections ApplicationSelectionOptions[]

@@map("application_selections")
}
Expand Down Expand Up @@ -368,34 +368,35 @@ model HouseholdMember {

// Note: [name] formerly max length 256
model Jurisdictions {
id String @id() @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(6)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(6)
name String @unique()
notificationsSignUpUrl String? @map("notifications_sign_up_url")
languages LanguagesEnum[] @default([en])
partnerTerms String? @map("partner_terms")
publicUrl String @default("") @map("public_url")
emailFromAddress String? @map("email_from_address")
rentalAssistanceDefault String @map("rental_assistance_default")
whatToExpect String @default("Applicants will be contacted by the property agent in rank order until vacancies are filled. All of the information that you have provided will be verified and your eligibility confirmed. Your application will be removed from the waitlist if you have made any fraudulent statements. If we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized. Should your application be chosen, be prepared to fill out a more detailed application and provide required supporting documents.") @map("what_to_expect")
whatToExpectAdditionalText String @default("") @map("what_to_expect_additional_text")
whatToExpectUnderConstruction String @default("") @map("what_to_expect_under_construction")
enablePartnerSettings Boolean @default(false) @map("enable_partner_settings")
enablePartnerDemographics Boolean @default(false) @map("enable_partner_demographics")
enableGeocodingPreferences Boolean @default(false) @map("enable_geocoding_preferences")
enableGeocodingRadiusMethod Boolean @default(false) @map("enable_geocoding_radius_method")
allowSingleUseCodeLogin Boolean @default(false) @map("allow_single_use_code_login")
id String @id() @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(6)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(6)
name String @unique()
notificationsSignUpUrl String? @map("notifications_sign_up_url")
languages LanguagesEnum[] @default([en])
partnerTerms String? @map("partner_terms")
publicUrl String @default("") @map("public_url")
emailFromAddress String? @map("email_from_address")
rentalAssistanceDefault String @map("rental_assistance_default")
whatToExpect String @default("Applicants will be contacted by the property agent in rank order until vacancies are filled. All of the information that you have provided will be verified and your eligibility confirmed. Your application will be removed from the waitlist if you have made any fraudulent statements. If we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized. Should your application be chosen, be prepared to fill out a more detailed application and provide required supporting documents.") @map("what_to_expect")
whatToExpectAdditionalText String @default("") @map("what_to_expect_additional_text")
whatToExpectUnderConstruction String @default("") @map("what_to_expect_under_construction")
enablePartnerSettings Boolean @default(false) @map("enable_partner_settings")
enablePartnerDemographics Boolean @default(false) @map("enable_partner_demographics")
enableGeocodingPreferences Boolean @default(false) @map("enable_geocoding_preferences")
enableGeocodingRadiusMethod Boolean @default(false) @map("enable_geocoding_radius_method")
allowSingleUseCodeLogin Boolean @default(false) @map("allow_single_use_code_login")
amiChart AmiChart[]
featureFlags FeatureFlags[]
multiselectQuestions MultiselectQuestions[]
listings Listings[]
reservedCommunityTypes ReservedCommunityTypes[]
translations Translations[]
user_accounts UserAccounts[]
listingApprovalPermissions UserRoleEnum[] @map("listing_approval_permission")
duplicateListingPermissions UserRoleEnum[] @map("duplicate_listing_permissions")
requiredListingFields String[] @default([]) @map("required_listing_fields")
listingApprovalPermissions UserRoleEnum[] @map("listing_approval_permission")
duplicateListingPermissions UserRoleEnum[] @map("duplicate_listing_permissions")
requiredListingFields String[] @default([]) @map("required_listing_fields")
visibleNeighborhoodAmenities NeighborhoodAmenitiesEnum[] @default([groceryStores, publicTransportation, schools, parksAndCommunityCenters, pharmacies, healthCareResources]) @map("visible_neighborhood_amenities")

@@map("jurisdictions")
}
Expand Down Expand Up @@ -973,15 +974,15 @@ model UserAccounts {
}

model UserRoles {
isAdmin Boolean @default(false) @map("is_admin")
isJurisdictionalAdmin Boolean @default(false) @map("is_jurisdictional_admin")
isLimitedJurisdictionalAdmin Boolean @default(false) @map("is_limited_jurisdictional_admin")
isPartner Boolean @default(false) @map("is_partner")
isSupportAdmin Boolean @default(false) @map("is_support_admin")
isAdmin Boolean @default(false) @map("is_admin")
isJurisdictionalAdmin Boolean @default(false) @map("is_jurisdictional_admin")
isLimitedJurisdictionalAdmin Boolean @default(false) @map("is_limited_jurisdictional_admin")
isPartner Boolean @default(false) @map("is_partner")
isSupportAdmin Boolean @default(false) @map("is_support_admin")
// Role for maintainers of the code base. Should have access to everything as well as developer specific pages
isSuperAdmin Boolean @default(false) @map("is_super_admin")
userId String @id() @map("user_id") @db.Uuid
userAccounts UserAccounts @relation(fields: [userId], references: [id], onDelete: Cascade)
isSuperAdmin Boolean @default(false) @map("is_super_admin")
userId String @id() @map("user_id") @db.Uuid
userAccounts UserAccounts @relation(fields: [userId], references: [id], onDelete: Cascade)

@@map("user_roles")
}
Expand Down Expand Up @@ -1295,3 +1296,14 @@ enum ValidationMethodEnum {

@@map("validation_method_enum")
}

enum NeighborhoodAmenitiesEnum {
groceryStores
publicTransportation
schools
parksAndCommunityCenters
pharmacies
healthCareResources

@@map("neighborhood_amenities_enum")
}
9 changes: 8 additions & 1 deletion api/prisma/seed-helpers/jurisdiction-factory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { LanguagesEnum, Prisma, UserRoleEnum } from '@prisma/client';
import {
LanguagesEnum,
NeighborhoodAmenitiesEnum,
Prisma,
UserRoleEnum,
} from '@prisma/client';
import { randomName } from './word-generator';

export const jurisdictionFactory = (
Expand All @@ -9,6 +14,7 @@ export const jurisdictionFactory = (
featureFlags?: string[];
requiredListingFields?: string[];
languages?: LanguagesEnum[];
visibleNeighborhoodAmenities?: NeighborhoodAmenitiesEnum[];
},
): Prisma.JurisdictionsCreateInput => ({
name: jurisdictionName,
Expand Down Expand Up @@ -43,4 +49,5 @@ export const jurisdictionFactory = (
}
: undefined,
requiredListingFields: optionalFields?.requiredListingFields || [],
visibleNeighborhoodAmenities: optionalFields?.visibleNeighborhoodAmenities,
});
7 changes: 6 additions & 1 deletion api/prisma/seed-staging.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ApplicationSubmissionTypeEnum,
NeighborhoodAmenitiesEnum,
LanguagesEnum,
MonthlyRentDeterminationTypeEnum,
MultiselectQuestions,
Expand Down Expand Up @@ -155,7 +156,11 @@ export const stagingSeed = async (
});
const angelopolisJurisdiction = await prismaClient.jurisdictions.create({
data: jurisdictionFactory('Angelopolis', {
featureFlags: [],
featureFlags: [FeatureFlagEnum.enableNeighborhoodAmenities],
visibleNeighborhoodAmenities: [
NeighborhoodAmenitiesEnum.groceryStores,
NeighborhoodAmenitiesEnum.pharmacies,
],
requiredListingFields: [
'listingsBuildingAddress',
'name',
Expand Down
20 changes: 19 additions & 1 deletion api/src/dtos/jurisdictions/jurisdiction.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
IsBoolean,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { LanguagesEnum, UserRoleEnum } from '@prisma/client';
import {
LanguagesEnum,
UserRoleEnum,
NeighborhoodAmenitiesEnum,
} from '@prisma/client';
import { FeatureFlag } from '../feature-flags/feature-flag.dto';
import { AbstractDTO } from '../shared/abstract.dto';
import { IdDTO } from '../shared/id.dto';
Expand Down Expand Up @@ -160,4 +164,18 @@ export class Jurisdiction extends AbstractDTO {
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty({ isArray: true })
requiredListingFields: string[];

@Expose()
@IsArray({ groups: [ValidationsGroupsEnum.default] })
@IsEnum(NeighborhoodAmenitiesEnum, {
groups: [ValidationsGroupsEnum.default],
each: true,
})
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty({
enum: NeighborhoodAmenitiesEnum,
enumName: 'NeighborhoodAmenitiesEnum',
isArray: true,
})
visibleNeighborhoodAmenities: NeighborhoodAmenitiesEnum[];
}
65 changes: 38 additions & 27 deletions api/src/services/listing-csv-export.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ApplicationMethodsTypeEnum,
ListingEventsTypeEnum,
MarketingTypeEnum,
NeighborhoodAmenitiesEnum,
} from '@prisma/client';
import { includeViews } from './listing.service';
import { PrismaService } from './prisma.service';
Expand Down Expand Up @@ -806,34 +807,44 @@ export class ListingCsvExporterService implements CsvExporterServiceInterface {
FeatureFlagEnum.enableNeighborhoodAmenities,
)
) {
headers.push(
...[
{
path: 'listingNeighborhoodAmenities.groceryStores',
label: 'Neighborhood Amenities - Grocery Stores',
},
{
path: 'listingNeighborhoodAmenities.publicTransportation',
label: 'Neighborhood Amenities - Public Transportation',
},
{
path: 'listingNeighborhoodAmenities.schools',
label: 'Neighborhood Amenities - Schools',
},
{
path: 'listingNeighborhoodAmenities.parksAndCommunityCenters',
label: 'Neighborhood Amenities - Parks and Community Centers',
},
{
path: 'listingNeighborhoodAmenities.pharmacies',
label: 'Neighborhood Amenities - Pharmacies',
},
{
path: 'listingNeighborhoodAmenities.healthCareResources',
label: 'Neighborhood Amenities - Health Care Resources',
},
],
const visibleAmenities = new Set<string>(
(user.jurisdictions || [])
.flatMap((j) => j.visibleNeighborhoodAmenities || [])
.filter(Boolean),
);

const amenityHeaderMap: Record<string, CsvHeader> = {
[NeighborhoodAmenitiesEnum.groceryStores]: {
path: 'listingNeighborhoodAmenities.groceryStores',
label: 'Neighborhood Amenities - Grocery Stores',
},
[NeighborhoodAmenitiesEnum.publicTransportation]: {
path: 'listingNeighborhoodAmenities.publicTransportation',
label: 'Neighborhood Amenities - Public Transportation',
},
[NeighborhoodAmenitiesEnum.schools]: {
path: 'listingNeighborhoodAmenities.schools',
label: 'Neighborhood Amenities - Schools',
},
[NeighborhoodAmenitiesEnum.parksAndCommunityCenters]: {
path: 'listingNeighborhoodAmenities.parksAndCommunityCenters',
label: 'Neighborhood Amenities - Parks and Community Centers',
},
[NeighborhoodAmenitiesEnum.pharmacies]: {
path: 'listingNeighborhoodAmenities.pharmacies',
label: 'Neighborhood Amenities - Pharmacies',
},
[NeighborhoodAmenitiesEnum.healthCareResources]: {
path: 'listingNeighborhoodAmenities.healthCareResources',
label: 'Neighborhood Amenities - Health Care Resources',
},
};

Object.keys(amenityHeaderMap).forEach((key) => {
if (visibleAmenities.has(key)) {
headers.push(amenityHeaderMap[key]);
}
});
}

headers.push(
Expand Down
14 changes: 13 additions & 1 deletion api/test/integration/jurisdiction.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { LanguagesEnum } from '@prisma/client';
import { LanguagesEnum, NeighborhoodAmenitiesEnum } from '@prisma/client';
import { randomUUID } from 'crypto';
import request from 'supertest';
import cookieParser from 'cookie-parser';
Expand Down Expand Up @@ -135,6 +135,10 @@ describe('Jurisdiction Controller Tests', () => {
listingApprovalPermissions: [],
duplicateListingPermissions: [],
requiredListingFields: [],
visibleNeighborhoodAmenities: [
NeighborhoodAmenitiesEnum.groceryStores,
NeighborhoodAmenitiesEnum.pharmacies,
],
};
const res = await request(app.getHttpServer())
.post('/jurisdictions')
Expand Down Expand Up @@ -165,6 +169,10 @@ describe('Jurisdiction Controller Tests', () => {
listingApprovalPermissions: [],
duplicateListingPermissions: [],
requiredListingFields: [],
visibleNeighborhoodAmenities: [
NeighborhoodAmenitiesEnum.groceryStores,
NeighborhoodAmenitiesEnum.pharmacies,
],
};
const res = await request(app.getHttpServer())
.put(`/jurisdictions/${id}`)
Expand Down Expand Up @@ -199,6 +207,10 @@ describe('Jurisdiction Controller Tests', () => {
listingApprovalPermissions: [],
duplicateListingPermissions: [],
requiredListingFields: [],
visibleNeighborhoodAmenities: [
NeighborhoodAmenitiesEnum.groceryStores,
NeighborhoodAmenitiesEnum.pharmacies,
],
};

const res = await request(app.getHttpServer())
Expand Down
2 changes: 2 additions & 0 deletions api/test/integration/permission-tests/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export const buildJurisdictionCreateMock = (
listingApprovalPermissions: [],
duplicateListingPermissions: [],
requiredListingFields: [],
visibleNeighborhoodAmenities: [],
};
};

Expand All @@ -138,6 +139,7 @@ export const buildJurisdictionUpdateMock = (
listingApprovalPermissions: [],
duplicateListingPermissions: [],
requiredListingFields: [],
visibleNeighborhoodAmenities: [],
};
};

Expand Down
18 changes: 18 additions & 0 deletions shared-helpers/src/types/backend-swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5960,6 +5960,9 @@ export interface JurisdictionCreate {

/** */
requiredListingFields: []

/** */
visibleNeighborhoodAmenities: NeighborhoodAmenitiesEnum[]
}

export interface JurisdictionUpdate {
Expand Down Expand Up @@ -6019,6 +6022,9 @@ export interface JurisdictionUpdate {

/** */
requiredListingFields: []

/** */
visibleNeighborhoodAmenities: NeighborhoodAmenitiesEnum[]
}

export interface FeatureFlag {
Expand Down Expand Up @@ -6113,6 +6119,9 @@ export interface Jurisdiction {

/** */
requiredListingFields: []

/** */
visibleNeighborhoodAmenities: NeighborhoodAmenitiesEnum[]
}

export interface MultiselectQuestionCreate {
Expand Down Expand Up @@ -7541,6 +7550,15 @@ export enum UserRoleEnum {
"supportAdmin" = "supportAdmin",
}

export enum NeighborhoodAmenitiesEnum {
"groceryStores" = "groceryStores",
"publicTransportation" = "publicTransportation",
"schools" = "schools",
"parksAndCommunityCenters" = "parksAndCommunityCenters",
"pharmacies" = "pharmacies",
"healthCareResources" = "healthCareResources",
}

export enum FeatureFlagEnum {
"disableCommonApplication" = "disableCommonApplication",
"disableJurisdictionalAdmin" = "disableJurisdictionalAdmin",
Expand Down
Loading
Loading