diff --git a/src/dashboard/dashboard.controller.ts b/src/dashboard/dashboard.controller.ts
index bb16aebd0a8cb044554d293950f8056baa3b6c4b..8cec5c50418c02eac5f35279a28b7582647e149e 100644
--- a/src/dashboard/dashboard.controller.ts
+++ b/src/dashboard/dashboard.controller.ts
@@ -1,22 +1,24 @@
 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,
   GetDashboardDosbimQueryDto,
+  GetDashboardTimTesisReqQueryDto,
+  GetDashboardTimTesisRespDto,
   JalurStatisticDto,
 } from "./dashboard.dto";
-import {
-  ApiBearerAuth,
-  ApiCookieAuth,
-  ApiOkResponse,
-  ApiTags,
-} from "@nestjs/swagger";
+import { DashboardService } from "./dashboard.service";
 
 @ApiTags("Dashboard")
 @ApiCookieAuth()
@@ -47,4 +49,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 b11d1e6155273b64a4aaf73064f813eada47abd1..22d667de53d46fd14fd84d98baa2caf73e92644e 100644
--- a/src/dashboard/dashboard.dto.ts
+++ b/src/dashboard/dashboard.dto.ts
@@ -4,11 +4,11 @@ import {
   OmitType,
   PickType,
 } from "@nestjs/swagger";
-import { JalurEnum } from "../entities/pendaftaranTesis.entity";
-import { Topik } from "src/entities/topik.entity";
-import { Pengguna } from "src/entities/pengguna.entity";
-import { IsOptional } from "class-validator";
+import { IsNumberString, IsOptional, IsString } from "class-validator";
 import { BimbinganStatus } from "src/entities/bimbingan.entity";
+import { Pengguna } from "src/entities/pengguna.entity";
+import { Topik } from "src/entities/topik.entity";
+import { JalurEnum } from "../entities/pendaftaranTesis.entity";
 
 class PickedTopikDashboard extends PickType(Topik, ["id", "judul"] as const) {}
 class PickedMhsDashboard extends PickType(Pengguna, [
@@ -52,3 +52,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 f168aa396a89aaf5a062d1c061baffa0ad988f45..80fe742036d2aab8a6c953993b43e6ce2feb43ec 100644
--- a/src/dashboard/dashboard.service.ts
+++ b/src/dashboard/dashboard.service.ts
@@ -1,13 +1,24 @@
 import { BadRequestException, Injectable } from "@nestjs/common";
 import { InjectRepository } from "@nestjs/typeorm";
-import { Brackets, Repository } from "typeorm";
+import { BimbinganService } from "src/bimbingan/bimbingan.service";
+import { DosenBimbingan } from "src/entities/dosenBimbingan.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 { DashboardDto, JalurStatisticDto } from "./dashboard.dto";
-import { BimbinganService } from "src/bimbingan/bimbingan.service";
+import { Pengguna, RoleEnum } from "../entities/pengguna.entity";
+import {
+  DashboardDto,
+  DashboardTimTesisStatusEnum,
+  GetDashboardTimTesisReqQueryDto,
+  GetDashboardTimTesisRespDto,
+  JalurStatisticDto,
+} from "./dashboard.dto";
 
 @Injectable()
 export class DashboardService {
@@ -16,6 +27,10 @@ export class DashboardService {
     private pendaftaranTesisRepository: Repository<PendaftaranTesis>,
     @InjectRepository(Pengguna)
     private penggunaRepository: Repository<Pengguna>,
+    @InjectRepository(DosenBimbingan)
+    private dosenBimbinganRepository: Repository<DosenBimbingan>,
+    @InjectRepository(PendaftaranSidsem)
+    private pendaftaranSidsemRepository: Repository<PendaftaranSidsem>,
     private bimbinganService: BimbinganService,
   ) {}
 
@@ -113,4 +128,172 @@ export class DashboardService {
 
     return statistics as JalurStatisticDto[];
   }
+
+  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 17eb3bf158b175d37e93a9a0684a16f943bc5ba1..75bbc9703a7c3c994c434b72b9b392a047ff6300 100644
--- a/src/entities/topik.entity.ts
+++ b/src/entities/topik.entity.ts
@@ -1,3 +1,4 @@
+import { ApiHideProperty, ApiProperty } from "@nestjs/swagger";
 import {
   Column,
   Entity,
@@ -6,7 +7,6 @@ import {
   PrimaryGeneratedColumn,
 } from "typeorm";
 import { Pengguna } from "./pengguna.entity";
-import { ApiHideProperty, ApiProperty } from "@nestjs/swagger";
 
 @Entity()
 export class Topik {