diff --git a/.env.example b/.env.example index 72f7f62791250d9e59c7aa77dcac87908056c761..636ce5ecb1f6cbdeac73878e12263937afd06f5b 100644 --- a/.env.example +++ b/.env.example @@ -6,15 +6,4 @@ POSTGRES_DATABASE= AUTH_SERVICE_URL=http://localhost:3001 FE_URL=http://localhost:5173 -KONF_PERIODE_KEY=PERIODE -KONF_MIN_BIMBINGAN_KEY=MINIMAL_BIMBINGAN -KONF_AWAL_PENDAFTARAN_KEY=AWAL_PENDAFTARAN -KONF_AKHIR_PENDAFTARAN_KEY=AKHIR_PENDAFTARAN -KONF_AWAL_SEMPRO_KEY=AWAL_SEMPRO -KONF_AKHIR_SEMPRO_KEY=AKHIR_SEMPRO -KONF_AWAL_SEM_TESIS_KEY=AWAL_SEM_TESIS -KONF_AKHIR_SEM_TESIS_KEY=AKHIR_SEM_TESIS -KONF_AWAL_SIDANG_KEY=AWAL_SIDANG -KONF_AKHIR_SIDANG_KEY=AKHIR_SIDANG - COOKIE_NAME=gradu-it.access-token diff --git a/src/app.module.ts b/src/app.module.ts index 9e9ee257abb0b46a1a689a034421ad2b3648ee36..9367c4296bbffba3212bbbb485a87b6314e0ac88 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,14 +5,8 @@ import { TypeOrmModule } from "@nestjs/typeorm"; import { Bimbingan } from "./entities/bimbingan.entity"; import { Pengguna } from "./entities/pengguna.entity"; import { Topik } from "./entities/topik.entity"; -import { AuditLog } from "./entities/auditLog.entity"; import { DosenBimbingan } from "./entities/dosenBimbingan.entity"; -import { Kelas } from "./entities/kelas.entity"; -import { MahasiswaKelas } from "./entities/mahasiswaKelas.entity"; -import { PengajarKelas } from "./entities/pengajarKelas.entity"; import { PendaftaranTesis } from "./entities/pendaftaranTesis.entity"; -// import { Ruangan } from "./entities/ruangan.entity"; -import { Tugas } from "./entities/tugas.entity"; import { PengujiSidsem } from "./entities/pengujiSidsem.entity"; import { RegistrasiTesisModule } from "./registrasi-tesis/registrasi-tesis.module"; import { ConfigModule } from "@nestjs/config"; @@ -20,18 +14,8 @@ import { AuthModule } from "./auth/auth.module"; import { AlokasiTopikModule } from "./alokasi-topik/alokasi-topik.module"; import { DashboardModule } from "./dashboard/dashboard.module"; import { BimbinganModule } from "./bimbingan/bimbingan.module"; -import { Konfigurasi } from "./entities/konfigurasi.entity"; -import { KonfigurasiModule } from "./konfigurasi/konfigurasi.module"; import { validate } from "./env.validation"; import { BerkasBimbingan } from "./entities/berkasBimbingan.entity"; -import { MataKuliah } from "./entities/mataKuliah.entity"; -import { SubmisiTugas } from "./entities/submisiTugas.entity"; -import { BerkasSubmisiTugas } from "./entities/berkasSubmisiTugas.entity"; -import { BerkasTugas } from "./entities/berkasTugas.entity"; -import { TugasModule } from "./tugas/tugas.module"; -import { KelasModule } from "./kelas/kelas.module"; -import { SubmisiTugasModule } from "./submisi-tugas/submisi-tugas.module"; -import { NilaiModule } from "./nilai/nilai.module"; import { PendaftaranSidsem } from "./entities/pendaftaranSidsem"; import { DosenBimbinganModule } from "./dosen-bimbingan/dosen-bimbingan.module"; @@ -52,20 +36,9 @@ import { DosenBimbinganModule } from "./dosen-bimbingan/dosen-bimbingan.module"; Pengguna, PendaftaranSidsem, Topik, - AuditLog, DosenBimbingan, - Kelas, - MahasiswaKelas, - PengajarKelas, PendaftaranTesis, - // Ruangan, - Tugas, PengujiSidsem, - Konfigurasi, - MataKuliah, - SubmisiTugas, - BerkasSubmisiTugas, - BerkasTugas, ], synchronize: true, }), @@ -74,12 +47,6 @@ import { DosenBimbinganModule } from "./dosen-bimbingan/dosen-bimbingan.module"; AlokasiTopikModule, DashboardModule, BimbinganModule, - KonfigurasiModule, - DosenBimbinganModule, - KelasModule, - TugasModule, - SubmisiTugasModule, - NilaiModule, DosenBimbinganModule, ], controllers: [AppController], diff --git a/src/entities/auditLog.entity.ts b/src/entities/auditLog.entity.ts deleted file mode 100644 index c95d05a03fb11fb13d835bc1e6c64b22e107bff6..0000000000000000000000000000000000000000 --- a/src/entities/auditLog.entity.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; -import { Pengguna } from "./pengguna.entity"; - -@Entity() -export class AuditLog { - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) - pengguna: Pengguna; -} diff --git a/src/entities/berkasSubmisiTugas.entity.ts b/src/entities/berkasSubmisiTugas.entity.ts deleted file mode 100644 index e48d5e8d88f1449b767d3e88f1f9862e8e0f6560..0000000000000000000000000000000000000000 --- a/src/entities/berkasSubmisiTugas.entity.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; -import { SubmisiTugas } from "./submisiTugas.entity"; -import { ApiProperty } from "@nestjs/swagger"; -import { IsString, IsUUID } from "@nestjs/class-validator"; -import { IsUrl } from "class-validator"; - -@Entity() -export class BerkasSubmisiTugas { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => SubmisiTugas, (submisi) => submisi.id, { - orphanedRowAction: "delete", - }) - submisiTugas: SubmisiTugas; - - @ApiProperty() - @IsString() - @Column({ type: "text" }) - nama: string; - - @ApiProperty({ example: "https://example.com/berkas.pdf" }) - @IsUrl() - @Column({ type: "text" }) - url: string; -} diff --git a/src/entities/berkasTugas.entity.ts b/src/entities/berkasTugas.entity.ts deleted file mode 100644 index c18bccfa9c839754ca8ed7bb0f5789f8841e3ff1..0000000000000000000000000000000000000000 --- a/src/entities/berkasTugas.entity.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; -import { Tugas } from "./tugas.entity"; -import { ApiProperty } from "@nestjs/swagger"; -import { IsUrl, IsUUID } from "class-validator"; -import { IsString } from "@nestjs/class-validator"; - -@Entity() -export class BerkasTugas { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => Tugas, (tugas) => tugas.id, { orphanedRowAction: "delete" }) - tugas: Tugas; - - @ApiProperty() - @IsString() - @Column({ type: "text" }) - nama: string; - - @ApiProperty({ example: "https://example.com/berkas.pdf" }) - @IsUrl() - @Column({ type: "text" }) - url: string; -} diff --git a/src/entities/kelas.entity.ts b/src/entities/kelas.entity.ts deleted file mode 100644 index 7070b47519d5ed99f52281c454c32e04f01ef2f3..0000000000000000000000000000000000000000 --- a/src/entities/kelas.entity.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, -} from "typeorm"; -import { MataKuliah } from "./mataKuliah.entity"; -import { ApiProperty } from "@nestjs/swagger"; -import { PengajarKelas } from "./pengajarKelas.entity"; -import { MahasiswaKelas } from "./mahasiswaKelas.entity"; -import { - IsPositive, - IsString, - IsUUID, - Length, - MaxLength, -} from "@nestjs/class-validator"; -import { Tugas } from "./tugas.entity"; - -@Entity() -export class Kelas { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @PrimaryGeneratedColumn("uuid") - id: string; - - @ApiProperty({ example: 1 }) - @Column({ type: "smallint" }) - @ApiProperty({ example: 1 }) - @IsPositive() - nomor: number; - - @ApiProperty() - @IsString() - @Column({ type: "text" }) - periode: string; - - @ApiProperty({ type: MataKuliah }) - @ManyToOne(() => MataKuliah, (mataKuliah) => mataKuliah.kode) - @JoinColumn({ name: "mataKuliahKode" }) - mataKuliah: MataKuliah; - - @ApiProperty({ minLength: 6, maxLength: 6, example: "IF4031" }) - @IsString() - @Length(6) - @Column({ nullable: true }) - mataKuliahKode: string; - - @ApiProperty({ example: "bg-blue-600/20" }) - @IsString() - @MaxLength(24) - @Column({ type: "varchar", length: 24 }) - warna: string; - - @OneToMany(() => PengajarKelas, (pengajar) => pengajar.kelas) - pengajar: PengajarKelas[]; - - @OneToMany(() => MahasiswaKelas, (mahasiswa) => mahasiswa.kelas) - mahasiswa: MahasiswaKelas[]; - - @OneToMany(() => Tugas, (tugas) => tugas.kelas) - tugas: Tugas[]; -} diff --git a/src/entities/konfigurasi.entity.ts b/src/entities/konfigurasi.entity.ts deleted file mode 100644 index 538a9fa944cad11819c8323dafa99decc9801908..0000000000000000000000000000000000000000 --- a/src/entities/konfigurasi.entity.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Column, Entity, PrimaryColumn } from "typeorm"; - -@Entity() -export class Konfigurasi { - @PrimaryColumn({ type: "text" }) - key: string; - - @Column({ type: "text" }) - value: string; -} diff --git a/src/entities/mahasiswaKelas.entity.ts b/src/entities/mahasiswaKelas.entity.ts deleted file mode 100644 index 0753aa6f75f865913b7e42209248310948dc67c2..0000000000000000000000000000000000000000 --- a/src/entities/mahasiswaKelas.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - PrimaryGeneratedColumn, -} from "typeorm"; -import { Kelas } from "./kelas.entity"; -import { Pengguna } from "./pengguna.entity"; - -@Entity() -export class MahasiswaKelas { - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => Kelas, (kelas) => kelas.id) - @JoinColumn({ name: "kelasId" }) - kelas: Kelas; - - @Column() - kelasId: string; - - @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) - @JoinColumn({ name: "mahasiswaId" }) - mahasiswa: Pengguna; - - @Column() - mahasiswaId: string; - - @Column({ type: "real", nullable: true }) - nilaiAkhir?: number; -} diff --git a/src/entities/mataKuliah.entity.ts b/src/entities/mataKuliah.entity.ts deleted file mode 100644 index 9e1fed5d807a176b89288144623ec72575ef5e33..0000000000000000000000000000000000000000 --- a/src/entities/mataKuliah.entity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { IsString, Length, MaxLength } from "class-validator"; -import { Column, Entity, OneToMany, PrimaryColumn } from "typeorm"; -import { Kelas } from "./kelas.entity"; - -@Entity() -export class MataKuliah { - @ApiProperty({ minLength: 6, maxLength: 6, example: "IF4031" }) - @IsString() - @Length(6) - @PrimaryColumn({ type: "varchar", length: 6 }) - kode: string; - - @ApiProperty({ maxLength: 256 }) - @IsString() - @MaxLength(256) - @Column({ type: "varchar", length: 256 }) - nama: string; - - @OneToMany(() => Kelas, (kelas) => kelas.mataKuliah) - kelas: Kelas[]; -} diff --git a/src/entities/pengajarKelas.entity.ts b/src/entities/pengajarKelas.entity.ts deleted file mode 100644 index 61a314c4f21548b3b48e9aeb510b2d195d9a3662..0000000000000000000000000000000000000000 --- a/src/entities/pengajarKelas.entity.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - PrimaryGeneratedColumn, -} from "typeorm"; -import { Kelas } from "./kelas.entity"; -import { Pengguna } from "./pengguna.entity"; - -@Entity() -export class PengajarKelas { - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => Kelas, (kelas) => kelas.id) - @JoinColumn({ name: "kelasId" }) - kelas: Kelas; - - @Column() - kelasId: string; - - @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) - @JoinColumn({ name: "pengajarId" }) - pengajar: Pengguna; - - @Column() - pengajarId: string; -} diff --git a/src/entities/pengguna.entity.ts b/src/entities/pengguna.entity.ts index 7e4899c2764d11b3871fa2d7e502aed033a04406..da7a922c6ae3fd0a80abbcfa6e0b4213f9f702f7 100644 --- a/src/entities/pengguna.entity.ts +++ b/src/entities/pengguna.entity.ts @@ -5,10 +5,7 @@ import { ApiPropertyOptional, } from "@nestjs/swagger"; import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { MahasiswaKelas } from "./mahasiswaKelas.entity"; -import { PengajarKelas } from "./pengajarKelas.entity"; import { PendaftaranTesis } from "./pendaftaranTesis.entity"; -import { SubmisiTugas } from "./submisiTugas.entity"; export enum RoleEnum { ADMIN = "ADMIN", @@ -56,11 +53,6 @@ export class Pengguna { }) roles: RoleEnum[]; - @OneToMany(() => MahasiswaKelas, (mahasiswaKelas) => mahasiswaKelas.mahasiswa) - mahasiswaKelas: MahasiswaKelas[]; - - @OneToMany(() => PengajarKelas, (pengajarKelas) => pengajarKelas.pengajar) - pengajarKelas: PengajarKelas[]; @ApiPropertyOptional() @IsString() @Column({ type: "text", nullable: true }) @@ -68,7 +60,4 @@ export class Pengguna { @OneToMany(() => PendaftaranTesis, (pendaftaran) => pendaftaran.mahasiswa) pendaftaranTesis: PendaftaranTesis[]; - - @OneToMany(() => SubmisiTugas, (submisiTugas) => submisiTugas.mahasiswa) - submisiTugas: SubmisiTugas[]; } diff --git a/src/entities/ruangan.entity.ts b/src/entities/ruangan.entity.ts deleted file mode 100644 index 86dfaf804683751d50f397df3a55f00488dc0bfa..0000000000000000000000000000000000000000 --- a/src/entities/ruangan.entity.ts +++ /dev/null @@ -1,13 +0,0 @@ -// import { ApiProperty } from "@nestjs/swagger"; -// import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; - -// @Entity() -// export class Ruangan { -// @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) -// @PrimaryGeneratedColumn("uuid") -// id: string; - -// @ApiProperty() -// @Column({ type: "text" }) -// nama: string; -// } diff --git a/src/entities/submisiTugas.entity.ts b/src/entities/submisiTugas.entity.ts deleted file mode 100644 index be380d37d6c0b4dadf641f794735a343990e13b5..0000000000000000000000000000000000000000 --- a/src/entities/submisiTugas.entity.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, -} from "typeorm"; -import { Pengguna } from "./pengguna.entity"; -import { Tugas } from "./tugas.entity"; -import { BerkasSubmisiTugas } from "./berkasSubmisiTugas.entity"; -import { ApiProperty } from "@nestjs/swagger"; -import { IsString, IsUUID } from "class-validator"; -import { IsBoolean } from "@nestjs/class-validator"; - -@Entity() -export class SubmisiTugas { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) - @JoinColumn({ name: "mahasiswaId" }) - mahasiswa: Pengguna; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @Column() - mahasiswaId: string; - - @ApiProperty() - @IsString() - @Column({ type: "text" }) - jawaban: string; - - @ApiProperty({ description: "true means submitted, false means draft" }) - @IsBoolean() - @Column({ type: "boolean" }) - isSubmitted: boolean; // false means draft (saved), true means submitted - - @ApiProperty({ type: [BerkasSubmisiTugas] }) - @OneToMany( - () => BerkasSubmisiTugas, - (berkasSubmisiTugas) => berkasSubmisiTugas.submisiTugas, - { cascade: true }, - ) - berkasSubmisiTugas: BerkasSubmisiTugas[]; - - @ApiProperty() - @Column({ - type: "timestamptz", - default: () => "CURRENT_TIMESTAMP", - nullable: true, - }) - submittedAt: Date; - - @ManyToOne(() => Tugas, (tugas) => tugas.id) - @JoinColumn({ name: "tugasId" }) - tugas: Tugas; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @Column() - tugasId: string; -} diff --git a/src/entities/tugas.entity.ts b/src/entities/tugas.entity.ts deleted file mode 100644 index 2424b87973fd01fe854e40ca50079dbc40e0f4ee..0000000000000000000000000000000000000000 --- a/src/entities/tugas.entity.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, -} from "typeorm"; -import { Kelas } from "./kelas.entity"; -import { Pengguna } from "./pengguna.entity"; -import { BerkasTugas } from "./berkasTugas.entity"; -import { ApiProperty } from "@nestjs/swagger"; -import { IsDateString, IsUUID, MaxLength } from "class-validator"; -import { IsString } from "@nestjs/class-validator"; -import { SubmisiTugas } from "./submisiTugas.entity"; - -@Entity() -export class Tugas { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @PrimaryGeneratedColumn("uuid") - id: string; - - @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) - @JoinColumn({ name: "pembuatId" }) - pembuat: Pengguna; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @Column() - pembuatId: string; - - @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) - @JoinColumn({ name: "pengubahId" }) - pengubah: Pengguna; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @Column() - pengubahId: string; - - @ApiProperty() - @IsString() - @MaxLength(256) - @Column({ type: "varchar", length: 256 }) - judul: string; - - @ApiProperty() - @IsDateString() - @Column({ type: "timestamptz", default: () => "CURRENT_TIMESTAMP" }) - waktuMulai: Date; - - @ApiProperty() - @IsDateString() - @Column({ type: "timestamptz" }) - waktuSelesai: Date; - - @ApiProperty() - @IsString() - @Column({ type: "text" }) - deskripsi: string; - - @ApiProperty({ type: [BerkasTugas] }) - @OneToMany(() => BerkasTugas, (berkasTugas) => berkasTugas.tugas, { - cascade: true, - }) - berkasTugas: BerkasTugas[]; - - @ApiProperty() - @Column({ type: "timestamptz", default: () => "CURRENT_TIMESTAMP" }) - createdAt: Date; - - @ApiProperty() - @Column({ type: "timestamptz", default: () => "CURRENT_TIMESTAMP" }) - updatedAt: Date; - - @ManyToOne(() => Kelas, (kelas) => kelas.id) - @JoinColumn({ name: "kelasId" }) - kelas: Kelas; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - @Column() - kelasId: string; - - @OneToMany(() => SubmisiTugas, (submisiTugas) => submisiTugas.tugas) - submisiTugas: SubmisiTugas[]; -} diff --git a/src/env.validation.ts b/src/env.validation.ts index e14a7cc35fd80b3e9bd3c0dbb3fbcbee7c31e157..1d5e6d037f02d9f54e2c3c7a0af08adc63785ff6 100644 --- a/src/env.validation.ts +++ b/src/env.validation.ts @@ -34,36 +34,6 @@ class EnvironmentVariables { @IsUrl({ require_tld: false }) FE_URL: string; - @IsString() - KONF_PERIODE_KEY: string; - - @IsString() - KONF_MIN_BIMBINGAN_KEY: string; - - @IsString() - KONF_AWAL_PENDAFTARAN_KEY: string; - - @IsString() - KONF_AKHIR_PENDAFTARAN_KEY: string; - - @IsString() - KONF_AWAL_SEMPRO_KEY: string; - - @IsString() - KONF_AKHIR_SEMPRO_KEY: string; - - @IsString() - KONF_AWAL_SEM_TESIS_KEY: string; - - @IsString() - KONF_AKHIR_SEM_TESIS_KEY: string; - - @IsString() - KONF_AWAL_SIDANG_KEY: string; - - @IsString() - KONF_AKHIR_SIDANG_KEY: string; - @IsString() COOKIE_NAME: string; } diff --git a/src/kelas/kelas.constant.ts b/src/kelas/kelas.constant.ts deleted file mode 100644 index 050cbbca5914f9eb24c9e646e62e2629d047a51b..0000000000000000000000000000000000000000 --- a/src/kelas/kelas.constant.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const CARD_COLORS = [ - "bg-blue-600/20", - "bg-yellow-600/20", - "bg-green-600/20", - "bg-red-600/20", - "bg-purple-600/20", - "bg-pink-600/20", - "bg-indigo-600/20", - "bg-cyan-600/20", - "bg-amber-600/20", - "bg-lime-600/20", - "bg-emerald-600/20", - "bg-teal-600/20", - "bg-cyan-600/20", - "bg-violet-600/20", - "bg-fuchsia-600/20", - "bg-rose-600/20", - "bg-sky-600/20", - "bg-cyan-600/20", -]; diff --git a/src/kelas/kelas.controller.ts b/src/kelas/kelas.controller.ts deleted file mode 100644 index 3c64b3d4c8c22e85d157f750ee961ee852f9cf6d..0000000000000000000000000000000000000000 --- a/src/kelas/kelas.controller.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { - Body, - Controller, - Delete, - ForbiddenException, - Get, - Param, - Post, - Put, - Query, - Req, - UseGuards, -} from "@nestjs/common"; -import { - AssignKelasDto, - ByIdKelasDto, - CreateKelasDto, - DeleteKelasDto, - GetKelasDetailRespDto, - GetKelasQueryDto, - GetKelasRespDto, - GetNextNomorResDto, - IdKelasResDto, - KodeRespDto, - MessageResDto, - UnassignKelasDto, - UserKelasResDto, - UpdateKelasDto, - SearchQueryDto, - UpdateKelasPenggunaDto, -} from "./kelas.dto"; -import { Request } from "express"; -import { AuthDto } from "src/auth/auth.dto"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { - ApiBadRequestResponse, - ApiBearerAuth, - ApiCookieAuth, - ApiCreatedResponse, - ApiInternalServerErrorResponse, - ApiNotFoundResponse, - ApiOkResponse, - ApiOperation, - ApiTags, -} from "@nestjs/swagger"; -import { CustomAuthGuard } from "src/middlewares/custom-auth.guard"; -import { RolesGuard } from "src/middlewares/roles.guard"; -import { KelasService } from "./kelas.service"; -import { Roles } from "src/middlewares/roles.decorator"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { Kelas } from "src/entities/kelas.entity"; - -@ApiTags("Kelas") -@ApiBearerAuth() -@ApiCookieAuth() -@Controller("kelas") -@UseGuards(CustomAuthGuard, RolesGuard) -export class KelasController { - constructor(private readonly kelasServ: KelasService) {} - - @ApiOperation({ - summary: - "Get list of kelas. Roles: S2_KULIAH, S2_MAHASISWA, S2_TIM_TESIS, ADMIN", - }) - @ApiOkResponse({ type: GetKelasRespDto, isArray: true }) - @Roles( - RoleEnum.S2_KULIAH, - RoleEnum.S2_MAHASISWA, - RoleEnum.S2_TIM_TESIS, - RoleEnum.ADMIN, - ) - @Get() - async getListKelas(@Query() query: GetKelasQueryDto, @Req() req: Request) { - let idMahasiswa = undefined; - let idPengajar = undefined; - - const { id, roles } = req.user as AuthDto; - - if (!roles.includes(query.view)) { - throw new ForbiddenException(); - } - - if (query.view === RoleEnum.S2_KULIAH) { - idPengajar = id; - } - - if (query.view === RoleEnum.S2_MAHASISWA) { - idMahasiswa = id; - } - - return await this.kelasServ.getListKelas( - idMahasiswa, - idPengajar, - query.kodeMatkul, - query.search, - ); - } - - @ApiOkResponse({ type: MataKuliah, isArray: true }) - @Get("/mata-kuliah") - async getAllMatkul() { - return await this.kelasServ.getAllMatkul(); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: GetNextNomorResDto }) - @Get("/next-nomor/:kodeMatkul") - async getNextNomor( - @Param("kodeMatkul") kodeMatkul: string, - ): Promise<GetNextNomorResDto> { - const nomor = await this.kelasServ.getNextNomorKelas(kodeMatkul); - - return { - nomor, - }; - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: IdKelasResDto }) - @ApiBadRequestResponse({ - description: "Nomor kelas sudah ada", - }) - @ApiInternalServerErrorResponse({ - description: "Gagal membuat kelas", - }) - @Post() - async createKelas(@Body() body: CreateKelasDto): Promise<IdKelasResDto> { - return await this.kelasServ.create(body); - } - - // TODO: restrict payload - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: IdKelasResDto }) - @ApiNotFoundResponse({ - description: "Kelas dengan id (dan nomor) yang terkait tidak ditemukan", - }) - @ApiBadRequestResponse({ - description: - "(Saat pembuatan kelas) nomor kelas sudah ada atau mataKuliahKode tidak ada", - }) - @ApiInternalServerErrorResponse({ - description: "Gagal memperbarui kelas atau gagal membuat kelas", - }) - @Put() - async updateKelas(@Body() body: UpdateKelasDto): Promise<IdKelasResDto> { - return await this.kelasServ.updateOrCreate(body); - } - - @ApiCreatedResponse({ type: KodeRespDto }) - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @Post("mata-kuliah") - async createMataKuliah(@Body() body: MataKuliah) { - return await this.kelasServ.createMatkul(body); - } - - @ApiOkResponse({ type: Kelas }) - @ApiNotFoundResponse({ description: "Kelas tidak ditemukan" }) - @ApiInternalServerErrorResponse({ description: "Gagal menghapus kelas" }) - @Delete() - async delete(@Body() body: DeleteKelasDto): Promise<Kelas> { - return await this.kelasServ.delete(body); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: UserKelasResDto, isArray: true }) - @Get("/mahasiswa") - async getMahasiswa( - @Query() query: SearchQueryDto, - ): Promise<UserKelasResDto[]> { - return await this.kelasServ.getKelasPengguna("MAHASISWA", query.search); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiCreatedResponse({ type: MessageResDto }) - @ApiInternalServerErrorResponse({ description: "Gagal menambahkan kelas" }) - @Post("/mahasiswa/assign") - async assignKelasMahasiswa( - @Body() body: AssignKelasDto, - ): Promise<MessageResDto> { - return await this.kelasServ.assignKelasMahasiswa(body); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: MessageResDto }) - @ApiInternalServerErrorResponse({ description: "Gagal menghapus kelas" }) - @Delete("/mahasiswa/unassign") - async unassignKelasMahasiswa( - @Body() body: UnassignKelasDto, - ): Promise<MessageResDto> { - return await this.kelasServ.unassignKelasMahasiswa(body); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: UserKelasResDto, isArray: true }) - @Get("/dosen") - async getDosen(@Query() query: SearchQueryDto): Promise<UserKelasResDto[]> { - return await this.kelasServ.getKelasPengguna("DOSEN", query.search); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiCreatedResponse({ type: MessageResDto }) - @ApiInternalServerErrorResponse({ description: "Gagal menambahkan kelas" }) - @Post("/dosen/assign") - async assignKelasDosen(@Body() body: AssignKelasDto): Promise<MessageResDto> { - return await this.kelasServ.assignKelasDosen(body); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOkResponse({ type: MessageResDto }) - @ApiInternalServerErrorResponse({ description: "Gagal menghapus kelas" }) - @Delete("/dosen/unassign") - async unassignKelasDosen( - @Body() body: UnassignKelasDto, - ): Promise<MessageResDto> { - return await this.kelasServ.unassignKelasDosen(body); - } - - @Roles( - RoleEnum.S2_TIM_TESIS, - RoleEnum.ADMIN, - RoleEnum.S2_KULIAH, - RoleEnum.S2_MAHASISWA, - ) - @ApiOkResponse({ type: GetKelasRespDto }) - @ApiOperation({ - summary: - "Get kelas general information by kelas id. Roles: S2_KULIAH, S2_MAHASISWA, S2_TIM_TESIS, ADMIN", - }) - @Get("/:id") - async getById( - @Param() param: ByIdKelasDto, - @Req() req: Request, - ): Promise<GetKelasRespDto> { - let idMahasiswa = undefined; - let idPengajar = undefined; - - const { id, roles } = req.user as AuthDto; - - if ( - !roles.includes(RoleEnum.S2_TIM_TESIS) && - !roles.includes(RoleEnum.ADMIN) - ) { - if (roles.includes(RoleEnum.S2_KULIAH)) { - idPengajar = id; - } else { - // requester only has S2_MAHASISWA access - idMahasiswa = id; - } - } - - return await this.kelasServ.getById(param.id, idMahasiswa, idPengajar); - } - - @Roles( - RoleEnum.S2_TIM_TESIS, - RoleEnum.ADMIN, - RoleEnum.S2_KULIAH, - RoleEnum.S2_MAHASISWA, - ) - @ApiOperation({ - summary: - "Get kelas mahasiswa and pengajar list by kelas id. Roles: S2_KULIAH, S2_MAHASISWA, S2_TIM_TESIS, ADMIN", - }) - @ApiOkResponse({ type: GetKelasDetailRespDto }) - @Get("/:id/detail") - async getKelasDetail(@Param() param: ByIdKelasDto, @Req() req: Request) { - let idMahasiswa = undefined; - let idPengajar = undefined; - - const { id, roles } = req.user as AuthDto; - - if ( - !roles.includes(RoleEnum.S2_TIM_TESIS) && - !roles.includes(RoleEnum.ADMIN) - ) { - if (roles.includes(RoleEnum.S2_KULIAH)) { - idPengajar = id; - } else { - // requester only has S2_MAHASISWA access - idMahasiswa = id; - } - } - - return await this.kelasServ.getKelasDetail( - param.id, - idMahasiswa, - idPengajar, - ); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOperation({ - summary: "Update kelas mahasiswa. Roles: S2_TIM_TESIS, ADMIN", - }) - @ApiOkResponse({ type: ByIdKelasDto }) - @Put("/mahasiswa") - async updateKelasMahasiswa( - @Body() body: UpdateKelasPenggunaDto, - ): Promise<ByIdKelasDto> { - return await this.kelasServ.updateKelas(body, "MAHASISWA"); - } - - @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN) - @ApiOperation({ - summary: "Update kelas dosen. Roles: S2_TIM_TESIS, ADMIN", - }) - @ApiOkResponse({ type: ByIdKelasDto }) - @Put("/dosen") - async updateKelasDosen( - @Body() body: UpdateKelasPenggunaDto, - ): Promise<ByIdKelasDto> { - return await this.kelasServ.updateKelas(body, "DOSEN"); - } -} diff --git a/src/kelas/kelas.dto.ts b/src/kelas/kelas.dto.ts deleted file mode 100644 index 3647164851b0a08f38fda3c651db4cc17587c086..0000000000000000000000000000000000000000 --- a/src/kelas/kelas.dto.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { - IsEnum, - IsOptional, - IsPositive, - IsUUID, -} from "@nestjs/class-validator"; -import { - ApiProperty, - PickType, - PartialType, - ApiPropertyOptional, -} from "@nestjs/swagger"; -import { Kelas } from "src/entities/kelas.entity"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { Pengguna, RoleEnum } from "src/entities/pengguna.entity"; - -export class CreateKelasDto extends PickType(Kelas, [ - "mataKuliahKode", -] as const) { - @ApiPropertyOptional({ example: 1 }) - @IsOptional() - @IsPositive() - nomor: number; -} - -export class UpdateKelasDto extends PartialType(Kelas) {} - -export class IdKelasResDto extends PickType(Kelas, ["id"] as const) {} - -export class DeleteKelasDto extends PickType(Kelas, [ - "mataKuliahKode", - "nomor", -] as const) {} - -export class GetKelasQueryDto { - @IsEnum([RoleEnum.S2_KULIAH, RoleEnum.S2_MAHASISWA, RoleEnum.S2_TIM_TESIS]) - @ApiProperty({ - enum: [RoleEnum.S2_KULIAH, RoleEnum.S2_MAHASISWA, RoleEnum.S2_TIM_TESIS], - }) - view: RoleEnum.S2_KULIAH | RoleEnum.S2_MAHASISWA | RoleEnum.S2_TIM_TESIS; - - @ApiPropertyOptional({ example: "IF3270" }) - @IsOptional() - kodeMatkul: string; - - @ApiPropertyOptional({ example: "Intelegensi Buatan" }) - @IsOptional() - search: string; -} - -export class SearchQueryDto { - @ApiPropertyOptional({ example: "Intelegensi Buatan" }) - @IsOptional() - search: string; -} - -export class ByIdKelasDto extends PickType(Kelas, ["id"] as const) {} - -export class GetKelasRespDto { - @ApiProperty() - id: string; - - @ApiProperty({ example: "K02" }) - nomor: string; - - @ApiProperty({ example: "IF3270" }) - kode_mata_kuliah: string; - - @ApiProperty({ example: "Pengembangan Aplikasi Terdistribusi" }) - nama_mata_kuliah: string; - - @ApiProperty() - jumlah_mahasiswa: number; - - @ApiProperty({ example: "bg-blue-600/20" }) - warna: string; -} - -export class KodeRespDto extends PickType(MataKuliah, ["kode"] as const) {} - -export class AssignKelasDto { - @ApiProperty() - @IsUUID("all", { each: true }) - kelasIds: string[]; - - @ApiProperty() - @IsUUID("all", { each: true }) - penggunaIds: string[]; -} - -export class UnassignKelasDto extends PickType(AssignKelasDto, [ - "penggunaIds", -] as const) {} - -export class MessageResDto { - @ApiProperty() - message: string; -} - -class KelasUser extends PickType(Kelas, [ - "id", - "nomor", - "mataKuliahKode", -] as const) { - @ApiProperty() - mataKuliahNama: string; -} - -export class UserKelasResDto extends PickType(Pengguna, [ - "id", - "nama", - "email", -] as const) { - @ApiProperty({ type: [KelasUser] }) - kelas: KelasUser[]; -} -export class GetNextNomorResDto { - @ApiProperty({ example: 2 }) - nomor: number; -} - -class PickedPengajarKelasDto extends PickType(Pengguna, [ - "id", - "nama", -] as const) {} - -class PickedMahasiswaKelasDto extends PickType(Pengguna, [ - "id", - "nama", - "nim", -] as const) {} - -export class GetKelasDetailRespDto extends PickType(Kelas, ["id"] as const) { - @ApiProperty({ type: [PickedPengajarKelasDto] }) - pengajar: PickedPengajarKelasDto[]; - - @ApiProperty({ type: [PickedMahasiswaKelasDto] }) - mahasiswa: PickedMahasiswaKelasDto[]; -} - -export class UpdateKelasPenggunaDto extends PickType(AssignKelasDto, [ - "kelasIds" as const, -]) { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - penggunaId: string; -} diff --git a/src/kelas/kelas.module.ts b/src/kelas/kelas.module.ts deleted file mode 100644 index dde13c556b1ffb854621c5394945ace1ed81225c..0000000000000000000000000000000000000000 --- a/src/kelas/kelas.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Module } from "@nestjs/common"; -import { KelasService } from "./kelas.service"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { Kelas } from "src/entities/kelas.entity"; -import { AuthModule } from "src/auth/auth.module"; -import { KonfigurasiModule } from "src/konfigurasi/konfigurasi.module"; -import { KelasController } from "./kelas.controller"; -import { CustomStrategy } from "src/middlewares/custom.strategy"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import { PengajarKelas } from "src/entities/pengajarKelas.entity"; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - Kelas, - MataKuliah, - Pengguna, - MahasiswaKelas, - PengajarKelas, - ]), - AuthModule, - KonfigurasiModule, - ], - controllers: [KelasController], - providers: [KelasService, CustomStrategy], -}) -export class KelasModule {} diff --git a/src/kelas/kelas.service.ts b/src/kelas/kelas.service.ts deleted file mode 100644 index f55b832ce066eb60d33e6e2ce1900ddbd9de5cbe..0000000000000000000000000000000000000000 --- a/src/kelas/kelas.service.ts +++ /dev/null @@ -1,708 +0,0 @@ -import { - BadRequestException, - Injectable, - InternalServerErrorException, - NotFoundException, -} from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Kelas } from "src/entities/kelas.entity"; -import { Brackets, DataSource, Repository } from "typeorm"; -import { - CreateKelasDto, - AssignKelasDto, - DeleteKelasDto, - GetKelasDetailRespDto, - GetKelasRespDto, - IdKelasResDto, - UpdateKelasDto, - MessageResDto, - UnassignKelasDto, - UpdateKelasPenggunaDto, - ByIdKelasDto, -} from "./kelas.dto"; -import { KonfigurasiService } from "src/konfigurasi/konfigurasi.service"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { CARD_COLORS } from "./kelas.constant"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import { PengajarKelas } from "src/entities/pengajarKelas.entity"; - -@Injectable() -export class KelasService { - constructor( - @InjectRepository(Kelas) - private kelasRepo: Repository<Kelas>, - @InjectRepository(MataKuliah) - private mataKuliahRepo: Repository<MataKuliah>, - private konfService: KonfigurasiService, - @InjectRepository(Pengguna) - private penggunaRepo: Repository<Pengguna>, - @InjectRepository(MahasiswaKelas) - private mahasiswaKelasRepo: Repository<MahasiswaKelas>, - @InjectRepository(PengajarKelas) - private pengajarKelasRepo: Repository<PengajarKelas>, - private datasource: DataSource, - ) {} - async getListKelas( - idMahasiswa?: string, - idPengajar?: string, - kodeMatkul?: string, - search?: string, - ) { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - let baseQuery = this.kelasRepo - .createQueryBuilder("kelas") - .leftJoinAndSelect("kelas.mahasiswa", "mahasiswa") - .leftJoinAndSelect("kelas.mataKuliah", "mataKuliah") - .select([ - "kelas.id AS id", - "kelas.nomor AS nomor", - "kelas.warna AS warna", - "mataKuliah.kode AS kode_mata_kuliah", - "mataKuliah.nama AS nama_mata_kuliah", - "COUNT(mahasiswa) AS jumlah_mahasiswa", - ]) - .orderBy("mataKuliah.kode", "ASC") - .addOrderBy("kelas.nomor", "ASC") - .where("kelas.periode = :periode", { periode: currPeriod }); - - if (idMahasiswa) { - baseQuery = baseQuery - .innerJoin("kelas.mahasiswa", "mahasiswa_filter") - .andWhere("mahasiswa_filter.mahasiswaId = :idMahasiswa", { - idMahasiswa, - }); - } - - if (idPengajar) { - baseQuery = baseQuery - .innerJoin("kelas.pengajar", "pengajar") - .andWhere("pengajar.pengajarId = :idPengajar", { - idPengajar, - }); - } - - if (kodeMatkul) { - baseQuery = baseQuery.andWhere("mataKuliah.kode = :kodeMatkul", { - kodeMatkul, - }); - } - - if (search) { - baseQuery = baseQuery.andWhere( - new Brackets((qb) => { - qb.where("mataKuliah.kode ILIKE :search", { - search: `%${search}%`, - }).orWhere("mataKuliah.nama ILIKE :search", { - search: `%${search}%`, - }); - }), - ); - } - - const result = await baseQuery - .groupBy("kelas.id, mataKuliah.kode") - .getRawMany(); - - const mapped: GetKelasRespDto[] = result.map((r) => ({ - id: r.id, - nomor: "K" + `${r.nomor}`.padStart(2, "0"), - kode_mata_kuliah: r.kode_mata_kuliah, - nama_mata_kuliah: r.nama_mata_kuliah, - jumlah_mahasiswa: parseInt(r.jumlah_mahasiswa), - warna: r.warna, - })); - - return mapped; - } - - async getById(id: string, idMahasiswa?: string, idPengajar?: string) { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - let baseQuery = this.kelasRepo - .createQueryBuilder("kelas") - .leftJoinAndSelect("kelas.mahasiswa", "mahasiswa") - .leftJoinAndSelect("kelas.mataKuliah", "mataKuliah") - .select([ - "kelas.id AS id", - "kelas.nomor AS nomor", - "kelas.warna AS warna", - "mataKuliah.kode AS kode_mata_kuliah", - "mataKuliah.nama AS nama_mata_kuliah", - "COUNT(mahasiswa) AS jumlah_mahasiswa", - ]) - .where("kelas.id = :id", { id }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - if (idMahasiswa) { - baseQuery = baseQuery - .innerJoin("kelas.mahasiswa", "mahasiswa_filter") - .andWhere("mahasiswa_filter.mahasiswaId = :idMahasiswa", { - idMahasiswa, - }); - } - - if (idPengajar) { - baseQuery = baseQuery - .innerJoin("kelas.pengajar", "pengajar") - .andWhere("pengajar.pengajarId = :idPengajar", { - idPengajar, - }); - } - - const result = await baseQuery - .groupBy("kelas.id, mataKuliah.kode") - .getRawOne(); - - if (!result) { - throw new NotFoundException("Kelas tidak ditemukan"); - } - - const mapped: GetKelasRespDto = { - id: result.id, - nomor: "K" + `${result.nomor}`.padStart(2, "0"), - kode_mata_kuliah: result.kode_mata_kuliah, - nama_mata_kuliah: result.nama_mata_kuliah, - jumlah_mahasiswa: parseInt(result.jumlah_mahasiswa), - warna: result.warna, - }; - - return mapped; - } - - async getKelasDetail( - idKelas: string, - idMahasiswa?: string, - idPengajar?: string, - ) { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - let baseQuery = this.kelasRepo - .createQueryBuilder("kelas") - .leftJoinAndSelect("kelas.mahasiswa", "mahasiswaKelas") - .leftJoinAndSelect("mahasiswaKelas.mahasiswa", "mahasiswa") - .leftJoinAndSelect("kelas.pengajar", "pengajarKelas") - .leftJoinAndSelect("pengajarKelas.pengajar", "pengajar") - .select([ - "kelas.id", - "mahasiswaKelas.id", - "mahasiswa.id", - "mahasiswa.nim", - "mahasiswa.nama", - "pengajarKelas.id", - "pengajar.id", - "pengajar.nama", - ]) - .orderBy("pengajar.nama", "ASC") - .addOrderBy("mahasiswa.nim", "ASC") - .where("kelas.id = :idKelas", { idKelas }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - if (idMahasiswa) { - baseQuery = baseQuery - .innerJoin("kelas.mahasiswa", "mahasiswaFilter") - .andWhere("mahasiswaFilter.mahasiswaId = :idMahasiswa", { - idMahasiswa, - }); - } - - if (idPengajar) { - baseQuery = baseQuery - .innerJoin("kelas.pengajar", "pengajarFilter") - .andWhere("pengajarFilter.pengajarId = :idPengajar", { - idPengajar, - }); - } - - const result = await baseQuery.getOne(); - - if (!result) { - throw new NotFoundException( - "Kelas tidak ditemukan di antara kelas yang dapat Anda akses", - ); - } - - const mapped: GetKelasDetailRespDto = { - id: result.id, - pengajar: result.pengajar.map((p) => ({ - id: p.pengajar.id, - nama: p.pengajar.nama, - })), - mahasiswa: result.mahasiswa.map((m) => ({ - id: m.mahasiswa.id, - nim: m.mahasiswa.nim, - nama: m.mahasiswa.nama, - })), - }; - - return mapped; - } - - async create(createDto: CreateKelasDto): Promise<IdKelasResDto> { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - let nomor = createDto.nomor; - if (nomor) { - const checkClassQueary = this.kelasRepo - .createQueryBuilder("kelas") - .where("kelas.nomor = :nomor", { nomor }) - .andWhere("kelas.mataKuliahKode = :mataKuliahKode", { - mataKuliahKode: createDto.mataKuliahKode, - }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const checkClass = await checkClassQueary.getOne(); - - if (checkClass) { - throw new BadRequestException(`Kelas dengan nomor ${nomor} sudah ada`); - } - } else { - nomor = await this.getNextNomorKelas(createDto.mataKuliahKode); - } - - const colorIdx = Math.floor(Math.random() * CARD_COLORS.length); - const kelas = this.kelasRepo.create({ - ...createDto, - nomor, - periode: currPeriod, - warna: CARD_COLORS[colorIdx], - }); - - try { - await this.kelasRepo.save(kelas); - } catch { - throw new InternalServerErrorException("Gagal membuat kelas baru"); - } - - return { - id: kelas.id, - }; - } - - async createMatkul(createDto: MataKuliah) { - await this.mataKuliahRepo.insert(createDto); - - return { kode: createDto.kode }; - } - - async getKelasPengguna( - mode: "MAHASISWA" | "DOSEN", - search?: string, - id?: string, - ) { - const currPeriod = await this.konfService.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - const relation = mode === "MAHASISWA" ? "mahasiswaKelas" : "pengajarKelas"; - - let penggunaQuery = this.penggunaRepo - .createQueryBuilder("pengguna") - .select(["pengguna.id", "pengguna.nama", "pengguna.email"]) - .leftJoinAndSelect(`pengguna.${relation}`, relation) - .leftJoinAndSelect( - `${relation}.kelas`, - "kelas", - "kelas.periode = :periode", - { - periode: currPeriod, - }, - ) - .leftJoinAndSelect("kelas.mataKuliah", "mataKuliah") - .where("pengguna.roles @> :role", { - role: [mode === "MAHASISWA" ? "S2_MAHASISWA" : "S2_KULIAH"], - }); - - if (search) { - penggunaQuery = penggunaQuery.andWhere( - new Brackets((qb) => { - qb.where("pengguna.nama ILIKE :search", { search: `%${search}%` }); - qb.orWhere("pengguna.email ILIKE :search", { search: `%${search}%` }); - }), - ); - } - - if (id) { - penggunaQuery = penggunaQuery.andWhere("pengguna.id = :id", { id }); - } - - const mhs = await penggunaQuery.getMany(); - - return mhs.map((m) => ({ - id: m.id, - nama: m.nama, - email: m.email, - kelas: m?.[relation].map((k) => ({ - id: k.kelas.id, - nomor: k.kelas.nomor, - mataKuliahKode: k.kelas.mataKuliahKode, - mataKuliahNama: k.kelas.mataKuliah.nama, - })), - })); - } - - async assignKelasMahasiswa(dto: AssignKelasDto): Promise<MessageResDto> { - const currPeriod = await this.konfService.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - const queryRunner = this.datasource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - for (const mhsId of dto.penggunaIds) { - const currKelasQuery = queryRunner.manager - .createQueryBuilder(MahasiswaKelas, "mahasiswaKelas") - .leftJoinAndSelect("mahasiswaKelas.kelas", "kelas") - .where("mahasiswaKelas.mahasiswaId = :mhsId", { mhsId }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const currKelas = (await currKelasQuery.getMany()).map( - (k) => k.kelasId, - ); - - for (const kelasId of dto.kelasIds) { - if (currKelas.includes(kelasId)) { - continue; - } - - await queryRunner.manager.insert(MahasiswaKelas, { - mahasiswaId: mhsId, - kelasId, - }); - } - } - - await queryRunner.commitTransaction(); - } catch { - await queryRunner.rollbackTransaction(); - - throw new InternalServerErrorException("Gagal menambahkan kelas"); - } finally { - await queryRunner.release(); - } - - return { message: "Kelas berhasil diassign" }; - } - - async unassignKelasMahasiswa(dto: UnassignKelasDto): Promise<MessageResDto> { - const currPeriod = await this.konfService.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - const queryRunner = this.datasource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - for (const mhsId of dto.penggunaIds) { - const currKelasQuery = queryRunner.manager - .createQueryBuilder(MahasiswaKelas, "mahasiswaKelas") - .leftJoinAndSelect("mahasiswaKelas.kelas", "kelas") - .where("mahasiswaKelas.mahasiswaId = :mhsId", { mhsId }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const currKelas = (await currKelasQuery.getMany()).map( - (k) => k.kelasId, - ); - - for (const kelasId of currKelas) { - await queryRunner.manager.delete(MahasiswaKelas, { - mahasiswaId: mhsId, - kelasId, - }); - } - } - - await queryRunner.commitTransaction(); - } catch { - await queryRunner.rollbackTransaction(); - - throw new InternalServerErrorException("Gagal menghapus kelas"); - } finally { - await queryRunner.release(); - } - - return { message: "Kelas berhasil dihapus" }; - } - - async assignKelasDosen(dto: AssignKelasDto): Promise<MessageResDto> { - const currPeriod = await this.konfService.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - const queryRunner = this.datasource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - for (const dosenId of dto.penggunaIds) { - const currKelasQuery = queryRunner.manager - .createQueryBuilder(PengajarKelas, "pengajarKelas") - .leftJoinAndSelect("pengajarKelas.kelas", "kelas") - .where("pengajarKelas.pengajarId = :dosenId", { dosenId }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const currKelas = (await currKelasQuery.getMany()).map( - (k) => k.kelasId, - ); - - for (const kelasId of dto.kelasIds) { - if (currKelas.includes(kelasId)) { - continue; - } - - await queryRunner.manager.insert(PengajarKelas, { - pengajarId: dosenId, - kelasId, - }); - } - } - - await queryRunner.commitTransaction(); - } catch { - await queryRunner.rollbackTransaction(); - - throw new InternalServerErrorException("Gagal menambahkan kelas"); - } finally { - await queryRunner.release(); - } - - return { message: "Kelas berhasil diassign" }; - } - - async unassignKelasDosen(dto: UnassignKelasDto): Promise<MessageResDto> { - const currPeriod = await this.konfService.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - const queryRunner = this.datasource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - for (const dosenId of dto.penggunaIds) { - const currKelasQuery = queryRunner.manager - .createQueryBuilder(PengajarKelas, "pengajarKelas") - .leftJoinAndSelect("pengajarKelas.kelas", "kelas") - .where("pengajarKelas.pengajarId = :dosenId", { dosenId }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const currKelas = (await currKelasQuery.getMany()).map( - (k) => k.kelasId, - ); - - for (const kelasId of currKelas) { - await queryRunner.manager.delete(PengajarKelas, { - pengajarId: dosenId, - kelasId, - }); - } - } - - await queryRunner.commitTransaction(); - } catch { - await queryRunner.rollbackTransaction(); - - throw new InternalServerErrorException("Gagal menghapus kelas"); - } finally { - await queryRunner.release(); - } - - return { message: "Kelas berhasil dihapus" }; - } - - async updateOrCreate(dto: UpdateKelasDto): Promise<IdKelasResDto> { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - if (!dto.id) { - // Create kelas - if (!dto.mataKuliahKode) { - throw new BadRequestException("Kode mata kuliah tidak boleh kosong"); - } - - return await this.create({ - mataKuliahKode: dto.mataKuliahKode, - nomor: dto.nomor, - }); - } else { - // Update kelas - const kelasQuery = this.kelasRepo - .createQueryBuilder("kelas") - .where("kelas.id = :id", { id: dto.id }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - if (dto.nomor) { - kelasQuery.andWhere("kelas.nomor = :nomor", { nomor: dto.nomor }); - } - - const kelas = await kelasQuery.getOne(); - - if (!kelas) { - throw new NotFoundException("Kelas tidak ditemukan"); - } - - try { - await this.kelasRepo.update(kelas.id, dto); - } catch { - throw new InternalServerErrorException("Gagal memperbarui kelas"); - } - - return { - id: kelas.id, - }; - } - } - - async delete(dto: DeleteKelasDto): Promise<Kelas> { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - const kelasQuery = this.kelasRepo - .createQueryBuilder("kelas") - .where("kelas.nomor = :nomor", { nomor: dto.nomor }) - .andWhere("kelas.mataKuliahKode = :mataKuliahKode", { - mataKuliahKode: dto.mataKuliahKode, - }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const kelas = await kelasQuery.getOne(); - - if (!kelas) { - throw new BadRequestException("Kelas tidak ditemukan"); - } - - try { - await this.kelasRepo.delete(kelas.id); - } catch { - throw new InternalServerErrorException("Gagal menghapus kelas"); - } - - return kelas; - } - - async getAllMatkul(): Promise<MataKuliah[]> { - return await this.mataKuliahRepo.find(); - } - - async getNextNomorKelas(kodeMatkul: string): Promise<number> { - const currPeriod = await this.konfService.getPeriodeOrFail(); - - const maxClass = await this.kelasRepo.findOne({ - where: { - mataKuliahKode: kodeMatkul, - periode: currPeriod, - }, - order: { - nomor: "DESC", - }, - }); - - return maxClass ? maxClass.nomor + 1 : 1; - } - - async updateKelas( - dto: UpdateKelasPenggunaDto, - mode: "MAHASISWA" | "DOSEN", - ): Promise<ByIdKelasDto> { - const currPeriod = await this.konfService.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - const queryRunner = this.datasource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - if (mode === "MAHASISWA") { - const currKelasQuery = queryRunner.manager - .createQueryBuilder(MahasiswaKelas, "mahasiswaKelas") - .leftJoinAndSelect("mahasiswaKelas.kelas", "kelas") - .where("mahasiswaKelas.mahasiswaId = :mhsId", { - mhsId: dto.penggunaId, - }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const currKelas = (await currKelasQuery.getMany()).map( - (k) => k.kelasId, - ); - - // Delete all kelas mahasiswa - for (const kelasId of currKelas) { - await queryRunner.manager.delete(MahasiswaKelas, { - mahasiswaId: dto.penggunaId, - kelasId, - }); - } - - // Assign kelas mahasiswa - for (const kelasId of dto.kelasIds) { - await queryRunner.manager.insert(MahasiswaKelas, { - mahasiswaId: dto.penggunaId, - kelasId, - }); - } - } else { - const currKelasQuery = queryRunner.manager - .createQueryBuilder(PengajarKelas, "pengajarKelas") - .leftJoinAndSelect("pengajarKelas.kelas", "kelas") - .where("pengajarKelas.pengajarId = :dosenId", { - dosenId: dto.penggunaId, - }) - .andWhere("kelas.periode = :periode", { periode: currPeriod }); - - const currKelas = (await currKelasQuery.getMany()).map( - (k) => k.kelasId, - ); - - for (const kelasId of currKelas) { - await queryRunner.manager.delete(PengajarKelas, { - pengajarId: dto.penggunaId, - kelasId, - }); - } - - for (const kelasId of dto.kelasIds) { - await queryRunner.manager.insert(PengajarKelas, { - pengajarId: dto.penggunaId, - kelasId, - }); - } - } - - await queryRunner.commitTransaction(); - } catch { - await queryRunner.rollbackTransaction(); - - throw new InternalServerErrorException("Gagal mengupdate kelas pengguna"); - } finally { - await queryRunner.release(); - } - - return { id: dto.penggunaId }; - } -} diff --git a/src/konfigurasi/konfigurasi.controller.ts b/src/konfigurasi/konfigurasi.controller.ts deleted file mode 100644 index 8f5b99b300caba5f8c9d09d5b032feaa08f613ab..0000000000000000000000000000000000000000 --- a/src/konfigurasi/konfigurasi.controller.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Body, Controller, Get, Put, UseGuards } from "@nestjs/common"; -import { KonfigurasiService } from "./konfigurasi.service"; -import { KonfigurasiArrDto, UpdateKonfigurasiResDto } from "./konfigurasi.dto"; -import { CustomAuthGuard } from "src/middlewares/custom-auth.guard"; -import { RolesGuard } from "src/middlewares/roles.guard"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { Roles } from "src/middlewares/roles.decorator"; -import { - ApiBearerAuth, - ApiCookieAuth, - ApiOkResponse, - ApiTags, -} from "@nestjs/swagger"; - -@ApiTags("Konfigurasi") -@ApiCookieAuth() -@ApiBearerAuth() -@Controller("konfigurasi") -@UseGuards(CustomAuthGuard, RolesGuard) -@Roles(RoleEnum.ADMIN, RoleEnum.S2_TIM_TESIS) -export class KonfigurasiController { - constructor(private readonly konfigurasiService: KonfigurasiService) {} - - @ApiOkResponse({ type: UpdateKonfigurasiResDto }) - @Put() - async updateKonfigurasi( - @Body() data: KonfigurasiArrDto, - ): Promise<UpdateKonfigurasiResDto> { - await this.konfigurasiService.updateKonfigurasi(data); - return { message: "success" }; - } - - @ApiOkResponse({ type: KonfigurasiArrDto }) - @Get() - async getKonfigurasi(): Promise<KonfigurasiArrDto> { - return this.konfigurasiService.getKonfigurasi(); - } -} diff --git a/src/konfigurasi/konfigurasi.dto.ts b/src/konfigurasi/konfigurasi.dto.ts deleted file mode 100644 index 0de2f06b9f82d10df61c2c475ab1e56a26d3342a..0000000000000000000000000000000000000000 --- a/src/konfigurasi/konfigurasi.dto.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Type } from "class-transformer"; -import { IsString, ValidateNested } from "class-validator"; - -export class KonfigurasiDto { - @ApiProperty() - @IsString() - key: string; - - @ApiProperty() - @IsString() - value: string; -} -export class KonfigurasiArrDto { - @ApiProperty({ type: [KonfigurasiDto] }) - @ValidateNested({ each: true }) - @Type(() => KonfigurasiDto) - data: KonfigurasiDto[]; -} - -export class UpdateKonfigurasiResDto { - @ApiProperty() - message: string; -} diff --git a/src/konfigurasi/konfigurasi.module.ts b/src/konfigurasi/konfigurasi.module.ts deleted file mode 100644 index 8a1fcc680131abd16772f7002a54c203572257be..0000000000000000000000000000000000000000 --- a/src/konfigurasi/konfigurasi.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from "@nestjs/common"; -import { KonfigurasiController } from "./konfigurasi.controller"; -import { KonfigurasiService } from "./konfigurasi.service"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { Konfigurasi } from "src/entities/konfigurasi.entity"; - -@Module({ - imports: [TypeOrmModule.forFeature([Konfigurasi])], - controllers: [KonfigurasiController], - providers: [KonfigurasiService], - exports: [KonfigurasiService], -}) -export class KonfigurasiModule {} diff --git a/src/konfigurasi/konfigurasi.service.ts b/src/konfigurasi/konfigurasi.service.ts deleted file mode 100644 index a8e7edaccbcafeeab8aa59f9039ce69ae88a21c2..0000000000000000000000000000000000000000 --- a/src/konfigurasi/konfigurasi.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BadRequestException, Injectable } from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Konfigurasi } from "src/entities/konfigurasi.entity"; -import { Repository } from "typeorm"; -import { KonfigurasiArrDto } from "./konfigurasi.dto"; - -@Injectable() -export class KonfigurasiService { - constructor( - @InjectRepository(Konfigurasi) - private konfigurasiRepository: Repository<Konfigurasi>, - ) {} - - async updateKonfigurasi({ data }: KonfigurasiArrDto) { - return await this.konfigurasiRepository.upsert(data, ["key"]); - } - - async getKonfigurasi(): Promise<KonfigurasiArrDto> { - const data = await this.konfigurasiRepository.find(); - return { data }; - } - - async getKonfigurasiByKey(key: string): Promise<string | undefined> { - const data = await this.konfigurasiRepository.findOne({ - where: { - key, - }, - }); - - return data?.value; - } - - async getPeriodeOrFail() { - const currPeriod = await this.getKonfigurasiByKey( - process.env.KONF_PERIODE_KEY, - ); - - if (!currPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi"); - } - - return currPeriod; - } -} diff --git a/src/nilai/nilai.controller.ts b/src/nilai/nilai.controller.ts deleted file mode 100644 index 6bb1cb659a4ad0347e37c69df0ad85750da2d1ad..0000000000000000000000000000000000000000 --- a/src/nilai/nilai.controller.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Body, Controller, Get, Patch, Query, UseGuards } from "@nestjs/common"; -import { NilaiService } from "./nilai.service"; -import { - ApiBearerAuth, - ApiCookieAuth, - ApiOkResponse, - ApiTags, -} from "@nestjs/swagger"; -import { CustomAuthGuard } from "src/middlewares/custom-auth.guard"; -import { RolesGuard } from "src/middlewares/roles.guard"; -import { Roles } from "src/middlewares/roles.decorator"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { - GetNilaiByMatkulQueryDto, - GetNilaiByMatkulRespDto, - UpdateNilaiDto, - UpdateNilaiRespDto, -} from "./nilai.dto"; - -@ApiBearerAuth() -@ApiCookieAuth() -@ApiTags("Nilai") -@UseGuards(CustomAuthGuard, RolesGuard) -@Roles(RoleEnum.ADMIN, RoleEnum.S2_TIM_TESIS) -@Controller("nilai") -export class NilaiController { - constructor(private nilaiServ: NilaiService) {} - - @ApiOkResponse({ type: [GetNilaiByMatkulRespDto] }) - @Get() - async getNilaiByMatkul(@Query() query: GetNilaiByMatkulQueryDto) { - return this.nilaiServ.getNilaiByMatkul( - query.kode, - query.page || 1, - query.limit || 10, - query.search || "", - ); - } - - @ApiOkResponse({ type: UpdateNilaiRespDto }) - @Patch() - async updateNilai(@Body() body: UpdateNilaiDto) { - return this.nilaiServ.updateNilai(body.mahasiswaKelasIds, body.nilaiAkhir); - } -} diff --git a/src/nilai/nilai.dto.ts b/src/nilai/nilai.dto.ts deleted file mode 100644 index 1b933d07468212dbfacd8fc41d441227b5084418..0000000000000000000000000000000000000000 --- a/src/nilai/nilai.dto.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { IsNumberString } from "@nestjs/class-validator"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { - IsNumber, - IsOptional, - IsPositive, - IsString, - IsUUID, - MaxLength, - MinLength, -} from "class-validator"; - -export class GetNilaiByMatkulQueryDto { - @ApiPropertyOptional({ example: "IF4031" }) - @IsOptional() - @IsString() - @MinLength(6) - @MaxLength(6) - kode?: string; - - @ApiPropertyOptional() - @IsOptional() - @IsString() - search?: string; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 1" }) - page?: number; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 10" }) - limit?: number; -} - -export class GetNilaiByMatkulRespDto { - @ApiProperty({ example: "IF4031" }) - mata_kuliah_kode: string; - - @ApiProperty({ example: "Pemrograman Berbasis Kerangka Kerja" }) - mata_kuliah_nama: string; - - @ApiProperty() - kelas_nomor: number; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - mahasiswa_kelas_id: string; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - mahasiswa_id: string; - - @ApiProperty() - mahasiswa_nama: string; - - @ApiProperty({ example: "13517000" }) - mahasiswa_nim: string; - - @ApiProperty() - nilai_akhir: string | null; -} - -export class UpdateNilaiRespDto { - @ApiProperty({ - type: [String], - example: ["550e8400-e29b-41d4-a716-446655440000"], - }) - @IsUUID("all", { each: true }) - mahasiswaKelasIds: string[]; -} - -export class UpdateNilaiDto extends UpdateNilaiRespDto { - @ApiPropertyOptional({ description: "undefined: assign nilai to null" }) - @IsOptional() - @IsNumber() - @IsPositive() - nilaiAkhir?: number; -} diff --git a/src/nilai/nilai.module.ts b/src/nilai/nilai.module.ts deleted file mode 100644 index c0a7c9c19ed393373632c2a8f3f8f5c6cc5610a8..0000000000000000000000000000000000000000 --- a/src/nilai/nilai.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Module } from "@nestjs/common"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { AuthModule } from "src/auth/auth.module"; -import { Kelas } from "src/entities/kelas.entity"; -import { Konfigurasi } from "src/entities/konfigurasi.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { KelasModule } from "src/kelas/kelas.module"; -import { KonfigurasiModule } from "src/konfigurasi/konfigurasi.module"; -import { NilaiController } from "./nilai.controller"; -import { NilaiService } from "./nilai.service"; -import { CustomStrategy } from "src/middlewares/custom.strategy"; -import { KonfigurasiService } from "src/konfigurasi/konfigurasi.service"; -import { KelasService } from "src/kelas/kelas.service"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { PengajarKelas } from "src/entities/pengajarKelas.entity"; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - MahasiswaKelas, - Kelas, - MataKuliah, - Konfigurasi, - Pengguna, - PengajarKelas, - ]), - AuthModule, - KonfigurasiModule, - KelasModule, - ], - controllers: [NilaiController], - providers: [NilaiService, CustomStrategy, KelasService, KonfigurasiService], -}) -export class NilaiModule {} diff --git a/src/nilai/nilai.service.ts b/src/nilai/nilai.service.ts deleted file mode 100644 index 973251cd9ba63a7be3a5aa0ef6a04903d8f77e54..0000000000000000000000000000000000000000 --- a/src/nilai/nilai.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Kelas } from "src/entities/kelas.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { KonfigurasiService } from "src/konfigurasi/konfigurasi.service"; -import { Brackets, In, Repository } from "typeorm"; -import { GetNilaiByMatkulRespDto, UpdateNilaiRespDto } from "./nilai.dto"; - -@Injectable() -export class NilaiService { - constructor( - @InjectRepository(MahasiswaKelas) - private mhsKelasRepo: Repository<MahasiswaKelas>, - @InjectRepository(Kelas) - private kelasRepo: Repository<Kelas>, - @InjectRepository(MataKuliah) - private mataKuliahRepo: Repository<MataKuliah>, - private konfServ: KonfigurasiService, - ) {} - - private async isMhsKelasOrFail(mhsKelasIds: string[]) { - const periode = await this.konfServ.getPeriodeOrFail(); - - const mhsKelas = await this.mhsKelasRepo.find({ - select: { id: true }, - where: { id: In(mhsKelasIds), kelas: { periode } }, - }); - - for (const mhsKelasId of mhsKelasIds) { - if (!mhsKelas.find((mk) => mk.id === mhsKelasId)) { - throw new Error(`Mahasiswa kelas ${mhsKelasId} tidak ditemukan`); - } - } - } - - async getNilaiByMatkul( - mataKuliahKode: string, - page: number, - limit: number, - search: string, - ) { - const currPeriode = await this.konfServ.getPeriodeOrFail(); - - const baseQuery = this.mataKuliahRepo - .createQueryBuilder("matkul") - .select([ - "matkul.kode AS mata_kuliah_kode", - "matkul.nama AS mata_kuliah_nama", - "kelas.nomor AS kelas_nomor", - "mhsKelas.id AS mahasiswa_kelas_id", - "mahasiswa.id AS mahasiswa_id", - "mahasiswa.nama AS mahasiswa_nama", - "mahasiswa.nim AS mahasiswa_nim", - "mhsKelas.nilaiAkhir AS nilai_akhir", - ]) - .innerJoin("matkul.kelas", "kelas") - .leftJoin("kelas.mahasiswa", "mhsKelas") - .innerJoin("mhsKelas.mahasiswa", "mahasiswa") - .where("kelas.periode = :periode", { periode: currPeriode }) - .andWhere( - new Brackets((qb) => { - qb.where("mahasiswa.nama ILIKE :search", { - search: `%${search}%`, - }).orWhere("mahasiswa.nim ILIKE :search", { search: `%${search}%` }); - }), - ); - - if (mataKuliahKode) { - baseQuery.andWhere("matkul.kode = :kode", { kode: mataKuliahKode }); - } - - const mhsKelas: GetNilaiByMatkulRespDto[] = await baseQuery - .orderBy("matkul.kode") - .addOrderBy("kelas.nomor") - .addOrderBy("mahasiswa.nim") - .skip((page - 1) * limit) - .limit(limit) - .getRawMany(); - - return mhsKelas; - } - - async updateNilai( - mhsKelasIds: string[], - nilaiAkhir?: number, - ): Promise<UpdateNilaiRespDto> { - await this.isMhsKelasOrFail(mhsKelasIds); - - await this.mhsKelasRepo.update( - { id: In(mhsKelasIds) }, - { nilaiAkhir: nilaiAkhir ?? null }, - ); - - return { mahasiswaKelasIds: mhsKelasIds }; - } -} diff --git a/src/submisi-tugas/submisi-tugas.controller.ts b/src/submisi-tugas/submisi-tugas.controller.ts deleted file mode 100644 index 4d48893e7288fc3766880718af9e6b4982d1a1aa..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi-tugas.controller.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { - Body, - Controller, - Get, - Param, - Put, - Query, - Req, - UseGuards, -} from "@nestjs/common"; -import { SubmisiTugasService } from "./submisi-tugas.service"; -import { - ApiBearerAuth, - ApiCookieAuth, - ApiOkResponse, - ApiOperation, - ApiTags, -} from "@nestjs/swagger"; -import { CustomAuthGuard } from "src/middlewares/custom-auth.guard"; -import { RolesGuard } from "src/middlewares/roles.guard"; -import { Roles } from "src/middlewares/roles.decorator"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { - CreateOrUpdateSubmisiTugasDto, - GetSubmisiTugasByIdRespDto, - GetSubmisiTugasByTugasIdQueryDto, - GetSubmisiTugasByTugasIdRespDto, - SubmisiTugasIdDto, -} from "./submisi-tugas.dto"; -import { AuthDto } from "src/auth/auth.dto"; -import { Request } from "express"; - -@ApiTags("Submisi Tugas") -@ApiBearerAuth() -@ApiCookieAuth() -@UseGuards(CustomAuthGuard, RolesGuard) -@Controller("submisi-tugas") -export class SubmisiTugasController { - constructor(private submisiTugasServ: SubmisiTugasService) {} - - @ApiOperation({ - summary: "Create submisi tugas. Roles: S2_MAHASISWA", - }) - @ApiOkResponse({ type: SubmisiTugasIdDto }) - @Roles(RoleEnum.S2_MAHASISWA) - @Put() - async upsertSubmisiTugas( - @Body() dto: CreateOrUpdateSubmisiTugasDto, - @Req() req: Request, - ) { - const { id } = req.user as AuthDto; - - return await this.submisiTugasServ.upsertSubmisiTugas(dto, id); - } - - @ApiOperation({ - summary: - "Get submisi tugas by submisi tugas id. Roles: S2_KULIAH, S2_MAHASISWA", - }) - @ApiOkResponse({ type: GetSubmisiTugasByIdRespDto }) - @Roles(RoleEnum.S2_KULIAH, RoleEnum.S2_MAHASISWA) - @Get("/:id") - async getSubmisiTugasById( - @Req() req: Request, - @Param() param: SubmisiTugasIdDto, - ) { - const { id, roles } = req.user as AuthDto; - - let idMahasiswa = undefined; - let idPengajar = undefined; - - if (!roles.includes(RoleEnum.S2_KULIAH)) { - idMahasiswa = id; - } else { - idPengajar = id; - } - - return await this.submisiTugasServ.getSubmisiTugasById( - param.id, - idMahasiswa, - idPengajar, - ); - } - - @ApiOperation({ - summary: "Get list of submisi tugas summary by tugas id. Roles: S2_KULIAH", - }) - @ApiOkResponse({ type: [GetSubmisiTugasByTugasIdRespDto] }) - @Roles(RoleEnum.S2_KULIAH) - @Get() - async getSubmisiTugasByTugasId( - @Req() req: Request, - @Query() query: GetSubmisiTugasByTugasIdQueryDto, - ) { - const { id } = req.user as AuthDto; - - return await this.submisiTugasServ.getSubmisiTugasByTugasId( - query.tugasId, - id, - query.search || "", - query.page || 1, - query.limit || 10, - query.order || "ASC", - query.isSubmitted, - ); - } -} diff --git a/src/submisi-tugas/submisi-tugas.dto.ts b/src/submisi-tugas/submisi-tugas.dto.ts deleted file mode 100644 index 86bbae65c8d1f21b2502e4679e16de8acf272ea9..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi-tugas.dto.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { IsString } from "@nestjs/class-validator"; -import { - ApiProperty, - ApiPropertyOptional, - OmitType, - PickType, -} from "@nestjs/swagger"; -import { Transform, Type } from "class-transformer"; -import { - IsBoolean, - IsEnum, - IsNumberString, - IsOptional, - IsUUID, - ValidateNested, -} from "class-validator"; -import { BerkasSubmisiTugas } from "src/entities/berkasSubmisiTugas.entity"; -import { PendaftaranTesis } from "src/entities/pendaftaranTesis.entity"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { SubmisiTugas } from "src/entities/submisiTugas.entity"; -import { GetTugasByIdRespDto } from "src/tugas/tugas.dto"; - -class BerkasSubmisiTugasWithoutId extends OmitType(BerkasSubmisiTugas, [ - "id", -] as const) {} - -export class CreateOrUpdateSubmisiTugasDto extends PickType(SubmisiTugas, [ - "jawaban", - "isSubmitted", - "tugasId", -]) { - @ApiProperty({ type: [BerkasSubmisiTugasWithoutId] }) - @ValidateNested({ each: true }) - @Type(() => BerkasSubmisiTugasWithoutId) - berkasSubmisiTugas: BerkasSubmisiTugasWithoutId[]; - - @ApiPropertyOptional({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsOptional() - @IsUUID() - id?: string; -} - -export class SubmisiTugasIdDto extends PickType(SubmisiTugas, [ - "id", -] as const) {} - -export class GetSubmisiTugasByTugasIdQueryDto extends PickType(SubmisiTugas, [ - "tugasId", -] as const) { - @ApiPropertyOptional() - @IsOptional() - @IsString() - search?: string; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 1" }) - page?: number; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 10" }) - limit?: number; - - @IsOptional() - @IsBoolean() - @Transform(({ value }) => value === "true") - @ApiPropertyOptional({ - description: "if not specified, will return all submisi tugas", - }) - isSubmitted?: boolean; - - @ApiPropertyOptional({ - enum: ["ASC", "DESC"], - description: "order by nim. default: ASC", - }) - @IsOptional() - @IsEnum(["ASC", "DESC"]) - order?: "ASC" | "DESC"; -} - -class PickedSubmisiTugas extends PickType(SubmisiTugas, [ - "id", - "isSubmitted", - "berkasSubmisiTugas", -] as const) {} - -export class PickedSubmisiTugasExtended extends PickType(SubmisiTugas, [ - "id", - "isSubmitted", - "jawaban", - "submittedAt", - "berkasSubmisiTugas", -] as const) {} - -class PickedPendaftaranTesis extends PickType(PendaftaranTesis, [ - "id", - "jalurPilihan", - "waktuPengiriman", - "jadwalInterview", - "status", - "topik", -] as const) {} - -class PickedPendaftaran extends PickType(Pengguna, [ - "id", - "nama", - "email", -] as const) { - @ApiProperty({ type: PickedPendaftaranTesis }) - pendaftaranTesis: PickedPendaftaranTesis; -} - -export class GetSubmisiTugasByTugasIdRespDto extends PickType(Pengguna, [ - "id", - "nim", - "nama", -] as const) { - @ApiPropertyOptional({ type: PickedSubmisiTugas }) - submisiTugas?: PickedSubmisiTugas; -} - -export class GetSubmisiTugasByIdRespDto { - @ApiProperty({ type: GetTugasByIdRespDto }) - tugas: GetTugasByIdRespDto; - - @ApiPropertyOptional({ type: PickedPendaftaran }) - pendaftaran?: PickedPendaftaran; - - @ApiProperty({ type: PickedSubmisiTugasExtended }) - submisiTugas: PickedSubmisiTugasExtended; -} diff --git a/src/submisi-tugas/submisi-tugas.module.ts b/src/submisi-tugas/submisi-tugas.module.ts deleted file mode 100644 index 1fd15428a499fe276ed0aeaef1b8168bd532b105..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi-tugas.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Module } from "@nestjs/common"; -import { SubmisiTugasController } from "./submisi-tugas.controller"; -import { SubmisiTugasService } from "./submisi-tugas.service"; -import { CustomStrategy } from "src/middlewares/custom.strategy"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { AuthModule } from "src/auth/auth.module"; -import { KonfigurasiModule } from "src/konfigurasi/konfigurasi.module"; -import { SubmisiTugas } from "src/entities/submisiTugas.entity"; -import { BerkasSubmisiTugas } from "src/entities/berkasSubmisiTugas.entity"; -import { TugasModule } from "src/tugas/tugas.module"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { Tugas } from "src/entities/tugas.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - SubmisiTugas, - BerkasSubmisiTugas, - Pengguna, - Tugas, - MahasiswaKelas, - ]), - AuthModule, - KonfigurasiModule, - TugasModule, - ], - controllers: [SubmisiTugasController], - providers: [SubmisiTugasService, CustomStrategy], -}) -export class SubmisiTugasModule {} diff --git a/src/submisi-tugas/submisi-tugas.service.ts b/src/submisi-tugas/submisi-tugas.service.ts deleted file mode 100644 index 59b4be284e535ec40456da9d3531badb009ea614..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi-tugas.service.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { - BadRequestException, - ForbiddenException, - Injectable, - NotFoundException, -} from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { BerkasSubmisiTugas } from "src/entities/berkasSubmisiTugas.entity"; -import { SubmisiTugas } from "src/entities/submisiTugas.entity"; -import { TugasService } from "src/tugas/tugas.service"; -import { Brackets, Repository } from "typeorm"; -import { - CreateOrUpdateSubmisiTugasDto, - GetSubmisiTugasByIdRespDto, - GetSubmisiTugasByTugasIdRespDto, - SubmisiTugasIdDto, -} from "./submisi-tugas.dto"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { Tugas } from "src/entities/tugas.entity"; -import { RegStatus } from "src/entities/pendaftaranTesis.entity"; -import { KonfigurasiService } from "src/konfigurasi/konfigurasi.service"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import * as dayjs from "dayjs"; - -@Injectable() -export class SubmisiTugasService { - constructor( - @InjectRepository(SubmisiTugas) - private submisiTugasRepo: Repository<SubmisiTugas>, - @InjectRepository(BerkasSubmisiTugas) - private berkasSubmisiTugasRepo: Repository<BerkasSubmisiTugas>, - @InjectRepository(Pengguna) - private penggunaRepo: Repository<Pengguna>, - @InjectRepository(Tugas) - private tugasRepo: Repository<Tugas>, - @InjectRepository(MahasiswaKelas) - private mahasiswaKelasRepo: Repository<MahasiswaKelas>, - private tugasService: TugasService, - private konfService: KonfigurasiService, - ) {} - private async isMahasiswaSubmisiTugasOrFail( - submisiTugasId: string, - mahasiswaId: string, - ) { - const submisiTugas = await this.submisiTugasRepo.findOne({ - where: { id: submisiTugasId }, - }); - - if (!submisiTugas) { - throw new NotFoundException("Submisi tugas tidak ditemukan"); - } - - if (submisiTugas.mahasiswaId !== mahasiswaId) { - throw new ForbiddenException("Anda tidak memiliki akses"); - } - - // validate periode - await this.tugasService.isMahasiswaTugasOrFail( - mahasiswaId, - submisiTugas.tugasId, - ); - } - - private async isPengajarSubmisiTugasOrFail( - submisiTugasId: string, - pengajarId: string, - ) { - const submisiTugas = await this.submisiTugasRepo.findOne({ - where: { id: submisiTugasId }, - relations: ["tugas"], - }); - - if (!submisiTugas) { - throw new NotFoundException("Submisi tugas tidak ditemukan"); - } - - await this.tugasService.isPengajarTugasOrFail( - pengajarId, - submisiTugas.tugas.id, - ); - } - - async upsertSubmisiTugas( - dto: CreateOrUpdateSubmisiTugasDto, - mahasiswaId: string, - ): Promise<SubmisiTugasIdDto> { - await this.tugasService.isMahasiswaTugasOrFail(mahasiswaId, dto.tugasId); - - // const tugas = await this.tugasRepo.findOne({ - // where: { id: dto.tugasId, submisiTugas: { mahasiswaId } }, - // relations: { - // submisiTugas: true, - // }, - // }); - - const tugas = await this.tugasRepo - .createQueryBuilder("tugas") - .leftJoinAndSelect( - "tugas.submisiTugas", - "submisiTugas", - "submisiTugas.mahasiswaId = :mahasiswaId", - { mahasiswaId }, - ) - .where("tugas.id = :id", { id: dto.tugasId }) - .getOne(); - - const mahasiswa = await this.penggunaRepo.findOneBy({ id: mahasiswaId }); - - const berkasSubmisiTugas = dto.berkasSubmisiTugas.map( - (berkasSubmisiTugas) => - this.berkasSubmisiTugasRepo.create(berkasSubmisiTugas), - ); - - // check deadline - if (dayjs(new Date()).isAfter(dayjs(new Date(tugas.waktuSelesai)))) { - throw new ForbiddenException("Tugas sudah melewati deadline"); - } - - if (!dto.id) { - // create - // no duplicate - if (tugas.submisiTugas.length > 0) { - throw new ForbiddenException("Submisi tugas sudah ada"); - } - - const submisiTugas = this.submisiTugasRepo.create({ - ...dto, - mahasiswa, - submittedAt: dto.isSubmitted ? new Date() : null, - tugas, - berkasSubmisiTugas, - }); - - const result = await this.submisiTugasRepo.save(submisiTugas); - - return { id: result.id }; - } else { - // update - // check if submisi tugas exists - if (tugas.submisiTugas.length === 0) { - throw new BadRequestException("Submisi tugas belum ada"); - } - - // check if submisi tugas id is valid - if (tugas.submisiTugas[0].id !== dto.id) { - throw new NotFoundException("Submisi tugas tidak ditemukan"); - } - - // check if submisi tugas is already submitted - if (tugas.submisiTugas[0].isSubmitted) { - throw new ForbiddenException("Submisi tugas sudah dikumpulkan"); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { submisiTugas: _, ...omittedTugas } = tugas; - - const data = { - ...tugas.submisiTugas[0], - ...dto, - berkasSubmisiTugas, - mahasiswa, - tugas: omittedTugas, - submittedAt: dto.isSubmitted ? new Date() : null, - }; - - await this.submisiTugasRepo.save(data); - - return { id: dto.id }; - } - } - - private async getSubmisiTugas(id: string) { - const submisiTugas = await this.submisiTugasRepo.findOne({ - where: { id }, - relations: ["berkasSubmisiTugas"], - }); - - if (!submisiTugas) { - throw new NotFoundException("Submisi tugas tidak ditemukan"); - } - - return submisiTugas; - } - - async getSubmisiTugasById( - id: string, - mahasiswaId?: string, - pengajarId?: string, - ) { - if (mahasiswaId) { - await this.isMahasiswaSubmisiTugasOrFail(id, mahasiswaId); - } - - if (pengajarId) { - await this.isPengajarSubmisiTugasOrFail(id, pengajarId); - } - - const currPeriod = await this.konfService.getPeriodeOrFail(); - const submisiTugas = await this.getSubmisiTugas(id); - - const pendaftaranQuery = this.penggunaRepo - .createQueryBuilder("pengguna") - .select([ - "pengguna.id", - "pengguna.nama", - "pengguna.email", - "pendaftaranTesis.jalurPilihan", - "pendaftaranTesis.waktuPengiriman", - "topik.id", - "topik.judul", - "topik.deskripsi", - ]) - .leftJoinAndSelect("pengguna.pendaftaranTesis", "pendaftaranTesis") - .leftJoinAndSelect("pendaftaranTesis.topik", "topik") - .where("pengguna.id = :id", { id: submisiTugas.mahasiswaId }) - .andWhere("pendaftaranTesis.status = :status", { - status: RegStatus.APPROVED, - }) - .andWhere("topik.periode = :periode", { periode: currPeriod }) - .getOne(); - - const [tugas, pendaftaran] = await Promise.all([ - this.tugasService.getTugasById(submisiTugas.tugasId), - pendaftaranQuery, - ]); - - const result: GetSubmisiTugasByIdRespDto = { - tugas, - submisiTugas, - pendaftaran: { - ...pendaftaran, - pendaftaranTesis: - pendaftaran.pendaftaranTesis.length > 0 - ? pendaftaran.pendaftaranTesis[0] - : undefined, - }, - }; - - return result; - } - - async getSubmisiTugasByTugasId( - tugasId: string, - idPenerima: string, - search: string, - page: number, - limit: number, - order: "ASC" | "DESC", - isSubmitted?: boolean, - ) { - await this.tugasService.isPengajarTugasOrFail(idPenerima, tugasId); - - const baseQuery = this.mahasiswaKelasRepo - .createQueryBuilder("mk") - .innerJoin("mk.kelas", "kelas", "kelas.id = mk.kelasId") - .innerJoinAndSelect("mk.mahasiswa", "mahasiswa") - .leftJoinAndSelect( - "mahasiswa.submisiTugas", - "submisiTugas", - "submisiTugas.tugasId = :tugasId", - { tugasId }, - ) - .leftJoinAndSelect( - "submisiTugas.berkasSubmisiTugas", - "berkasSubmisiTugas", - ) - .select([ - "mk.id", - "mahasiswa.id", - "mahasiswa.nim", - "mahasiswa.nama", - "submisiTugas.id", - "submisiTugas.isSubmitted", - "berkasSubmisiTugas", - ]) - .distinctOn(["mahasiswa.nim"]) - .where( - new Brackets((qb) => { - qb.where("mahasiswa.nama ILIKE :search", { - search: `%${search}%`, - }).orWhere("mahasiswa.nim ILIKE :search", { search: `%${search}%` }); - }), - ) - .orderBy("mahasiswa.nim", order); - - if (isSubmitted !== undefined) { - if (isSubmitted) { - baseQuery.andWhere("submisiTugas.isSubmitted = true"); - } else { - baseQuery.andWhere( - new Brackets((qb) => - qb - .where("submisiTugas.isSubmitted <> true") - .orWhere("submisiTugas.isSubmitted IS NULL"), - ), - ); - } - } - - const submisiTugas = await baseQuery - .limit(limit) - .skip((page - 1) * limit) - .getMany(); - - const mappedResult: GetSubmisiTugasByTugasIdRespDto[] = submisiTugas.map( - (submisi) => ({ - id: submisi.mahasiswa.id, - nim: submisi.mahasiswa.nim, - nama: submisi.mahasiswa.nama, - submisiTugas: - submisi.mahasiswa.submisiTugas.length > 0 - ? submisi.mahasiswa.submisiTugas[0] - : undefined, - }), - ); - - return mappedResult; - } -} diff --git a/src/tugas/tugas.controller.ts b/src/tugas/tugas.controller.ts deleted file mode 100644 index 1e2b396d33bcdebaab6e7f7b79b292b9022f7224..0000000000000000000000000000000000000000 --- a/src/tugas/tugas.controller.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { - Body, - Controller, - Get, - Param, - Post, - Put, - Query, - Req, - UseGuards, -} from "@nestjs/common"; -import { TugasService } from "./tugas.service"; -import { Roles } from "src/middlewares/roles.decorator"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { - ApiBearerAuth, - ApiCookieAuth, - ApiCreatedResponse, - ApiOkResponse, - ApiOperation, - ApiTags, -} from "@nestjs/swagger"; -import { CustomAuthGuard } from "src/middlewares/custom-auth.guard"; -import { RolesGuard } from "src/middlewares/roles.guard"; -import { - TugasIdDto, - CreateTugasDto, - UpdateTugasDto, - GetTugasByIdRespDto, - GetTugasByKelasIdQueryDto, - GetTugasByKelasIdRespDto, - GetTugasByMahasiswaIdQueryDto, - GetDaftarTugasByMahasiswaIdRespDto, -} from "./tugas.dto"; -import { Request } from "express"; -import { AuthDto } from "src/auth/auth.dto"; -import { PickedSubmisiTugasExtended } from "src/submisi-tugas/submisi-tugas.dto"; - -@ApiCookieAuth() -@ApiBearerAuth() -@ApiTags("Tugas") -@UseGuards(CustomAuthGuard, RolesGuard) -@Controller("tugas") -export class TugasController { - constructor(private readonly tugasService: TugasService) {} - - @ApiOperation({ summary: "Create Tugas. Roles: S2_KULIAH" }) - @ApiCreatedResponse({ type: TugasIdDto }) - @Roles(RoleEnum.S2_KULIAH) - @Post() - async createTugas(@Body() createDto: CreateTugasDto, @Req() req: Request) { - const { id } = req.user as AuthDto; - - return await this.tugasService.createTugas(createDto, id); - } - - @ApiOperation({ summary: "Update Tugas. Roles: S2_KULIAH" }) - @ApiOkResponse({ type: TugasIdDto }) - @Roles(RoleEnum.S2_KULIAH) - @Put() - async updateTugas(@Body() updateDto: UpdateTugasDto, @Req() req: Request) { - const { id } = req.user as AuthDto; - - return await this.tugasService.updateTugasById(updateDto, id); - } - - @ApiOperation({ - summary: "Get Tugas by id. Roles: S2_KULIAH, S2_MAHASISWA", - }) - @ApiOkResponse({ type: GetTugasByIdRespDto }) - @Roles(RoleEnum.S2_KULIAH, RoleEnum.S2_MAHASISWA) - @Get("/:id") - async getTugasById(@Param() param: TugasIdDto, @Req() req: Request) { - let idPengajar = undefined; - let idMahasiswa = undefined; - - const { id, roles } = req.user as AuthDto; - - if (!roles.includes(RoleEnum.S2_KULIAH)) { - idMahasiswa = id; - } else { - idPengajar = id; - } - - return this.tugasService.getTugasById(param.id, idMahasiswa, idPengajar); - } - - @ApiOperation({ - summary: "Get Tugas list by kelas id. Roles: S2_KULIAH", - }) - @ApiOkResponse({ type: GetTugasByKelasIdRespDto }) - @Roles(RoleEnum.S2_KULIAH) - @Get() - async getTugasByKelasId( - @Req() req: Request, - @Query() query: GetTugasByKelasIdQueryDto, - ) { - const { id } = req.user as AuthDto; - - return this.tugasService.getTugasByKelasId( - query.kelasId, - id, - query.search || "", - query.page || 1, - query.limit || 10, - ); - } - - @ApiOperation({ - summary: - "Get a specific submisi tugas by mahasiswa ID and tugas ID. Roles: S2_MAHASISWA", - }) - @ApiOkResponse({ type: PickedSubmisiTugasExtended }) - @Roles(RoleEnum.S2_MAHASISWA) - @Get("/:id/submisi-tugas") - async getSubmisiTugasByMahasiswaAndTugasId( - @Req() req: Request, - @Param() param: TugasIdDto, - ) { - const { id: mahasiswaId } = req.user as AuthDto; - - return await this.tugasService.getSubmisiTugasByMahasiswaAndTugasId( - mahasiswaId, - param.id, - ); - } - - @ApiOperation({ - summary: "Get Tugas list by mahasiswa Id. Roles: S2_MAHASISWA", - }) - @ApiOkResponse({ type: [GetDaftarTugasByMahasiswaIdRespDto] }) - @Roles(RoleEnum.S2_MAHASISWA) - @Get("/-/daftar-tugas") - async getTugasByMahasiswaId( - @Query() query: GetTugasByMahasiswaIdQueryDto, - @Req() req: Request, - ) { - const { id } = req.user as AuthDto; - - return this.tugasService.getDaftarTugasByMahasiswa( - id, - query.search || "", - query.page || 1, - query.limit || 10, - query.isSubmitted || undefined, - ); - } -} diff --git a/src/tugas/tugas.dto.ts b/src/tugas/tugas.dto.ts deleted file mode 100644 index 1bf372b3ce03a32ce1e16c50e39d08af6313c921..0000000000000000000000000000000000000000 --- a/src/tugas/tugas.dto.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - ApiProperty, - ApiPropertyOptional, - OmitType, - PickType, -} from "@nestjs/swagger"; -import { Transform, Type } from "class-transformer"; -import { - IsBoolean, - IsNumberString, - IsOptional, - IsString, - IsUUID, - ValidateNested, -} from "class-validator"; -import { BerkasTugas } from "src/entities/berkasTugas.entity"; -import { Kelas } from "src/entities/kelas.entity"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { Tugas } from "src/entities/tugas.entity"; -import { GetKelasRespDto } from "src/kelas/kelas.dto"; - -class BerkasTugasWithoutId extends OmitType(BerkasTugas, ["id"] as const) {} - -export class CreateTugasDto extends PickType(Tugas, [ - "judul", - "waktuMulai", - "waktuSelesai", - "deskripsi", - "kelasId", -]) { - @ApiProperty({ type: [BerkasTugasWithoutId] }) - @ValidateNested({ each: true }) - @Type(() => BerkasTugasWithoutId) - berkasTugas: BerkasTugasWithoutId[]; -} - -export class UpdateTugasDto extends OmitType(CreateTugasDto, [ - "kelasId", -] as const) { - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - @IsUUID() - id: string; -} - -export class TugasIdDto extends PickType(Tugas, ["id"] as const) {} - -class PickedPengajarKelas extends PickType(Pengguna, ["id", "nama"] as const) {} - -class PickedTugasKelas extends PickType(Kelas, [ - "id", - "nomor", - "mataKuliah", -] as const) {} - -export class GetTugasByIdRespDto extends PickType(Tugas, [ - "id", - "judul", - "waktuMulai", - "waktuSelesai", - "deskripsi", - "createdAt", - "updatedAt", - "berkasTugas", -] as const) { - @ApiProperty({ type: PickedPengajarKelas }) - pembuat: PickedPengajarKelas; - - @ApiProperty({ type: PickedPengajarKelas }) - pengubah: PickedPengajarKelas; - - @ApiProperty({ type: PickedTugasKelas }) - kelas: PickedTugasKelas; -} - -export class GetTugasByKelasIdQueryDto extends PickType(Tugas, [ - "kelasId", -] as const) { - @ApiPropertyOptional() - @IsOptional() - @IsString() - search?: string; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 1" }) - page?: number; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 10" }) - limit?: number; -} - -export class GetTugasByMahasiswaIdQueryDto { - @ApiPropertyOptional() - @IsOptional() - @IsString() - search?: string; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 1" }) - page?: number; - - @IsOptional() - @IsNumberString() - @ApiPropertyOptional({ description: "default: 10" }) - limit?: number; - - @IsOptional() - @IsBoolean() - @Transform(({ value }) => value === "true") - @ApiPropertyOptional({ - description: "if not specified, will return all submisi tugas", - }) - isSubmitted?: boolean; -} - -export class GetTugasSummaryRespDto extends PickType(Tugas, [ - "id", - "judul", - "waktuMulai", - "waktuSelesai", -] as const) { - @ApiProperty() - totalSubmisi: number; -} - -export class GetTugasByKelasIdRespDto { - @ApiProperty({ type: [GetTugasSummaryRespDto] }) - tugas: GetTugasSummaryRespDto[]; - - @ApiProperty({ type: GetKelasRespDto }) - kelas: GetKelasRespDto; -} - -export class GetDaftarTugasByMahasiswaIdRespDto { - @ApiProperty({ example: "IF4031" }) - kodeMataKuliah: string; - - @ApiProperty({ example: "Pengembangan Aplikasi" }) - namaMataKuliah: string; - - @ApiProperty({ example: "550e8400-e29b-41d4-a716-446655440000" }) - kelasId: string; - - @ApiProperty({ - example: "550e8400-e29b-41d4-a716-446655440000", - description: "tugas id", - }) - id: string; - - @ApiProperty() - judul: string; - - @ApiProperty() - waktuMulai: Date; - - @ApiProperty() - waktuSelesai: Date; - - @ApiPropertyOptional({ example: "550e8400-e29b-41d4-a716-446655440000" }) - submisiTugasId: string; - - @ApiPropertyOptional() - isSubmitted: boolean; -} diff --git a/src/tugas/tugas.module.ts b/src/tugas/tugas.module.ts deleted file mode 100644 index 3a8d49258c546ffaa9d9d95014f4d10af9190442..0000000000000000000000000000000000000000 --- a/src/tugas/tugas.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Module } from "@nestjs/common"; -import { TugasController } from "./tugas.controller"; -import { TugasService } from "./tugas.service"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { PengajarKelas } from "src/entities/pengajarKelas.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import { Tugas } from "src/entities/tugas.entity"; -import { SubmisiTugas } from "src/entities/submisiTugas.entity"; -import { AuthModule } from "src/auth/auth.module"; -import { KonfigurasiModule } from "src/konfigurasi/konfigurasi.module"; -import { CustomStrategy } from "src/middlewares/custom.strategy"; -import { BerkasTugas } from "src/entities/berkasTugas.entity"; -import { BerkasSubmisiTugas } from "src/entities/berkasSubmisiTugas.entity"; -import { Kelas } from "src/entities/kelas.entity"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { KelasModule } from "src/kelas/kelas.module"; -import { KelasService } from "src/kelas/kelas.service"; -import { MataKuliah } from "src/entities/mataKuliah.entity"; -import { KonfigurasiService } from "src/konfigurasi/konfigurasi.service"; -import { Konfigurasi } from "src/entities/konfigurasi.entity"; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - PengajarKelas, - MahasiswaKelas, - Tugas, - SubmisiTugas, - BerkasTugas, - BerkasSubmisiTugas, - Kelas, - Pengguna, - MataKuliah, - Konfigurasi, - ]), - AuthModule, - KonfigurasiModule, - KelasModule, - ], - controllers: [TugasController], - providers: [TugasService, CustomStrategy, KelasService, KonfigurasiService], - exports: [TugasService], -}) -export class TugasModule {} diff --git a/src/tugas/tugas.service.ts b/src/tugas/tugas.service.ts deleted file mode 100644 index 0e33561b73063e615f4229787ec4b9e8f92730d0..0000000000000000000000000000000000000000 --- a/src/tugas/tugas.service.ts +++ /dev/null @@ -1,383 +0,0 @@ -import { - BadRequestException, - ForbiddenException, - Injectable, - NotFoundException, -} from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Tugas } from "src/entities/tugas.entity"; -import { Brackets, Repository } from "typeorm"; -import { - CreateTugasDto, - GetDaftarTugasByMahasiswaIdRespDto, - GetTugasByIdRespDto, - GetTugasByKelasIdRespDto, - GetTugasSummaryRespDto, - TugasIdDto, - UpdateTugasDto, -} from "./tugas.dto"; -import { BerkasTugas } from "src/entities/berkasTugas.entity"; -import { PengajarKelas } from "src/entities/pengajarKelas.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas.entity"; -import { Kelas } from "src/entities/kelas.entity"; -import { Pengguna } from "src/entities/pengguna.entity"; -import { KelasService } from "src/kelas/kelas.service"; -import { KonfigurasiService } from "src/konfigurasi/konfigurasi.service"; -import * as dayjs from "dayjs"; -import { SubmisiTugas } from "src/entities/submisiTugas.entity"; -import { PickedSubmisiTugasExtended } from "src/submisi-tugas/submisi-tugas.dto"; - -@Injectable() -export class TugasService { - constructor( - @InjectRepository(Tugas) private tugasRepo: Repository<Tugas>, - @InjectRepository(BerkasTugas) - private berkasTugasRepo: Repository<BerkasTugas>, - @InjectRepository(SubmisiTugas) - private submisiTugasRepo: Repository<SubmisiTugas>, - @InjectRepository(PengajarKelas) - private doskelRepo: Repository<PengajarKelas>, - @InjectRepository(MahasiswaKelas) - private mahasiswaKelasRepo: Repository<MahasiswaKelas>, - @InjectRepository(Kelas) - private kelasRepo: Repository<Kelas>, - @InjectRepository(Pengguna) - private penggunaRepo: Repository<Pengguna>, - private kelasService: KelasService, - private konfService: KonfigurasiService, - ) {} - - private async isPengajarKelasOrFail(pengajarId: string, kelasId: string) { - const periode = await this.konfService.getPeriodeOrFail(); - - const doskel = await this.doskelRepo.findOne({ - where: { - pengajarId, - kelasId, - kelas: { - periode, - }, - }, - relations: ["kelas"], - }); - - if (!doskel) { - throw new ForbiddenException("Anda tidak memiliki akses"); - } - } - - private async isMahasiswaKelasOrFail(mahasiswaId: string, kelasId: string) { - const periode = await this.konfService.getPeriodeOrFail(); - - const mahasiswaKelas = await this.mahasiswaKelasRepo.findOne({ - where: { - mahasiswaId, - kelasId, - kelas: { - periode, - }, - }, - relations: ["kelas"], - }); - - if (!mahasiswaKelas) { - throw new ForbiddenException("Anda tidak memiliki akses"); - } - } - - async isPengajarTugasOrFail(pengajarId: string, tugasId: string) { - const tugas = await this.tugasRepo.findOne({ - where: { id: tugasId }, - }); - - if (!tugas) { - throw new NotFoundException("Tugas tidak ditemukan"); - } - - await this.isPengajarKelasOrFail(pengajarId, tugas.kelasId); - } - - async isMahasiswaTugasOrFail(mahasiswaId: string, tugasId: string) { - const tugas = await this.tugasRepo.findOne({ - where: { id: tugasId }, - }); - - if (!tugas) { - throw new NotFoundException("Tugas tidak ditemukan"); - } - - return await this.isMahasiswaKelasOrFail(mahasiswaId, tugas.kelasId); - } - - private async getTugas(tugasId: string): Promise<GetTugasByIdRespDto> { - const result: GetTugasByIdRespDto[] = await this.tugasRepo - .createQueryBuilder("tugas") - .leftJoinAndSelect("tugas.pembuat", "pembuat") - .leftJoinAndSelect("tugas.pengubah", "pengubah") - .leftJoinAndSelect("tugas.kelas", "kelas") - .leftJoinAndSelect("kelas.mataKuliah", "mataKuliah") - .leftJoinAndSelect("tugas.berkasTugas", "berkasTugas") - .select([ - "tugas.id", - "pembuat.id", - "pembuat.nama", - "pengubah.id", - "pengubah.nama", - "tugas.judul", - "tugas.waktuMulai", - "tugas.waktuSelesai", - "tugas.deskripsi", - "tugas.createdAt", - "tugas.updatedAt", - "berkasTugas", - "kelas.id", - "kelas.nomor", - "mataKuliah.kode", - "mataKuliah.nama", - ]) - .where("tugas.id = :tugasId", { tugasId }) - .getMany(); - - return result[0]; - } - - async createTugas( - createDto: CreateTugasDto, - pembuatId: string, - ): Promise<TugasIdDto> { - await this.isPengajarKelasOrFail(pembuatId, createDto.kelasId); - - if (dayjs(createDto.waktuMulai).isAfter(dayjs(createDto.waktuSelesai))) { - throw new BadRequestException( - "Waktu mulai tidak boleh setelah waktu selesai", - ); - } - - const kelas = await this.kelasRepo.findOne({ - where: { id: createDto.kelasId }, - }); - - const pembuat = await this.penggunaRepo.findOne({ - where: { id: pembuatId }, - }); - - const berkasTugas = createDto.berkasTugas.map((berkas) => - this.berkasTugasRepo.create(berkas), - ); - - const tugas = this.tugasRepo.create({ - ...createDto, - kelas, - pembuat, - pengubah: pembuat, - berkasTugas, - }); - - const result = await this.tugasRepo.save(tugas); - - return { id: result.id }; - } - - async updateTugasById( - updateDto: UpdateTugasDto, - pengubahId: string, - ): Promise<TugasIdDto> { - await this.isPengajarTugasOrFail(pengubahId, updateDto.id); - - if (dayjs(updateDto.waktuMulai).isAfter(dayjs(updateDto.waktuSelesai))) { - throw new BadRequestException( - "Waktu mulai tidak boleh setelah waktu selesai", - ); - } - - const berkasTugas = updateDto.berkasTugas.map((berkas) => - this.berkasTugasRepo.create(berkas), - ); - - const pengubah = await this.penggunaRepo.findOne({ - where: { id: pengubahId }, - }); - - const prevTugas = await this.tugasRepo.findOne({ - where: { id: updateDto.id }, - relations: ["kelas", "pembuat"], - }); - - const data = { - ...updateDto, - updatedAt: new Date(), - pengubah, - kelas: prevTugas.kelas, - pembuat: prevTugas.pembuat, - berkasTugas, - }; - - await this.tugasRepo.save(data); - - return { id: updateDto.id }; - } - - async getTugasById(id: string, idMahasiswa?: string, idPengajar?: string) { - if (idMahasiswa) { - await this.isMahasiswaTugasOrFail(idMahasiswa, id); - } - - if (idPengajar) { - await this.isPengajarTugasOrFail(idPengajar, id); - } - - const result = await this.getTugas(id); - - return result; - } - - async getTugasByKelasId( - kelasId: string, - idPengajar: string, - search: string, - page: number, - limit: number, - ): Promise<GetTugasByKelasIdRespDto> { - await this.isPengajarKelasOrFail(idPengajar, kelasId); - - const kelasQuery = this.kelasService.getById(kelasId); - const tugasQuery = this.tugasRepo - .createQueryBuilder("tugas") - .leftJoinAndSelect( - "tugas.submisiTugas", - "submisi_tugas", - "submisi_tugas.isSubmitted = true", - ) - .select([ - "tugas.id AS id", - "tugas.judul AS judul", - "tugas.waktuMulai AS waktu_mulai", - "tugas.waktuSelesai AS waktu_selesai", - "COUNT(submisi_tugas) AS total_submisi", - ]) - .where("tugas.kelasId = :kelasId", { - kelasId, - }) - .andWhere("tugas.judul ILIKE :search", { search: `%${search}%` }) - .groupBy("tugas.id") - .orderBy("tugas.createdAt", "DESC") - .limit(limit) - .skip((page - 1) * limit) - .getRawMany(); - - const [kelas, tugas] = await Promise.all([kelasQuery, tugasQuery]); - const mappedTugas: GetTugasSummaryRespDto[] = tugas.map((tugas) => ({ - id: tugas.id, - judul: tugas.judul, - waktuMulai: tugas.waktu_mulai, - waktuSelesai: tugas.waktu_selesai, - totalSubmisi: parseInt(tugas.total_submisi), - })); - - return { kelas, tugas: mappedTugas }; - } - - async getSubmisiTugasByMahasiswaAndTugasId( - mahasiswaId: string, - tugasId: string, - ): Promise<PickedSubmisiTugasExtended> { - await this.isMahasiswaTugasOrFail(mahasiswaId, tugasId); - - const submisiTugas = await this.submisiTugasRepo.findOne({ - select: [ - "id", - "isSubmitted", - "jawaban", - "submittedAt", - "berkasSubmisiTugas", - ], - where: { - mahasiswaId: mahasiswaId, - tugasId: tugasId, - }, - relations: ["berkasSubmisiTugas"], - }); - - if (!submisiTugas) { - throw new NotFoundException( - `Submisi tugas tidak ditemukan untuk mahasiswa ID: ${mahasiswaId} dan tugas ID: ${tugasId}`, - ); - } - - return submisiTugas; - } - - async getDaftarTugasByMahasiswa( - mahasiswaId: string, - search: string, - page: number, - limit: number, - isSubmitted?: boolean, - ): Promise<GetDaftarTugasByMahasiswaIdRespDto[]> { - const baseQuery = this.mahasiswaKelasRepo - .createQueryBuilder("mk") - .innerJoin("mk.kelas", "kelas", "kelas.id = mk.kelasId") - .innerJoin("kelas.mataKuliah", "mataKuliah") - .innerJoin("kelas.tugas", "tugas") - .leftJoinAndSelect( - "tugas.submisiTugas", - "submisiTugas", - "submisiTugas.mahasiswaId = :mahasiswaId", - { mahasiswaId }, - ) - .select([ - "mk.id", - "kelas.id AS kelas_id", - "mataKuliah.kode AS kode_mata_kuliah", - "mataKuliah.nama AS nama_mata_kuliah", - "tugas.id AS tugas_id", - "tugas.judul AS tugas_judul", - "tugas.waktuMulai AS tugas_waktu_mulai", - "tugas.waktuSelesai AS tugas_waktu_selesai", - "submisiTugas.id AS submisi_tugas_id", - "submisiTugas.isSubmitted AS submisi_tugas_is_submitted", - ]) - .where("mk.mahasiswaId = :mahasiswaId", { - mahasiswaId, - }) - .andWhere("tugas.judul ILIKE :search", { search: `%${search}%` }) - .orderBy("tugas.createdAt", "DESC"); - - if (isSubmitted !== undefined) { - if (isSubmitted) { - baseQuery.andWhere("submisiTugas.isSubmitted = true"); - } else { - baseQuery.andWhere( - new Brackets((qb) => - qb - .where("submisiTugas.isSubmitted <> true") - .orWhere("submisiTugas.isSubmitted IS NULL"), - ), - ); - } - } - - const daftarTugas = await baseQuery - .limit(limit) - .skip((page - 1) * limit) - .getRawMany(); - - const mappedDaftarTugas: GetDaftarTugasByMahasiswaIdRespDto[] = - daftarTugas.map((tugas) => ({ - kodeMataKuliah: tugas.kode_mata_kuliah, - namaMataKuliah: tugas.nama_mata_kuliah, - kelasId: tugas.kelas_id, - id: tugas.tugas_id, - judul: tugas.tugas_judul, - waktuMulai: tugas.tugas_waktu_mulai, - waktuSelesai: tugas.tugas_waktu_selesai, - submisiTugasId: tugas.submisi_tugas_id || undefined, - isSubmitted: - tugas.submisi_tugas_is_submitted === null - ? undefined - : tugas.submisi_tugas_is_submitted, - })); - - return mappedDaftarTugas; - } -}