feat: Filter validasi

This commit is contained in:
yosaphatprs 2025-12-01 11:17:47 +07:00
parent b78cbc4fe6
commit 5388ef03bc
4 changed files with 216 additions and 8 deletions

View File

@ -1,4 +1,4 @@
import { Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
import { Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../auth/guard/auth.guard';
import { ValidationService } from './validation.service';
import { CurrentUser } from '../auth/decorator/current-user.decorator';
@ -10,8 +10,29 @@ export class ValidationController {
@Get('/')
@UseGuards(AuthGuard)
async getValidationStatus() {
return this.validationService.getAllValidationsQueue();
async getValidationStatus(
@Query('take') take: number,
@Query('skip') skip: number,
@Query('page') page: number,
@Query('orderBy') orderBy: string,
@Query('searchIdRecord') searchIdRecord: string,
@Query('order') order: 'asc' | 'desc',
@Query('kelompok_data') kelompok_data: string,
@Query('aksi') aksi: string,
@Query('status') status: string,
) {
const queryParams = {
take,
skip,
page,
orderBy,
searchIdRecord,
order,
kelompok_data,
aksi,
status,
};
return this.validationService.getAllValidationsQueue(queryParams);
}
@Get('/:id')

View File

@ -109,12 +109,56 @@ export class ValidationService {
},
};
async getAllValidationsQueue() {
async getAllValidationsQueue(params: any) {
const {
take,
skip,
page,
orderBy,
order,
searchIdRecord,
kelompok_data,
aksi,
status,
} = params;
const skipValue = skip
? parseInt(skip.toString())
: page
? (parseInt(page.toString()) - 1) * take
: 0;
console.log('Params', params);
const result = await this.prisma.validation_queue.findMany({
where: { status: 'PENDING' },
take,
skip: skipValue,
orderBy: orderBy ? { [orderBy]: order || 'asc' } : { created_at: 'desc' },
where: {
record_id: searchIdRecord
? {
contains: searchIdRecord,
}
: undefined,
table_name:
kelompok_data && kelompok_data !== 'all'
? kelompok_data.toLowerCase()
: undefined,
action: aksi && aksi !== 'all' ? aksi.toUpperCase() : undefined,
status: status && status !== 'all' ? status.toUpperCase() : undefined,
},
});
const totalCount = await this.prisma.validation_queue.count({
where: { status: 'PENDING' },
where: {
record_id: searchIdRecord
? {
contains: searchIdRecord,
}
: undefined,
table_name:
kelompok_data && kelompok_data !== 'all'
? kelompok_data.toLowerCase()
: undefined,
action: aksi && aksi !== 'all' ? aksi.toUpperCase() : undefined,
status: status && status !== 'all' ? status.toUpperCase() : undefined,
},
});
return { data: result, totalCount };
}

View File

@ -264,6 +264,7 @@ const handleResetFilters = () => {
pagination.reset();
fetchData();
};
watch(
() => pagination.page.value,
() => {

View File

@ -18,6 +18,7 @@ import SortDropdown from "../../../components/dashboard/SortDropdown.vue";
import DataTable from "../../../components/dashboard/DataTable.vue";
import PaginationControls from "../../../components/dashboard/PaginationControls.vue";
import type { ValidationLog } from "../../../constants/interfaces";
import Footer from "../../../components/dashboard/Footer.vue";
interface ApiResponse {
data: ValidationLog[];
@ -26,7 +27,7 @@ interface ApiResponse {
const data = ref<ValidationLog[]>([]);
const searchValidation = ref("");
const sortBy = ref("id");
const sortBy = ref("created_at");
const router = useRouter();
const route = useRoute();
const pagination = usePagination({
@ -34,10 +35,16 @@ const pagination = usePagination({
initialPageSize: Number(route.query.pageSize) || DEFAULT_PAGE_SIZE,
});
const sortOrder = ref<"asc" | "desc">(
(route.query.order as "asc" | "desc") || "asc"
(route.query.order as "asc" | "desc") || "desc"
);
const api = useApi();
const { debounce } = useDebounce();
const searchId = ref("");
const filters = ref({
kelompok_data: (route.query.kelompok_data as string) || "initial",
aksi: (route.query.aksi as string) || "initial",
status: (route.query.status as string) || "PENDING",
});
const updateQueryParams = () => {
const query: Record<string, string> = {
@ -53,6 +60,21 @@ const updateQueryParams = () => {
query.sortBy = sortBy.value;
}
if (filters.value.status !== "all") {
query.status = filters.value.status;
}
if (filters.value.aksi !== "all" && filters.value.aksi !== "initial") {
query.aksi = filters.value.aksi;
}
if (
filters.value.kelompok_data !== "all" &&
filters.value.kelompok_data !== "initial"
) {
query.kelompok_data = filters.value.kelompok_data;
}
query.order = sortOrder.value;
router.replace({ query });
@ -137,6 +159,14 @@ const normalizedData = (rawData: any[]): ValidationLog[] => {
}));
};
const handleResetFilters = () => {
filters.value.kelompok_data = "all";
filters.value.aksi = "all";
searchId.value = "";
pagination.reset();
fetchData();
};
const fetchData = async () => {
try {
const queryParams = new URLSearchParams({
@ -144,6 +174,12 @@ const fetchData = async () => {
page: pagination.page.value.toString(),
orderBy: sortBy.value,
order: sortOrder.value,
kelompok_data:
filters.value.kelompok_data !== "initial"
? filters.value.kelompok_data
: "",
aksi: filters.value.aksi !== "initial" ? filters.value.aksi : "",
status: filters.value.status !== "initial" ? filters.value.status : "",
...(searchValidation.value && { validation: searchValidation.value }),
});
@ -220,6 +256,30 @@ watch(searchValidation, (newValue, oldValue) => {
}
});
watch(
() => filters.value.kelompok_data,
() => {
pagination.reset();
fetchData();
}
);
watch(
() => filters.value.aksi,
() => {
pagination.reset();
fetchData();
}
);
watch(
() => filters.value.status,
() => {
pagination.reset();
fetchData();
}
);
onMounted(async () => {
if (route.query.search) {
searchValidation.value = route.query.search as string;
@ -244,6 +304,87 @@ onMounted(async () => {
<div class="flex h-full p-2">
<Sidebar>
<PageHeader title="Validasi" subtitle="Manajemen Validasi" />
<div
class="collapse collapse-arrow bg-white border-white border shadow-sm mb-2"
>
<input type="checkbox" />
<div
class="collapse-title font-semibold after:start-5 after:end-auto pe-4 ps-12"
>
Filter
</div>
<div class="collapse-content text-sm flex flex-col gap-4">
<div class="flex gap-x-4">
<div class="flex gap-x-4 items-end">
<div class="h-full">
<label for="jenis_kelamin" class="font-bold"
>Kelompok Data</label
>
<select
v-model="filters.kelompok_data"
class="select bg-white border border-gray-300 mt-1"
>
<option disabled selected value="initial">
Pilih Kelompok Data
</option>
<option value="rekam_medis">Rekam Medis</option>
<option value="pemberian_tindakan">Tindakan</option>
<option value="pemberian_obat">Obat</option>
<option value="all">Semua Tipe</option>
</select>
</div>
</div>
<div class="flex gap-x-4 items-end">
<div class="h-full">
<label for="jenis_kelamin" class="font-bold"
>Jenis Aksi</label
>
<select
v-model="filters.aksi"
class="select bg-white border border-gray-300 mt-1"
>
<option disabled selected value="initial">
Pilih Jenis Aksi
</option>
<option value="CREATE">Create</option>
<option value="UPDATE">Update</option>
<option value="DELETE">Delete</option>
<option value="all">Semua</option>
</select>
</div>
</div>
<div class="flex gap-x-4 items-end">
<div class="h-full">
<label for="jenis_kelamin" class="font-bold"
>Status Validasi</label
>
<select
v-model="filters.status"
class="select bg-white border border-gray-300 mt-1"
>
<option disabled selected value="initial">
Pilih Status Validasi
</option>
<option value="PENDING">Pending</option>
<option value="APPROVED">Approved</option>
<option value="REJECTED">Rejected</option>
<option value="all">Semua</option>
</select>
</div>
</div>
</div>
<div class="flex justify-end">
<button
@click="handleResetFilters"
class="btn btn-sm btn-outline btn-dark hover:bg-dark hover:text-light"
>
Reset Filter
</button>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-md">
<div
class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 px-4 pt-4 pb-2"
@ -315,6 +456,7 @@ onMounted(async () => {
</div>
</Sidebar>
</div>
<Footer />
</div>
</template>