diff --git a/apps/backend/src/anthology/anthology.entity.ts b/apps/backend/src/anthology/anthology.entity.ts new file mode 100644 index 00000000..be22827f --- /dev/null +++ b/apps/backend/src/anthology/anthology.entity.ts @@ -0,0 +1,49 @@ +import { Entity, Column, IntegerType } from 'typeorm'; + +import { AnthologyStatus, AnthologyPubLevel } from './types'; + +@Entity() +export class Anthology { + @Column({ primary: true }) + id: number; + + @Column() + title: string; + + @Column() + description: string; + + @Column() + published_year: IntegerType; + + @Column({ nullable: true }) + programs?: string[] | string; + + @Column({ nullable: true }) + inventory?: IntegerType; + + @Column() + status: AnthologyStatus; + + @Column() + pub_level: AnthologyPubLevel; + + @Column({ nullable: true }) + photo_url: string; + + @Column({ nullable: true }) + genre: string; + + @Column({ nullable: true }) + theme: string; + + @Column({ nullable: true }) + isbn: string; + + @Column({ nullable: true }) + shopify_url: string; + + // TODO once Library is implemented + // @Column() + // library_id: +} diff --git a/apps/backend/src/anthology/anthology.service.ts b/apps/backend/src/anthology/anthology.service.ts new file mode 100644 index 00000000..35bd39c2 --- /dev/null +++ b/apps/backend/src/anthology/anthology.service.ts @@ -0,0 +1,123 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Anthology } from './anthology.entity'; +import { AnthologyStatus, AnthologyPubLevel } from './types'; + +@Injectable() +export class AnthologyService { + constructor( + @InjectRepository(Anthology) private repo: Repository, + ) {} + + async create( + title: string, + description: string, + published_year: number, + status: AnthologyStatus, + pub_level: AnthologyPubLevel, + programs?: string[] | string, + inventory?: number, + photo_url?: string, + genre?: string, + theme?: string, + isbn?: string, + shopify_url?: string, + ) { + const anthologyId = (await this.repo.count()) + 1; + const anthology = this.repo.create({ + id: anthologyId, + title, + description, + published_year, + status, + pub_level, + programs, + inventory, + photo_url, + genre, + theme, + isbn, + shopify_url, + }); + + return this.repo.save(anthology); + } + + findOne(id: number) { + if (!id) { + return null; + } + + return this.repo.findOneBy({ id }); + } + + findAll() { + return this.repo.find(); + } + + findByStatus(status: AnthologyStatus) { + return this.repo.find({ where: { status } }); + } + + findByPubLevel(pub_level: AnthologyPubLevel) { + return this.repo.find({ where: { pub_level } }); + } + + findByYear(published_year: number) { + return this.repo.find({ where: { published_year } }); + } + + findByGenre(genre: string) { + return this.repo.find({ where: { genre } }); + } + + findByTheme(theme: string) { + return this.repo.find({ where: { theme } }); + } + + async update(id: number, attrs: Partial) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + Object.assign(anthology, attrs); + + return this.repo.save(anthology); + } + + async remove(id: number) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + return this.repo.remove(anthology); + } + + async updateInventory(id: number, inventory: number) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + anthology.inventory = inventory; + return this.repo.save(anthology); + } + + async updateStatus(id: number, status: AnthologyStatus) { + const anthology = await this.findOne(id); + + if (!anthology) { + throw new NotFoundException('Anthology not found'); + } + + anthology.status = status; + return this.repo.save(anthology); + } +} diff --git a/apps/backend/src/anthology/types.ts b/apps/backend/src/anthology/types.ts new file mode 100644 index 00000000..214797ed --- /dev/null +++ b/apps/backend/src/anthology/types.ts @@ -0,0 +1,13 @@ +export enum AnthologyStatus { + ARCHIVED = 'Archived', + NOT_STARTED = 'NotStarted', + DRAFTING = 'Drafting', + CAN_BE_SHARED = 'CanBeShared', +} + +export enum AnthologyPubLevel { + ZINE = 'Zine', + CHAPBOOK = 'Chapbook', + PERFECT_BOUND = 'PerfectBound', + SIGNATURE = 'Signature', +} diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 54fb044e..9af0cf4b 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -3,11 +3,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { TaskModule } from './task/task.module'; import AppDataSource from './data-source'; @Module({ - imports: [TypeOrmModule.forRoot(AppDataSource.options), TaskModule], + imports: [TypeOrmModule.forRoot(AppDataSource.options)], controllers: [AppController], providers: [AppService], }) diff --git a/apps/backend/src/data-source.ts b/apps/backend/src/data-source.ts index 4cd06624..e9c75481 100644 --- a/apps/backend/src/data-source.ts +++ b/apps/backend/src/data-source.ts @@ -1,6 +1,5 @@ import { DataSource } from 'typeorm'; import { PluralNamingStrategy } from './strategies/plural-naming.strategy'; -import { Task } from './task/types/task.entity'; import * as dotenv from 'dotenv'; dotenv.config(); @@ -12,7 +11,7 @@ const AppDataSource = new DataSource({ username: process.env.NX_DB_USERNAME, password: process.env.NX_DB_PASSWORD, database: process.env.NX_DB_DATABASE, - entities: [Task], + entities: [], migrations: ['apps/backend/src/migrations/*.js'], // Setting synchronize: true shouldn't be used in production - otherwise you can lose production data synchronize: false, diff --git a/apps/backend/src/library/library.controller.ts b/apps/backend/src/library/library.controller.ts new file mode 100644 index 00000000..bb217ae7 --- /dev/null +++ b/apps/backend/src/library/library.controller.ts @@ -0,0 +1,32 @@ +import { + Controller, + Delete, + Get, + Param, + ParseIntPipe, + UseGuards, +} from '@nestjs/common'; +import { LibraryService } from './library.service'; +import { AuthGuard } from '@nestjs/passport'; +import { Library } from './library.entity'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; + +@ApiTags('Library') +@ApiBearerAuth() +@Controller('library') +@UseGuards(AuthGuard('jwt')) +export class LibraryController { + constructor(private libraryService: LibraryService) {} + + @Get('/:libraryId') + async getLibrary( + @Param('libraryId', ParseIntPipe) libraryId: number, + ): Promise { + return this.libraryService.findOne(libraryId); + } + + @Delete('/:id') + removeLibrary(@Param('id') id: string) { + return this.libraryService.remove(parseInt(id)); + } +} diff --git a/apps/backend/src/library/library.entity.ts b/apps/backend/src/library/library.entity.ts new file mode 100644 index 00000000..516d71a2 --- /dev/null +++ b/apps/backend/src/library/library.entity.ts @@ -0,0 +1,11 @@ +import { Entity, Column } from 'typeorm'; +import { Anthology } from '../anthology/anthology.entity'; + +@Entity() +export class Library { + @Column({ primary: true }) + id: number; + + @Column() + anthologies: Anthology[]; +} diff --git a/apps/backend/src/library/library.module.ts b/apps/backend/src/library/library.module.ts new file mode 100644 index 00000000..4afe3a37 --- /dev/null +++ b/apps/backend/src/library/library.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { LibraryController } from './library.controller'; +import { LibraryService } from './library.service'; +import { Library } from './library.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([Library])], + controllers: [LibraryController], + providers: [LibraryService], +}) +export class LibraryModule {} diff --git a/apps/backend/src/library/library.service.ts b/apps/backend/src/library/library.service.ts new file mode 100644 index 00000000..d85cef9a --- /dev/null +++ b/apps/backend/src/library/library.service.ts @@ -0,0 +1,77 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Library } from './library.entity'; +import { Anthology } from '../anthology/anthology.entity'; + +@Injectable() +export class LibraryService { + constructor(@InjectRepository(Library) private repo: Repository) {} + + async create(anthologies: Anthology[]) { + const libraryId = (await this.repo.count()) + 1; + const library = this.repo.create({ + id: libraryId, + anthologies: anthologies, + }); + + return this.repo.save(library); + } + + findOne(id: number) { + if (!id) { + return null; + } + + return this.repo.findOneBy({ id }); + } + + findAll() { + return this.repo.find(); + } + + async update(id: number, attrs: Partial) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + Object.assign(library, attrs); + + return this.repo.save(library); + } + + async remove(id: number) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + return this.repo.remove(library); + } + + async addAnthology(id: number, anthology: Anthology | Anthology[]) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + library.anthologies.concat(anthology); + return this.repo.save(library); + } + + async removeAnthology(id: number, anthology: Anthology) { + const library = await this.findOne(id); + + if (!library) { + throw new NotFoundException('Library not found'); + } + + library.anthologies.filter((a) => a !== anthology); + return this.repo.save(library); + } +} diff --git a/apps/backend/src/migrations/1754254886189-add_task.ts b/apps/backend/src/migrations/1754254886189-add_task.ts deleted file mode 100644 index 450a6415..00000000 --- a/apps/backend/src/migrations/1754254886189-add_task.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTask1754254886189 implements MigrationInterface { - name = 'AddTask1754254886189'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."tasks_category_enum" AS ENUM('Draft', 'To Do', 'In Progress', 'Completed')`, - ); - await queryRunner.query( - `CREATE TABLE "task" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "description" character varying, "dateCreated" TIMESTAMP NOT NULL DEFAULT now(), "dueDate" TIMESTAMP, "labels" jsonb NOT NULL DEFAULT '[]', "category" "public"."tasks_category_enum" NOT NULL DEFAULT 'Draft', CONSTRAINT "PK_8d12ff38fcc62aaba2cab748772" PRIMARY KEY ("id"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "task"`); - await queryRunner.query(`DROP TYPE "public"."tasks_category_enum"`); - } -} diff --git a/example.env b/example.env index 211b1472..19d989f2 100644 --- a/example.env +++ b/example.env @@ -1,5 +1,5 @@ NX_DB_HOST=localhost, NX_DB_USERNAME=postgres, NX_DB_PASSWORD=, -NX_DB_DATABASE=jumpstart, +NX_DB_DATABASE=826boston, NX_DB_PORT=5432, \ No newline at end of file