diff --git a/src/dashboard/dashboard.controller.ts b/src/dashboard/dashboard.controller.ts
index 31a3a4cc1dc88afe12ba4b05e16bc015fd2a41e9..5631e9daa9da0d30a886abbb1e631faf3e7b03f9 100644
--- a/src/dashboard/dashboard.controller.ts
+++ b/src/dashboard/dashboard.controller.ts
@@ -1,23 +1,25 @@
 import { Controller, Get, Query, Req, UseGuards } from "@nestjs/common";
-import { DashboardService } from "./dashboard.service";
-import { CustomAuthGuard } from "src/middlewares/custom-auth.guard";
-import { RolesGuard } from "src/middlewares/roles.guard";
+import {
+  ApiBearerAuth,
+  ApiCookieAuth,
+  ApiOkResponse,
+  ApiTags,
+} from "@nestjs/swagger";
+import { Request } from "express";
+import { AuthDto } from "src/auth/auth.dto";
 import { RoleEnum } from "src/entities/pengguna.entity";
+import { CustomAuthGuard } from "src/middlewares/custom-auth.guard";
 import { Roles } from "src/middlewares/roles.decorator";
-import { AuthDto } from "src/auth/auth.dto";
-import { Request } from "express";
+import { RolesGuard } from "src/middlewares/roles.guard";
 import {
   DashboardDto,
   DashboardMahasiswaResDto,
   GetDashboardDosbimQueryDto,
+  GetDashboardTimTesisReqQueryDto,
+  GetDashboardTimTesisRespDto,
   JalurStatisticDto,
 } from "./dashboard.dto";
-import {
-  ApiBearerAuth,
-  ApiCookieAuth,
-  ApiOkResponse,
-  ApiTags,
-} from "@nestjs/swagger";
+import { DashboardService } from "./dashboard.service";
 
 @ApiTags("Dashboard")
 @ApiCookieAuth()
@@ -60,4 +62,14 @@ export class DashboardController {
       (request.user as AuthDto).id,
     );
   }
+
+  @UseGuards(CustomAuthGuard, RolesGuard)
+  @Roles(RoleEnum.S2_TIM_TESIS, RoleEnum.ADMIN)
+  @ApiOkResponse({ type: GetDashboardTimTesisRespDto })
+  @Get("/tim-tesis")
+  async getDashboardTimTesis(
+    @Query() query: GetDashboardTimTesisReqQueryDto,
+  ): Promise<GetDashboardTimTesisRespDto> {
+    return this.dashboardService.getDashboardTimTesis(query);
+  }
 }
diff --git a/src/dashboard/dashboard.dto.ts b/src/dashboard/dashboard.dto.ts
index 8d279937d0616eb007400145e622316c5d4b9012..d49ba0fd308a77aa3f0ea54ef0de78032f1ce200 100644
--- a/src/dashboard/dashboard.dto.ts
+++ b/src/dashboard/dashboard.dto.ts
@@ -4,16 +4,15 @@ import {
   OmitType,
   PickType,
 } from "@nestjs/swagger";
+import { IsNumberString, IsOptional, IsString } from "class-validator";
+import { Bimbingan, BimbinganStatus } from "src/entities/bimbingan.entity";
+import { PendaftaranSidsem } from "src/entities/pendaftaranSidsem";
+import { Pengguna } from "src/entities/pengguna.entity";
+import { Topik } from "src/entities/topik.entity";
 import {
   JalurEnum,
   PendaftaranTesis,
 } from "../entities/pendaftaranTesis.entity";
-import { Topik } from "src/entities/topik.entity";
-import { Pengguna } from "src/entities/pengguna.entity";
-import { Bimbingan } from "src/entities/bimbingan.entity";
-import { PendaftaranSidsem } from "src/entities/pendaftaranSidsem";
-import { IsOptional } from "class-validator";
-import { BimbinganStatus } from "src/entities/bimbingan.entity";
 
 class PickedTopikDashboard extends PickType(Topik, ["id", "judul"] as const) {}
 class PickedMhsDashboard extends PickType(Pengguna, [
@@ -114,3 +113,49 @@ export class GetDashboardDosbimQueryDto {
   @IsOptional()
   search: string;
 }
+
+export class GetDashboardTimTesisReqQueryDto {
+  @IsOptional()
+  @IsNumberString()
+  @ApiPropertyOptional({ description: "default: 1" })
+  page?: number;
+
+  @IsOptional()
+  @IsNumberString()
+  @ApiPropertyOptional({ description: "default: no limit" })
+  limit?: number;
+
+  @IsOptional()
+  @IsString()
+  @ApiPropertyOptional()
+  search?: string;
+}
+
+export enum DashboardTimTesisStatusEnum {
+  PENGAJUAN_TOPIK = "PENGAJUAN_TOPIK",
+  SEMINAR_1 = "SEMINAR_1",
+  SEMINAR_2 = "SEMINAR_2",
+  SIDANG = "SIDANG",
+}
+
+class GetDashboardTimTesisDataDto {
+  @ApiProperty()
+  nim_mahasiswa: string;
+
+  @ApiProperty()
+  nama_mahasiswa: string;
+
+  @ApiProperty({ isArray: true })
+  dosen_pembimbing: string[];
+
+  @ApiProperty({ isArray: true, enum: DashboardTimTesisStatusEnum })
+  status: DashboardTimTesisStatusEnum[];
+}
+
+export class GetDashboardTimTesisRespDto {
+  @ApiProperty()
+  maxPage: number;
+
+  @ApiProperty({ type: GetDashboardTimTesisDataDto })
+  data: GetDashboardTimTesisDataDto[];
+}
diff --git a/src/dashboard/dashboard.service.ts b/src/dashboard/dashboard.service.ts
index c2bfcb12e4e7c8bbcbfdfacf6b77a224c475a5b1..a4360e8f1010086dffb050df8315bc57fa92ce2c 100644
--- a/src/dashboard/dashboard.service.ts
+++ b/src/dashboard/dashboard.service.ts
@@ -1,25 +1,28 @@
 import { BadRequestException, Injectable } from "@nestjs/common";
 import { InjectRepository } from "@nestjs/typeorm";
-import { Brackets, Repository } from "typeorm";
+import { BimbinganService } from "src/bimbingan/bimbingan.service";
+import { Bimbingan } from "src/entities/bimbingan.entity";
+import { DosenBimbingan } from "src/entities/dosenBimbingan.entity";
+import { Konfigurasi } from "src/entities/konfigurasi.entity";
+import {
+  PendaftaranSidsem,
+  TipeSidsemEnum,
+} from "src/entities/pendaftaranSidsem";
+import { ArrayContains, Brackets, In, Like, Repository } from "typeorm";
 import {
   PendaftaranTesis,
   RegStatus,
 } from "../entities/pendaftaranTesis.entity";
-import { Pengguna } from "../entities/pengguna.entity";
-import { Konfigurasi } from "src/entities/konfigurasi.entity";
-import { Bimbingan } from "src/entities/bimbingan.entity";
+import { Pengguna, RoleEnum } from "../entities/pengguna.entity";
 import {
   DashboardDto,
   DashboardMahasiswaResDto,
+  DashboardTimTesisStatusEnum,
+  GetDashboardTimTesisReqQueryDto,
+  GetDashboardTimTesisRespDto,
   JalurStatisticDto,
   NoNIMUserDashboard,
 } from "./dashboard.dto";
-import {
-  PendaftaranSidsem,
-  TipeSidsemEnum,
-} from "src/entities/pendaftaranSidsem";
-import { DosenBimbingan } from "src/entities/dosenBimbingan.entity";
-import { BimbinganService } from "src/bimbingan/bimbingan.service";
 
 @Injectable()
 export class DashboardService {
@@ -270,4 +273,172 @@ export class DashboardService {
       },
     };
   }
+
+  async getDashboardTimTesis(
+    query: GetDashboardTimTesisReqQueryDto,
+  ): Promise<GetDashboardTimTesisRespDto> {
+    const [foundMahasiswa, total] = await this.penggunaRepository.findAndCount({
+      select: {
+        id: true,
+        nama: true,
+        nim: true,
+      },
+      take: query.limit || undefined,
+      skip: (query.page - 1) * query.limit || undefined,
+      where: [
+        {
+          nim: Like(`%${query.search ?? ""}%`),
+          roles: ArrayContains([RoleEnum.S2_MAHASISWA]),
+        },
+        {
+          nama: Like(`%${query.search ?? ""}%`),
+          roles: ArrayContains([RoleEnum.S2_MAHASISWA]),
+        },
+      ],
+      order: {
+        nim: "ASC",
+      },
+    });
+
+    const dosbimQuery = this.dosenBimbinganRepository.find({
+      select: {
+        pendaftaran: {
+          id: true,
+          mahasiswaId: true,
+        },
+        dosen: {
+          id: true,
+          nama: true,
+        },
+      },
+      relations: {
+        pendaftaran: true,
+        dosen: true,
+      },
+      where: {
+        pendaftaran: {
+          mahasiswaId: In(foundMahasiswa.map(({ id }) => id)),
+        },
+      },
+    });
+
+    const topicAcceptedQuery = this.pendaftaranTesisRepository.find({
+      select: {
+        id: true,
+        mahasiswaId: true,
+      },
+      where: {
+        status: RegStatus.APPROVED,
+        mahasiswaId: In(foundMahasiswa.map(({ id }) => id)),
+      },
+    });
+
+    const mhsSemProAcceptedQuery = this.pendaftaranSidsemRepository.find({
+      select: {
+        pendaftaranTesis: {
+          mahasiswaId: true,
+        },
+      },
+      relations: {
+        pendaftaranTesis: true,
+      },
+      where: {
+        tipe: TipeSidsemEnum.SEMINAR_1,
+        ditolak: false,
+        pendaftaranTesis: {
+          mahasiswaId: In(foundMahasiswa.map(({ id }) => id)),
+        },
+      },
+    });
+
+    const mhsSemTesAcceptedQuery = this.pendaftaranSidsemRepository.find({
+      select: {
+        pendaftaranTesis: {
+          mahasiswaId: true,
+        },
+      },
+      relations: {
+        pendaftaranTesis: true,
+      },
+      where: {
+        tipe: TipeSidsemEnum.SEMINAR_2,
+        ditolak: false,
+        pendaftaranTesis: {
+          mahasiswaId: In(foundMahasiswa.map(({ id }) => id)),
+        },
+      },
+    });
+
+    const mhsSidangAcceptedQuery = this.pendaftaranSidsemRepository.find({
+      select: {
+        pendaftaranTesis: {
+          mahasiswaId: true,
+        },
+      },
+      relations: {
+        pendaftaranTesis: true,
+      },
+      where: {
+        tipe: TipeSidsemEnum.SIDANG,
+        ditolak: false,
+        pendaftaranTesis: {
+          mahasiswaId: In(foundMahasiswa.map(({ id }) => id)),
+        },
+      },
+    });
+
+    const [
+      foundDosbim,
+      topicAccepted,
+      mhsSemProAccepted,
+      mhsSemTesAccepted,
+      mhsSidangAccepted,
+    ] = await Promise.all([
+      dosbimQuery,
+      topicAcceptedQuery,
+      mhsSemProAcceptedQuery,
+      mhsSemTesAcceptedQuery,
+      mhsSidangAcceptedQuery,
+    ]);
+
+    const mhsStatusMap: Record<string, DashboardTimTesisStatusEnum[]> = {};
+    const dosbimMap: Record<string, string[]> = {};
+
+    foundMahasiswa.forEach(({ id }) => {
+      mhsStatusMap[id] = [];
+      dosbimMap[id] = [];
+    });
+
+    foundDosbim.forEach(({ pendaftaran: { mahasiswaId }, dosen: { nama } }) => {
+      dosbimMap[mahasiswaId].push(nama);
+    });
+
+    topicAccepted.forEach(({ mahasiswaId }) => {
+      mhsStatusMap[mahasiswaId].push(
+        DashboardTimTesisStatusEnum.PENGAJUAN_TOPIK,
+      );
+    });
+
+    mhsSemProAccepted.forEach(({ pendaftaranTesis: { mahasiswaId } }) => {
+      mhsStatusMap[mahasiswaId].push(DashboardTimTesisStatusEnum.SEMINAR_1);
+    });
+
+    mhsSemTesAccepted.forEach(({ pendaftaranTesis: { mahasiswaId } }) => {
+      mhsStatusMap[mahasiswaId].push(DashboardTimTesisStatusEnum.SEMINAR_2);
+    });
+
+    mhsSidangAccepted.forEach(({ pendaftaranTesis: { mahasiswaId } }) => {
+      mhsStatusMap[mahasiswaId].push(DashboardTimTesisStatusEnum.SEMINAR_2);
+    });
+
+    return {
+      maxPage: !!query.limit ? Math.ceil(total / query.limit) : 1,
+      data: foundMahasiswa.map(({ nim, id, nama }) => ({
+        nim_mahasiswa: nim,
+        nama_mahasiswa: nama,
+        status: mhsStatusMap[id] ?? [],
+        dosen_pembimbing: dosbimMap[id] ?? [],
+      })),
+    };
+  }
 }
diff --git a/src/entities/topik.entity.ts b/src/entities/topik.entity.ts
index 7523412cc562e04c5c854a3e596195e903ee6d33..127540cc0619fa7863227bf11029f78160287a84 100644
--- a/src/entities/topik.entity.ts
+++ b/src/entities/topik.entity.ts
@@ -1,3 +1,4 @@
+import { ApiProperty } from "@nestjs/swagger";
 import {
   Column,
   Entity,
@@ -6,7 +7,6 @@ import {
   PrimaryGeneratedColumn,
 } from "typeorm";
 import { Pengguna } from "./pengguna.entity";
-import { ApiProperty } from "@nestjs/swagger";
 
 @Entity()
 export class Topik {
@@ -22,7 +22,7 @@ export class Topik {
   @Column({ type: "text" })
   deskripsi: string;
 
-  @ApiProperty({ type: Pengguna })
+  @ApiProperty({ type: () => Pengguna })
   @ManyToOne(() => Pengguna, (pengguna) => pengguna.id)
   @JoinColumn({ name: "idPengaju" })
   pengaju: Pengguna;