fix: all bugs in fe
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Sabda Yagra 2026-02-25 14:53:07 +07:00
parent 8d2fce74bb
commit b670a175fa
8 changed files with 167 additions and 36 deletions

View File

@ -69,7 +69,7 @@ const FormSchema = z.object({
.refine((val) => !/\s/.test(val), { .refine((val) => !/\s/.test(val), {
message: "Username tidak boleh mengandung spasi", message: "Username tidak boleh mengandung spasi",
}), }),
// .transform((val) => val.toLowerCase()), // .transform((val) => val.toLowerCase()),
password: z password: z
.string({ required_error: "Required" }) .string({ required_error: "Required" })
@ -85,6 +85,7 @@ const FormSchema = z.object({
skills: z.string({ required_error: "Required" }), skills: z.string({ required_error: "Required" }),
experiences: z.string({ required_error: "Required" }), experiences: z.string({ required_error: "Required" }),
company: z.string({ required_error: "Required" }), company: z.string({ required_error: "Required" }),
address: z.string({ required_error: "Required" }),
}); });
// .refine((data) => data.password === data.confirmPassword, { // .refine((data) => data.password === data.confirmPassword, {
// path: ["confirmPassword"], // path: ["confirmPassword"],
@ -116,7 +117,7 @@ export default function AddExpertForm() {
const togglePasswordType = () => { const togglePasswordType = () => {
setPasswordType((prevType) => setPasswordType((prevType) =>
prevType === "password" ? "text" : "password" prevType === "password" ? "text" : "password",
); );
}; };
@ -155,7 +156,7 @@ export default function AddExpertForm() {
username: data.username, username: data.username,
email: data.email, email: data.email,
password: data.password, password: data.password,
address: "", address: data.address,
roleId: "EXP-ID", roleId: "EXP-ID",
phoneNumber: data.phoneNumber, phoneNumber: data.phoneNumber,
userCompetencyId: data.skills, userCompetencyId: data.skills,
@ -264,12 +265,12 @@ export default function AddExpertForm() {
const handleSelectionChange = ( const handleSelectionChange = (
index: number, index: number,
type: "roleId" | "userLevelId", type: "roleId" | "userLevelId",
value: string value: string,
) => { ) => {
setPlacementRows((prevRows) => setPlacementRows((prevRows) =>
prevRows.map((row) => prevRows.map((row) =>
row.index === index ? { ...row, [type]: value } : row row.index === index ? { ...row, [type]: value } : row,
) ),
); );
}; };
@ -424,6 +425,24 @@ export default function AddExpertForm() {
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Alamat</FormLabel>
<Input
type="text"
value={field.value}
placeholder="Masukkan Alamat"
onChange={field.onChange}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="password" name="password"
@ -462,8 +481,8 @@ export default function AddExpertForm() {
passwordStrength === "weak" passwordStrength === "weak"
? "bg-red-500 w-1/4" ? "bg-red-500 w-1/4"
: passwordStrength === "medium" : passwordStrength === "medium"
? "bg-yellow-500 w-2/4" ? "bg-yellow-500 w-2/4"
: "bg-green-500 w-full" : "bg-green-500 w-full"
}`} }`}
/> />
@ -472,8 +491,8 @@ export default function AddExpertForm() {
passwordStrength === "weak" passwordStrength === "weak"
? "text-red-500" ? "text-red-500"
: passwordStrength === "medium" : passwordStrength === "medium"
? "text-yellow-600" ? "text-yellow-600"
: "text-green-600" : "text-green-600"
}`} }`}
> >
{passwordStrength === "weak" && "Weak Password"} {passwordStrength === "weak" && "Weak Password"}

View File

@ -53,7 +53,7 @@ const TableImage = () => {
const [totalPage, setTotalPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1);
const [sorting, setSorting] = React.useState<SortingState>([]); const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>( const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[] [],
); );
const [columnVisibility, setColumnVisibility] = const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({}); React.useState<VisibilityState>({});
@ -64,7 +64,7 @@ const TableImage = () => {
const [searchTimeout, setSearchTimeout] = React.useState<any>(null); const [searchTimeout, setSearchTimeout] = React.useState<any>(null);
const [categories, setCategories] = React.useState<any[]>([]); const [categories, setCategories] = React.useState<any[]>([]);
const [selectedCategories, setSelectedCategories] = React.useState<number[]>( const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
[] [],
); );
const [categoryFilter, setCategoryFilter] = React.useState<string>(""); const [categoryFilter, setCategoryFilter] = React.useState<string>("");
const [statusFilter, setStatusFilter] = React.useState<any[]>([]); const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
@ -170,6 +170,10 @@ const TableImage = () => {
} }
const isForSelf = Number(roleId) === 4; const isForSelf = Number(roleId) === 4;
const isNeedApproval = statusFilter.includes(1);
const needApprovalFrom = isNeedApproval ? userLevelId : "";
const res = await listDataImage( const res = await listDataImage(
parseInt(showData) || 10, parseInt(showData) || 10,
page - 1, page - 1,
@ -177,16 +181,33 @@ const TableImage = () => {
!isForSelf, !isForSelf,
categoryFilter, categoryFilter,
statusFilter, statusFilter,
statusFilter?.sort().join(",").includes("1") ? userLevelId : "", needApprovalFrom,
filterByCreator, filterByCreator,
filterBySource, filterBySource,
formattedStartDate, formattedStartDate,
formattedEndDate, formattedEndDate,
customSearch ?? search, customSearch ?? search,
filterByCreatorGroup, filterByCreatorGroup,
locale == "en" locale == "en",
); );
// const res = await listDataImage(
// parseInt(showData) || 10,
// page - 1,
// isForSelf,
// !isForSelf,
// categoryFilter,
// statusFilter,
// statusFilter?.sort().join(",").includes("1") ? userLevelId : "",
// filterByCreator,
// filterBySource,
// formattedStartDate,
// formattedEndDate,
// customSearch ?? search,
// filterByCreatorGroup,
// locale == "en"
// );
const data = res?.data?.data; const data = res?.data?.data;
const contentData = data?.content || []; const contentData = data?.content || [];
const newData = contentData.map((item: any, index: number) => ({ const newData = contentData.map((item: any, index: number) => ({
@ -209,7 +230,7 @@ const TableImage = () => {
setSelectedCategories((prev) => setSelectedCategories((prev) =>
prev.includes(categoryId) prev.includes(categoryId)
? prev.filter((id) => id !== categoryId) ? prev.filter((id) => id !== categoryId)
: [...prev, categoryId] : [...prev, categoryId],
); );
setCategoryFilter((prev) => { setCategoryFilter((prev) => {
@ -225,7 +246,7 @@ const TableImage = () => {
setStatusFilter((prev: any) => setStatusFilter((prev: any) =>
prev.includes(value) prev.includes(value)
? prev.filter((status: any) => status !== value) ? prev.filter((status: any) => status !== value)
: [...prev, value] : [...prev, value],
); );
}; };
@ -253,14 +274,14 @@ const TableImage = () => {
}; };
const handleSearchFilterByCreator = ( const handleSearchFilterByCreator = (
e: React.ChangeEvent<HTMLInputElement> e: React.ChangeEvent<HTMLInputElement>,
) => { ) => {
setFilterByCreator(e.target.value); setFilterByCreator(e.target.value);
fetchData(); fetchData();
}; };
const handleSearchFilterBySource = ( const handleSearchFilterBySource = (
e: React.ChangeEvent<HTMLInputElement> e: React.ChangeEvent<HTMLInputElement>,
) => { ) => {
setFilterBySource(e.target.value); setFilterBySource(e.target.value);
fetchData(); fetchData();
@ -430,10 +451,10 @@ const TableImage = () => {
id === 1 id === 1
? "Menunggu Review" ? "Menunggu Review"
: id === 2 : id === 2
? "Diterima" ? "Diterima"
: id === 3 : id === 3
? "Minta Update" ? "Minta Update"
: "Ditolak"; : "Ditolak";
return ( return (
<div key={id} className="flex items-center px-4 py-1"> <div key={id} className="flex items-center px-4 py-1">
<input <input
@ -491,7 +512,7 @@ const TableImage = () => {
? null ? null
: flexRender( : flexRender(
header.column.columnDef.header, header.column.columnDef.header,
header.getContext() header.getContext(),
)} )}
</TableHead> </TableHead>
))} ))}

View File

@ -235,12 +235,35 @@ const useTableColumns = (
<DropdownMenuContent align="end" className="p-0"> <DropdownMenuContent align="end" className="p-0">
{/* VIEW */} {/* VIEW */}
<Link href={`/contributor/task-ta/detail/${row.original.id}`}> {/* <Link href={`/contributor/task-ta/detail/${row.original.id}`}>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
View
</DropdownMenuItem>
</Link> */}
{/* VIEW */}
<Link
href={
activeTab === "special"
? `/contributor/task/detail/${row.original.id}`
: `/contributor/task-ta/detail/${row.original.id}`
}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none"> <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" /> <Eye className="w-4 h-4 me-1.5" />
View View
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
{roleId === 19 && (
<Link
href={`/contributor/task-ta/upload-task/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Upload className="w-4 h-4 me-1.5" />
Upload Tugas
</DropdownMenuItem>
</Link>
)}
{/* UPLOAD (MABES → KOOR TAB) */} {/* UPLOAD (MABES → KOOR TAB) */}
{activeTab === "mabes-koor" && roleId === 11 && ( {activeTab === "mabes-koor" && roleId === 11 && (

View File

@ -60,6 +60,7 @@ export default function TaskTaTable() {
const roleId = Number(getCookiesDecrypt("urie")); const roleId = Number(getCookiesDecrypt("urie"));
const userId = Number(getCookiesDecrypt("uie")); const userId = Number(getCookiesDecrypt("uie"));
const isKoorKuratorRole11 = roleId === 11; const isKoorKuratorRole11 = roleId === 11;
const isRole19 = roleId === 19;
const isMabesApprover = const isMabesApprover =
userLevelId === MABES_LEVEL_ID && roleId === APPROVER_ROLE_ID; userLevelId === MABES_LEVEL_ID && roleId === APPROVER_ROLE_ID;
@ -301,7 +302,7 @@ export default function TaskTaTable() {
<label className="inline-flex text-md cursor-pointer"> <label className="inline-flex text-md cursor-pointer">
<div className="flex mb-6 flex-wrap gap-2"> <div className="flex mb-6 flex-wrap gap-2">
{/* ❗ JIKA MABES APPROVER → HANYA 1 TAB */} {/* ❗ JIKA MABES APPROVER → HANYA 1 TAB */}
{isMabesApprover ? ( {/* {isMabesApprover ? (
<button <button
onClick={() => setActiveTab("mabes-koor")} onClick={() => setActiveTab("mabes-koor")}
className={`px-4 py-1 rounded transition ${ className={`px-4 py-1 rounded transition ${
@ -314,7 +315,6 @@ export default function TaskTaTable() {
</button> </button>
) : ( ) : (
<> <>
{/* 👇 USER SELAIN MABES APPROVER */}
<button <button
onClick={() => setActiveTab("special")} onClick={() => setActiveTab("special")}
className={`px-4 py-1 rounded transition ${ className={`px-4 py-1 rounded transition ${
@ -361,6 +361,73 @@ export default function TaskTaTable() {
</button> </button>
)} )}
</> </>
)} */}
{isMabesApprover ? (
<button
onClick={() => setActiveTab("mabes-koor")}
className={`px-4 py-1 rounded transition ${
activeTab === "mabes-koor"
? "bg-default-900 text-white dark:text-black"
: "border dark:text-default-700"
}`}
>
Atensi Khusus Mabes Koor Kurator
</button>
) : (
<>
{/* ❗ HIDE untuk role 19 */}
{!isRole19 && (
<>
<button
onClick={() => setActiveTab("special")}
className={`px-4 py-1 rounded transition ${
activeTab === "special"
? "bg-default-900 text-white dark:text-black"
: "border dark:text-default-700"
}`}
>
Atensi Khusus Mabes
</button>
<button
onClick={() => setActiveTab("mabes-koor")}
className={`px-4 py-1 rounded transition ${
activeTab === "mabes-koor"
? "bg-default-900 text-white dark:text-black"
: "border dark:text-default-700"
}`}
>
Atensi Khusus Mabes Koor Kurator
</button>
</>
)}
{/* ✅ TA tetap tampil untuk semua */}
<button
onClick={() => setActiveTab("ta")}
className={`px-4 py-1 rounded transition ${
activeTab === "ta"
? "bg-default-900 text-white dark:text-black"
: "border dark:text-default-700"
}`}
>
Atensi Khusus TA
</button>
{/* ❗ Daily juga hide untuk role 19 */}
{!isKoorKuratorRole11 && !isRole19 && (
<button
onClick={() => setActiveTab("daily")}
className={`px-4 py-1 rounded transition ${
activeTab === "daily"
? "bg-default-900 text-white dark:text-black"
: "border dark:text-default-700"
}`}
>
{t("daily-tasks", { defaultValue: "Daily Tasks" })}
</button>
)}
</>
)} )}
</div> </div>
</label> </label>

View File

@ -63,15 +63,14 @@ const DashboardPage = () => {
> >
{t("indeks", { defaultValue: "Indeks" })} {t("indeks", { defaultValue: "Indeks" })}
</TabsTrigger> </TabsTrigger>
<TabsTrigger
value="report"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
{t("report", { defaultValue: "Report" })}
</TabsTrigger>
</> </>
)} )}
<TabsTrigger
value="report"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
{t("report", { defaultValue: "Report" })}
</TabsTrigger>
</TabsList> </TabsList>
</Card> </Card>
<TabsContent value="routine-task"> <TabsContent value="routine-task">

View File

@ -10,6 +10,7 @@ import { useTranslations } from "next-intl";
import { z } from "zod"; import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter } from "@/i18n/routing"; import { useRouter } from "@/i18n/routing";
import Image from "next/image";
interface IFormInput { interface IFormInput {
name: string; name: string;
@ -112,7 +113,7 @@ const ContactForm = () => {
<Reveal> <Reveal>
{/* Header */} {/* Header */}
<div className="flex items-center justify-center mb-6"> <div className="flex items-center justify-center mb-6">
<img src="/assets/icons-contact.png" alt="contact" /> <Image src="/assets/icons-contact.png" alt="contact" />
<h2 className="ml-4 text-2xl font-bold"> <h2 className="ml-4 text-2xl font-bold">
{t("contactUs", { defaultValue: "Contact Us" })} {t("contactUs", { defaultValue: "Contact Us" })}
</h2> </h2>

View File

@ -5,6 +5,7 @@ import { error, loading, successCallback } from "@/config/swal";
import { getFeedback, postUserFeedback } from "@/service/landing/landing"; import { getFeedback, postUserFeedback } from "@/service/landing/landing";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import Image from "next/image";
interface RatingProps { interface RatingProps {
label: string; label: string;
@ -119,7 +120,7 @@ const FeedbackForm: React.FC = () => {
<Reveal> <Reveal>
<div className="max-w-6xl flex flex-col mx-auto p-4 lg:p-40 gap-5 "> <div className="max-w-6xl flex flex-col mx-auto p-4 lg:p-40 gap-5 ">
<div className="flex items-center justify-center mb-6"> <div className="flex items-center justify-center mb-6">
<img src="/assets/icons-feedback.png" alt="Feedback" /> <Image src="/assets/icons-feedback.png" alt="Feedback" />
<h2 className="ml-4 text-[15px] lg:text-[32px] font-bold text-gray-800 dark:text-white">{t("userFeedback", { defaultValue: "User Feedback" })}</h2> <h2 className="ml-4 text-[15px] lg:text-[32px] font-bold text-gray-800 dark:text-white">{t("userFeedback", { defaultValue: "User Feedback" })}</h2>
</div> </div>
<div className="text-black dark:text-white"> <div className="text-black dark:text-white">

View File

@ -1645,7 +1645,7 @@ export default function FormTaskTaDetail() {
variant={"default"} variant={"default"}
onClick={() => { onClick={() => {
setIsTableResult(!isTableResult); setIsTableResult(!isTableResult);
if (!isTableResult) fetchAllData(); // Panggil API saat tombol diklik if (!isTableResult) fetchAllData();
}} }}
> >
Hasil Upload Hasil Upload