diff --git a/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts b/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts index c788a891..d5a6387e 100644 --- a/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts +++ b/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts @@ -1,5 +1,4 @@ -import { CampaignApplicationState, CampaignTypeCategory } from '@prisma/client' -import { CreateCampaignApplicationDto } from '../dto/create-campaign-application.dto' +import { CampaignApplicationState } from '@prisma/client' export const mockNewCampaignApplication = { campaignName: 'Test Campaign', @@ -15,7 +14,9 @@ export const mockNewCampaignApplication = { campaignGuarantee: 'Test guarantee', otherFinanceSources: 'Test otherFinanceSources', otherNotes: 'Test otherNotes', - category: CampaignTypeCategory.medical, + campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af', + campaignEnd: 'funds', + campaignEndDate: '2024-02-02', } export const mockSingleCampaignApplication = { @@ -37,10 +38,12 @@ export const mockSingleCampaignApplication = { otherFinanceSources: 'test otherFinanceSources1', otherNotes: 'test otherNotes1', state: CampaignApplicationState.review, - category: CampaignTypeCategory.medical, + campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af', ticketURL: 'testsodifhso1', archived: false, documents: [{ id: 'fileId' }], + campaignEnd: 'funds', + campaignEndDate: undefined, } export const mockCampaigns = [ @@ -63,7 +66,7 @@ export const mockCampaigns = [ otherFinanceSources: 'test otherFinanceSources1', otherNotes: 'test otherNotes1', state: CampaignApplicationState.review, - category: CampaignTypeCategory.medical, + campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af', ticketURL: 'testsodifhso1', archived: false, }, @@ -86,7 +89,7 @@ export const mockCampaigns = [ otherFinanceSources: 'test otherFinanceSources2', otherNotes: 'test otherNotes2', state: CampaignApplicationState.review, - category: CampaignTypeCategory.medical, + campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af', ticketURL: 'testsodifhso2', archived: false, }, @@ -117,42 +120,7 @@ export const mockUpdateCampaignApplication = { campaignGuarantee: 'Test guarantee', otherFinanceSources: 'Test otherFinanceSources', otherNotes: 'Test otherNotes', - category: CampaignTypeCategory.medical, -} - -const mockUpdateCampaignApplicationResponceOrganizer = { - amount: '1000', - beneficiary: 'Test beneficary', - campaignGuarantee: 'Test guarantee', - campaignName: 'Test Campaign', - category: 'medical', - description: 'Test description', - goal: 'Test goal', - history: 'Test history', - organizerBeneficiaryRel: 'Test organizerBeneficiaryRel', - organizerEmail: 'testemail@gmail.com', - organizerName: 'Test Organizer', - organizerPhone: '123456789', - otherFinanceSources: 'Test otherFinanceSources', - otherNotes: 'Test otherNotes', -} - -const mockUpdateCampaignApplicationResponceAdmin = { - amount: '1000', - beneficiary: 'Test beneficary', - campaignGuarantee: 'Test guarantee', - campaignName: 'Test Campaign', - category: 'medical', - description: 'Test description', - goal: 'Test goal', - history: 'Test history', - organizerBeneficiaryRel: 'Test organizerBeneficiaryRel', - organizerEmail: 'testemail@gmail.com', - organizerName: 'Test Organizer', - organizerPhone: '123456789', - otherFinanceSources: 'Test otherFinanceSources', - otherNotes: 'Test otherNotes', - archived: false, - state: 'active', - ticketURL: 'http://test.com/ticket', + campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af', + campaignEnd: 'funds', + campaignEndDate: '2024-09-09', } diff --git a/apps/api/src/campaign-application/campaign-application.controller.ts b/apps/api/src/campaign-application/campaign-application.controller.ts index b3beb2ac..44f73e43 100644 --- a/apps/api/src/campaign-application/campaign-application.controller.ts +++ b/apps/api/src/campaign-application/campaign-application.controller.ts @@ -1,27 +1,26 @@ import { - Controller, - Get, - Post, Body, - Patch, - Param, + Controller, + Delete, ForbiddenException, - NotFoundException, + Get, Logger, + NotFoundException, + Param, + Patch, + Post, UploadedFiles, UseInterceptors, - Delete, } from '@nestjs/common' -import { CampaignApplicationService } from './campaign-application.service' -import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' -import { UpdateCampaignApplicationDto } from './dto/update-campaign-application.dto' +import { FilesInterceptor } from '@nestjs/platform-express' import { ApiTags } from '@nestjs/swagger' -import { AuthenticatedUser, RoleMatchingMode, Roles } from 'nest-keycloak-connect' -import { RealmViewSupporters, ViewSupporters } from '@podkrepi-bg/podkrepi-types' +import { AuthenticatedUser } from 'nest-keycloak-connect' import { KeycloakTokenParsed, isAdmin } from '../auth/keycloak' -import { PersonService } from '../person/person.service' -import { FilesInterceptor } from '@nestjs/platform-express' import { validateFileType } from '../common/files' +import { PersonService } from '../person/person.service' +import { CampaignApplicationService } from './campaign-application.service' +import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' +import { UpdateCampaignApplicationDto } from './dto/update-campaign-application.dto' @ApiTags('campaign-application') @Controller('campaign-application') @@ -85,7 +84,7 @@ export class CampaignApplicationController { } const isAdminFlag = isAdmin(user) - + return this.campaignApplicationService.findOne(id, isAdminFlag, person) } @@ -98,7 +97,7 @@ export class CampaignApplicationController { } const isAdminFlag = isAdmin(user) - + return this.campaignApplicationService.deleteFile(id, isAdminFlag, person) } diff --git a/apps/api/src/campaign-application/campaign-application.service.spec.ts b/apps/api/src/campaign-application/campaign-application.service.spec.ts index eadf7e94..d9c35fd8 100644 --- a/apps/api/src/campaign-application/campaign-application.service.spec.ts +++ b/apps/api/src/campaign-application/campaign-application.service.spec.ts @@ -1,11 +1,9 @@ -import { Test, TestingModule } from '@nestjs/testing' -import { CampaignApplicationService } from './campaign-application.service' -import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' import { BadRequestException, ForbiddenException, NotFoundException } from '@nestjs/common' -import { CampaignApplicationFileRole, CampaignTypeCategory, Person } from '@prisma/client' -import { prismaMock, MockPrismaService } from '../prisma/prisma-client.mock' +import { Test, TestingModule } from '@nestjs/testing' import { OrganizerService } from '../organizer/organizer.service' import { personMock } from '../person/__mock__/personMock' +import { MockPrismaService, prismaMock } from '../prisma/prisma-client.mock' +import { S3Service } from '../s3/s3.service' import { mockCampaigns, mockCreatedCampaignApplication, @@ -13,12 +11,12 @@ import { mockSingleCampaignApplication, mockUpdateCampaignApplication, } from './__mocks__/campaign-application-mocks' -import { S3Service } from '../s3/s3.service' import { mockCampaignApplicationFileFn, mockCampaignApplicationFilesFn, - mockCampaignApplicationUploadFileFn, } from './__mocks__/campaing-application-file-mocks' +import { CampaignApplicationService } from './campaign-application.service' +import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' describe('CampaignApplicationService', () => { let service: CampaignApplicationService @@ -108,6 +106,7 @@ describe('CampaignApplicationService', () => { transparencyTermsAccepted: true, personalInformationProcessingAccepted: true, toEntity: new CreateCampaignApplicationDto().toEntity, + campaignEndDate: '2024-01-01' } const mockOrganizerId = 'mockOrganizerId' @@ -143,8 +142,10 @@ describe('CampaignApplicationService', () => { campaignGuarantee: 'Test guarantee', otherFinanceSources: 'Test otherFinanceSources', otherNotes: 'Test otherNotes', - category: CampaignTypeCategory.medical, + campaignTypeId: 'ffdbcc41-85ec-0000-9e59-0662f3b433af', organizerId: mockOrganizerId, + campaignEnd: 'funds', + campaignEndDate: new Date('2024-01-01T00:00:00.000Z'), }, }) @@ -283,6 +284,7 @@ describe('CampaignApplicationService', () => { where: { id: '1' }, data: { ...mockUpdateCampaignApplication, + campaignEndDate: new Date('2024-09-09T00:00:00.000Z') }, }) }) @@ -338,6 +340,7 @@ describe('CampaignApplicationService', () => { where: { id: '1' }, data: { ...mockUpdateCampaignApplication, + campaignEndDate: new Date('2024-09-09T00:00:00.000Z') }, }) }) diff --git a/apps/api/src/campaign-application/campaign-application.service.ts b/apps/api/src/campaign-application/campaign-application.service.ts index ae0fff51..d8b29d81 100644 --- a/apps/api/src/campaign-application/campaign-application.service.ts +++ b/apps/api/src/campaign-application/campaign-application.service.ts @@ -12,6 +12,15 @@ import { OrganizerService } from '../organizer/organizer.service' import { CampaignApplicationFileRole, Person, Prisma } from '@prisma/client' import { S3Service } from './../s3/s3.service' import { CreateCampaignApplicationFileDto } from './dto/create-campaignApplication-file.dto' + +function dateMaybe (d?: string) { + return d != null && + typeof d === 'string' && + new Date(d).toString() != new Date('----invalid date ---').toString() + ? new Date(d) + : undefined +} + @Injectable() export class CampaignApplicationService { private readonly bucketName: string = 'campaignapplication-files' @@ -21,10 +30,6 @@ export class CampaignApplicationService { private s3: S3Service, ) {} - async getCampaignByIdWithPersonIds(id: string): Promise { - throw new Error('Method not implemented.') - } - async create(createCampaignApplicationDto: CreateCampaignApplicationDto, person: Person) { try { if ( @@ -59,8 +64,10 @@ export class CampaignApplicationService { campaignGuarantee: createCampaignApplicationDto.campaignGuarantee, otherFinanceSources: createCampaignApplicationDto.otherFinanceSources, otherNotes: createCampaignApplicationDto.otherNotes, - category: createCampaignApplicationDto.category, + campaignTypeId: createCampaignApplicationDto.campaignTypeId, organizerId: organizer.id, + campaignEnd: createCampaignApplicationDto.campaignEnd, + campaignEndDate: dateMaybe(createCampaignApplicationDto.campaignEndDate) } const newCampaignApplication = await this.prisma.campaignApplication.create({ @@ -96,10 +103,22 @@ export class CampaignApplicationService { } } - async findOne(id: string, isAdminFlag: boolean, person: Prisma.PersonGetPayload<{ include: { organizer: {select:{id:true}}}}>) { + async findOne( + id: string, + isAdminFlag: boolean, + person: Prisma.PersonGetPayload<{ include: { organizer: { select: { id: true } } } }>, + ) { try { const singleCampaignApplication = await this.prisma.campaignApplication.findUnique({ where: { id }, + include: { + documents: { + select: { + id: true, + filename: true, + }, + }, + }, }) if (!singleCampaignApplication) { throw new NotFoundException('Campaign application doesnt exist') @@ -116,7 +135,11 @@ export class CampaignApplicationService { } } - async deleteFile(id: string, isAdminFlag: boolean, person: Prisma.PersonGetPayload<{ include: { organizer: {select:{id:true}}}}>) { + async deleteFile( + id: string, + isAdminFlag: boolean, + person: Prisma.PersonGetPayload<{ include: { organizer: { select: { id: true } } } }>, + ) { try { const campaignApplication = await this.prisma.campaignApplication.findFirst({ where: { @@ -184,7 +207,9 @@ export class CampaignApplicationService { campaignGuarantee: updateCampaignApplicationDto?.campaignGuarantee, otherFinanceSources: updateCampaignApplicationDto?.otherFinanceSources, otherNotes: updateCampaignApplicationDto?.otherNotes, - category: updateCampaignApplicationDto?.category, + campaignTypeId: updateCampaignApplicationDto?.campaignTypeId, + campaignEnd: updateCampaignApplicationDto.campaignEnd, + campaignEndDate: dateMaybe(updateCampaignApplicationDto.campaignEndDate), }, }) @@ -208,10 +233,12 @@ export class CampaignApplicationService { campaignGuarantee: updateCampaignApplicationDto?.campaignGuarantee, otherFinanceSources: updateCampaignApplicationDto?.otherFinanceSources, otherNotes: updateCampaignApplicationDto?.otherNotes, - category: updateCampaignApplicationDto?.category, + campaignTypeId: updateCampaignApplicationDto?.campaignTypeId, state: updateCampaignApplicationDto?.state, ticketURL: updateCampaignApplicationDto?.ticketURL, archived: updateCampaignApplicationDto?.archived, + campaignEnd: updateCampaignApplicationDto.campaignEnd, + campaignEndDate: dateMaybe(updateCampaignApplicationDto.campaignEndDate), }, }) } diff --git a/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts b/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts index 20a84bce..4274ed88 100644 --- a/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts +++ b/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger' -import { CampaignTypeCategory, Prisma } from '@prisma/client' +import { Prisma } from '@prisma/client' import { Expose } from 'class-transformer' -import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator' +import { IsBoolean, IsDateString, IsNotEmpty, IsOptional, IsString } from 'class-validator' @Expose() export class CreateCampaignApplicationDto { @@ -116,10 +116,23 @@ export class CreateCampaignApplicationDto { @IsOptional() otherNotes?: string - @ApiProperty({ enum: CampaignTypeCategory }) + @ApiProperty() + @Expose() + @IsString() + @IsOptional() + campaignTypeId?: string + + @ApiProperty() + @Expose() + @IsString() + @IsOptional() + campaignEnd?: string + + @ApiProperty() @Expose() + @IsDateString() @IsOptional() - category?: CampaignTypeCategory + campaignEndDate?: string public toEntity(): Prisma.CampaignApplicationCreateInput { return { diff --git a/apps/api/src/domain/generated/campaignApplication/dto/create-campaignApplication.dto.ts b/apps/api/src/domain/generated/campaignApplication/dto/create-campaignApplication.dto.ts index 64443e2f..af2cf96f 100644 --- a/apps/api/src/domain/generated/campaignApplication/dto/create-campaignApplication.dto.ts +++ b/apps/api/src/domain/generated/campaignApplication/dto/create-campaignApplication.dto.ts @@ -1,6 +1,3 @@ -import { CampaignTypeCategory } from '@prisma/client' -import { ApiProperty } from '@nestjs/swagger' - export class CreateCampaignApplicationDto { organizerName: string organizerEmail?: string @@ -15,8 +12,9 @@ export class CreateCampaignApplicationDto { campaignGuarantee?: string otherFinanceSources?: string otherNotes?: string - @ApiProperty({ enum: CampaignTypeCategory }) - category?: CampaignTypeCategory + campaignTypeId?: string ticketURL?: string archived?: boolean + campaignEnd?: string + campaignEndDate?: Date } diff --git a/apps/api/src/domain/generated/campaignApplication/dto/update-campaignApplication.dto.ts b/apps/api/src/domain/generated/campaignApplication/dto/update-campaignApplication.dto.ts index f80c13dc..d80cacae 100644 --- a/apps/api/src/domain/generated/campaignApplication/dto/update-campaignApplication.dto.ts +++ b/apps/api/src/domain/generated/campaignApplication/dto/update-campaignApplication.dto.ts @@ -1,6 +1,3 @@ -import { CampaignTypeCategory } from '@prisma/client' -import { ApiProperty } from '@nestjs/swagger' - export class UpdateCampaignApplicationDto { organizerName?: string organizerEmail?: string @@ -15,8 +12,9 @@ export class UpdateCampaignApplicationDto { campaignGuarantee?: string otherFinanceSources?: string otherNotes?: string - @ApiProperty({ enum: CampaignTypeCategory }) - category?: CampaignTypeCategory + campaignTypeId?: string ticketURL?: string archived?: boolean + campaignEnd?: string + campaignEndDate?: Date } diff --git a/apps/api/src/domain/generated/campaignApplication/entities/campaignApplication.entity.ts b/apps/api/src/domain/generated/campaignApplication/entities/campaignApplication.entity.ts index db325d73..b1ee5f98 100644 --- a/apps/api/src/domain/generated/campaignApplication/entities/campaignApplication.entity.ts +++ b/apps/api/src/domain/generated/campaignApplication/entities/campaignApplication.entity.ts @@ -1,4 +1,4 @@ -import { CampaignApplicationState, CampaignTypeCategory } from '@prisma/client' +import { CampaignApplicationState } from '@prisma/client' import { Organizer } from '../../organizer/entities/organizer.entity' import { CampaignApplicationFile } from '../../campaignApplicationFile/entities/campaignApplicationFile.entity' @@ -23,7 +23,9 @@ export class CampaignApplication { otherFinanceSources: string | null otherNotes: string | null state: CampaignApplicationState - category: CampaignTypeCategory | null + campaignTypeId: string | null ticketURL: string | null archived: boolean | null + campaignEnd: string | null + campaignEndDate: Date | null } diff --git a/migrations/20240907144954_replace_category_with_campaign_type_id/migration.sql b/migrations/20240907144954_replace_category_with_campaign_type_id/migration.sql new file mode 100644 index 00000000..bd208282 --- /dev/null +++ b/migrations/20240907144954_replace_category_with_campaign_type_id/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `category` on the `campaign_applications` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "campaign_applications" DROP COLUMN "category", +ADD COLUMN "campaignTypeId" UUID; diff --git a/migrations/20240909080426_campaign_application_add_campaign_end/migration.sql b/migrations/20240909080426_campaign_application_add_campaign_end/migration.sql new file mode 100644 index 00000000..a513960c --- /dev/null +++ b/migrations/20240909080426_campaign_application_add_campaign_end/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "campaign_applications" ADD COLUMN "campaignEnd" TEXT DEFAULT 'funds', +ADD COLUMN "campaignEndDate" TIMESTAMPTZ(6); diff --git a/podkrepi.dbml b/podkrepi.dbml index f1b8bcdb..316d0752 100644 --- a/podkrepi.dbml +++ b/podkrepi.dbml @@ -597,9 +597,11 @@ Table campaign_applications { otherFinanceSources String otherNotes String state CampaignApplicationState [not null, default: 'review'] - category CampaignTypeCategory [default: 'others'] + campaignTypeId String ticketURL String archived Boolean [default: false] + campaignEnd String [default: 'funds'] + campaignEndDate DateTime Note: 'CampaignApplication represents a request for a new campaign - it is not a Campaign yet and has to proove it needs to be' } @@ -988,4 +990,6 @@ Ref: expense_files.uploaderId > people.id Ref: documents.ownerId > people.id -Ref: campaign_applications.organizerId > organizers.id \ No newline at end of file +Ref: campaign_applications.organizerId > organizers.id + +Ref: campaign_application_files.campaignApplicationId > campaign_applications.id \ No newline at end of file diff --git a/schema.prisma b/schema.prisma index d5bb2c69..70d0c593 100644 --- a/schema.prisma +++ b/schema.prisma @@ -1008,9 +1008,9 @@ enum EmailType { /// CampaignApplication represents a request for a new campaign - it is not a Campaign yet and has to proove it needs to be model CampaignApplication { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) - updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) // organizer editable fields // need to be logged in to create a campaign application @@ -1033,10 +1033,12 @@ model CampaignApplication { otherNotes String? @db.Text // operator editable fields - state CampaignApplicationState @default(review) - category CampaignTypeCategory? @default(others) - ticketURL String? @db.VarChar(500) - archived Boolean? @default(false) + state CampaignApplicationState @default(review) + campaignTypeId String? @db.Uuid + ticketURL String? @db.VarChar(500) + archived Boolean? @default(false) + campaignEnd String? @default("funds") + campaignEndDate DateTime? @db.Timestamptz(6) @@map("campaign_applications") }