notification forum #40

Merged
bagasbanuna merged 2 commits from notification/19-jan-26 into staging 2026-01-20 10:41:07 +08:00
18 changed files with 366 additions and 114 deletions

View File

@@ -616,6 +616,20 @@ export default function UserLayout() {
headerLeft: () => <BackButton />, headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen
name="forum/[id]/preview-report-posting"
options={{
title: "Laporan Postingan",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/preview-report-comment"
options={{
title: "Laporan Komentar",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== Maps Section ========= */} {/* ========== Maps Section ========= */}
<Stack.Screen <Stack.Screen

View File

@@ -19,34 +19,20 @@ import {
apiForumGetOne, apiForumGetOne,
apiForumUpdateStatus, apiForumUpdateStatus,
} from "@/service/api-client/api-forum"; } from "@/service/api-client/api-forum";
import { TypeForum_CommentProps } from "@/types/type-forum";
import { isBadContent } from "@/utils/badWordsIndonesia"; import { isBadContent } from "@/utils/badWordsIndonesia";
import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { Alert } from "react-native";
interface CommentProps {
id: string;
isActive: boolean;
komentar: string;
createdAt: Date;
authorId: string;
Author: {
id: string;
username: string;
Profile: {
id: string;
imageId: string;
};
};
}
export default function ForumDetail() { export default function ForumDetail() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const { user } = useAuth(); const { user } = useAuth();
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const [data, setData] = useState<any | null>(null); const [data, setData] = useState<any | null>(null);
const [listComment, setListComment] = useState<CommentProps[] | null>(null); const [listComment, setListComment] = useState<TypeForum_CommentProps[] | null>(null);
const [isLoadingComment, setLoadingComment] = useState(false); const [isLoadingComment, setLoadingComment] = useState(false);
// Status // Status
@@ -122,6 +108,7 @@ export default function ForumDetail() {
comment: text, comment: text,
authorId: user?.id, authorId: user?.id,
}; };
try { try {
setLoadingComment(true); setLoadingComment(true);
const response = await apiForumCreateComment({ const response = await apiForumCreateComment({

View File

@@ -6,7 +6,7 @@ import {
} from "@/components"; } from "@/components";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiForumCreateReportCommentar } from "@/service/api-client/api-master"; import { apiForumCreateReportCommentar } from "@/service/api-client/api-forum";
import { router, useLocalSearchParams } from "expo-router"; import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react"; import { useState } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";

View File

@@ -6,7 +6,7 @@ import {
} from "@/components"; } from "@/components";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiForumCreateReportPosting } from "@/service/api-client/api-master"; import { apiForumCreateReportPosting } from "@/service/api-client/api-forum";
import { router, useLocalSearchParams } from "expo-router"; import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react"; import { useState } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";

View File

@@ -0,0 +1,91 @@
import {
BaseBox,
NewWrapper,
Spacing,
StackCustom,
TextCustom,
} from "@/components";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
import NoDataText from "@/components/_ShareComponent/NoDataText";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { apiForumGetReportComment } from "@/service/api-client/api-forum";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function ForumPreviewReportComment() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [listData, setListData] = useState<any | null>(null);
const [loading, setLoading] = useState<boolean>(false);
// Status
useFocusEffect(
useCallback(() => {
onLoadData(id as string);
}, [id])
);
const onLoadData = async (id: string) => {
try {
setLoading(true);
const response = await apiForumGetReportComment({ id });
setData(response.data);
setListData(response?.data?.Forum_ReportKomentar);
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
return (
<>
<NewWrapper>
<StackCustom>
<TextCustom color="red" bold>
Komentar anda telah melanggar aturan forum ! Admin mengambil
tindakan untuk menghapus komentar anda!
</TextCustom>
{loading ? (
<CustomSkeleton height={100} />
) : (
<BaseBox>
<TextCustom>"{data?.komentar ? data?.komentar : "-"}"</TextCustom>
</BaseBox>
)}
</StackCustom>
<Spacing height={10} />
<TextCustom bold>Beberapa laporan yang telah diterima</TextCustom>
<Spacing height={10} />
{loading ? (
<ListSkeletonComponent />
) : _.isEmpty(listData) ? (
<NoDataText />
) : (
listData?.map((e: any, index: number) => (
<BaseBox key={index}>
{e?.deskripsi ? (
<StackCustom gap={"sm"}>
<TextCustom bold>Laporan Lainnya</TextCustom>
<TextCustom>{e?.deskripsi}</TextCustom>
</StackCustom>
) : (
<StackCustom gap={"sm"}>
<TextCustom bold>
{e?.ForumMaster_KategoriReport?.title}
</TextCustom>
<TextCustom>
{e?.ForumMaster_KategoriReport?.deskripsi}
</TextCustom>
</StackCustom>
)}
</BaseBox>
))
)}
</NewWrapper>
</>
);
}

View File

@@ -0,0 +1,91 @@
import {
BaseBox,
NewWrapper,
Spacing,
StackCustom,
TextCustom,
} from "@/components";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
import NoDataText from "@/components/_ShareComponent/NoDataText";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { apiForumGetReportPosting } from "@/service/api-client/api-forum";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function ForumPreviewReportPosting() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [listData, setListData] = useState<any | null>(null);
const [loading, setLoading] = useState<boolean>(false);
// Status
useFocusEffect(
useCallback(() => {
onLoadData(id as string);
}, [id])
);
const onLoadData = async (id: string) => {
try {
setLoading(true);
const response = await apiForumGetReportPosting({ id });
setData(response.data);
setListData(response?.data?.Forum_ReportPosting);
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
return (
<>
<NewWrapper>
<StackCustom>
<TextCustom color="red" bold>
Postingan anda telah melanggar aturan forum ! Admin mengambil
tindakan untuk menghapus komentar anda!
</TextCustom>
{loading ? (
<CustomSkeleton height={100} />
) : (
<BaseBox>
<TextCustom>"{data?.diskusi ? data?.diskusi : "-"}"</TextCustom>
</BaseBox>
)}
</StackCustom>
<Spacing height={10} />
<TextCustom bold>Beberapa laporan yang telah diterima</TextCustom>
<Spacing height={10} />
{loading ? (
<ListSkeletonComponent />
) : _.isEmpty(listData) ? (
<NoDataText />
) : (
listData?.map((e: any) => (
<BaseBox key={e?.id}>
{e?.deskripsi ? (
<StackCustom gap={"sm"}>
<TextCustom bold>Laporan Lainnya</TextCustom>
<TextCustom>{e?.deskripsi}</TextCustom>
</StackCustom>
) : (
<StackCustom gap={"sm"}>
<TextCustom bold>
{e?.ForumMaster_KategoriReport?.title}
</TextCustom>
<TextCustom>
{e?.ForumMaster_KategoriReport?.deskripsi}
</TextCustom>
</StackCustom>
)}
</BaseBox>
))
)}
</NewWrapper>
</>
);
}

View File

@@ -8,7 +8,8 @@ import {
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import Forum_ReportListSection from "@/screens/Forum/ReportListSection"; import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
import { apiForumCreateReportCommentar, apiMasterForumReportList } from "@/service/api-client/api-master"; import { apiForumCreateReportCommentar } from "@/service/api-client/api-forum";
import { apiMasterForumReportList } from "@/service/api-client/api-master";
import { router, useLocalSearchParams } from "expo-router"; import { router, useLocalSearchParams } from "expo-router";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";

View File

@@ -9,8 +9,8 @@ import {
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import Forum_ReportListSection from "@/screens/Forum/ReportListSection"; import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
import { apiForumCreateReportPosting } from "@/service/api-client/api-forum";
import { import {
apiForumCreateReportPosting,
apiMasterForumReportList, apiMasterForumReportList,
} from "@/service/api-client/api-master"; } from "@/service/api-client/api-master";
import { router, useLocalSearchParams } from "expo-router"; import { router, useLocalSearchParams } from "expo-router";

View File

@@ -2,15 +2,14 @@ import {
BoxButtonOnFooter, BoxButtonOnFooter,
ButtonCustom, ButtonCustom,
TextAreaCustom, TextAreaCustom,
ViewWrapper ViewWrapper,
} from "@/components"; } from "@/components";
import AlertWarning from "@/components/Alert/AlertWarning"; import AlertWarning from "@/components/Alert/AlertWarning";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiForumCreate } from "@/service/api-client/api-forum"; import { apiForumCreate } from "@/service/api-client/api-forum";
import { isBadContent } from "@/utils/badWordsIndonesia"; import { censorText, isBadContent } from "@/utils/badWordsIndonesia";
import { router } from "expo-router"; import { router } from "expo-router";
import { useState } from "react"; import { useState } from "react";
import { Alert } from "react-native";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function ForumCreate() { export default function ForumCreate() {
@@ -19,16 +18,22 @@ export default function ForumCreate() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const handlerSubmit = async () => { const handlerSubmit = async () => {
if (text.trim() === "") {
if (isBadContent(text)) { AlertWarning({
AlertWarning({}) title: "Lengkapi Data",
description: "Postingan tidak boleh kosong",
});
return; return;
} }
// Bisa di sensor atau return dan tidak bisa di post
const cencorContent = censorText(text)
const newData = { const newData = {
diskusi: text, diskusi: cencorContent,
authorId: user?.id, authorId: user?.id,
}; };
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiForumCreate({ data: newData }); const response = await apiForumCreate({ data: newData });
@@ -50,6 +55,7 @@ export default function ForumCreate() {
const buttonFooter = ( const buttonFooter = (
<BoxButtonOnFooter> <BoxButtonOnFooter>
<ButtonCustom <ButtonCustom
disabled={!text.trim() || isLoading}
isLoading={isLoading} isLoading={isLoading}
onPress={() => { onPress={() => {
handlerSubmit(); handlerSubmit();

View File

@@ -15,12 +15,11 @@ import { IconDot, IconView } from "@/components/_Icon/IconComponent";
import { IconTrash } from "@/components/_Icon/IconTrash"; import { IconTrash } from "@/components/_Icon/IconTrash";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent"; import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { import {
apiAdminForumCommentById, apiAdminForumCommentById,
apiAdminForumDeactivateComment, apiAdminForumDeactivateComment,
@@ -35,6 +34,7 @@ import Toast from "react-native-toast-message";
export default function AdminForumReportComment() { export default function AdminForumReportComment() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const { user } = useAuth();
const [data, setData] = useState<any | null>(null); const [data, setData] = useState<any | null>(null);
const [listReport, setListReport] = useState<any[] | null>(null); const [listReport, setListReport] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false); const [loadList, setLoadList] = useState(false);
@@ -111,9 +111,13 @@ export default function AdminForumReportComment() {
<AdminComp_BoxTitle title="Daftar Report Komentar" /> <AdminComp_BoxTitle title="Daftar Report Komentar" />
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<GridSpan_NewComponent <GridSpan_NewComponent
text1={<TextCustom bold align="center">Aksi</TextCustom>} text1={
<TextCustom bold align="center">
Aksi
</TextCustom>
}
text2={<TextCustom bold>Pelapor</TextCustom>} text2={<TextCustom bold>Pelapor</TextCustom>}
text3={<TextCustom bold>Kategori Report</TextCustom>} text3={<TextCustom bold>Kategori Report</TextCustom>}
/> />
@@ -129,22 +133,24 @@ export default function AdminForumReportComment() {
<View key={index}> <View key={index}>
<GridSpan_NewComponent <GridSpan_NewComponent
text1={ text1={
<CenterCustom> <CenterCustom>
<ActionIcon <ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />} icon={
onPress={() => { <IconView size={ICON_SIZE_BUTTON} color="black" />
setOpenDrawerAction(true); }
setSelectedReport({ onPress={() => {
id: item.id, setOpenDrawerAction(true);
username: item.User?.username, setSelectedReport({
kategori: item.ForumMaster_KategoriReport?.title, id: item.id,
keterangan: username: item.User?.username,
item.ForumMaster_KategoriReport?.deskripsi, kategori: item.ForumMaster_KategoriReport?.title,
deskripsi: item.deskripsi, keterangan:
}); item.ForumMaster_KategoriReport?.deskripsi,
}} deskripsi: item.deskripsi,
/> });
</CenterCustom> }}
/>
</CenterCustom>
} }
text2={ text2={
<TextCustom truncate={1}> <TextCustom truncate={1}>
@@ -188,15 +194,18 @@ export default function AdminForumReportComment() {
onPressRight: async () => { onPressRight: async () => {
const deleteComment = await apiAdminForumDeactivateComment({ const deleteComment = await apiAdminForumDeactivateComment({
id: id as string, id: id as string,
data: {
senderId: user?.id as string,
},
}); });
if (!deleteComment.success) { // if (!deleteComment.success) {
Toast.show({ // Toast.show({
type: "error", // type: "error",
text1: "Komentar gagal dihapus", // text1: "Komentar gagal dihapus",
}); // });
return; // return;
} // }
setOpenDrawer(false); setOpenDrawer(false);
Toast.show({ Toast.show({

View File

@@ -16,12 +16,11 @@ import { IconDot, IconView } from "@/components/_Icon/IconComponent";
import { IconTrash } from "@/components/_Icon/IconTrash"; import { IconTrash } from "@/components/_Icon/IconTrash";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent"; import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { import {
apiAdminForumDeactivatePosting, apiAdminForumDeactivatePosting,
apiAdminForumListReportPostingById, apiAdminForumListReportPostingById,
@@ -35,6 +34,7 @@ import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function AdminForumReportPosting() { export default function AdminForumReportPosting() {
const { user } = useAuth();
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [openDrawerPage, setOpenDrawerPage] = useState(false); const [openDrawerPage, setOpenDrawerPage] = useState(false);
const [openDrawerAction, setOpenDrawerAction] = useState(false); const [openDrawerAction, setOpenDrawerAction] = useState(false);
@@ -215,6 +215,9 @@ export default function AdminForumReportPosting() {
onPressRight: async () => { onPressRight: async () => {
const response = await apiAdminForumDeactivatePosting({ const response = await apiAdminForumDeactivatePosting({
id: id as string, id: id as string,
data: {
senderId: user?.id as string,
},
}); });
if (!response.success) { if (!response.success) {

View File

@@ -73,7 +73,7 @@ export default function AdminForumReportPosting() {
<GridSpan_NewComponent <GridSpan_NewComponent
text1={ text1={
<TextCustom bold truncate> <TextCustom bold truncate>
Username Pelapor
</TextCustom> </TextCustom>
} }
text2={ text2={

View File

@@ -13,7 +13,8 @@ export const tabsHome: any = ({
icon: "chatbubble-ellipses-outline", icon: "chatbubble-ellipses-outline",
activeIcon: "chatbubble-ellipses", activeIcon: "chatbubble-ellipses",
label: "Forum", label: "Forum",
path: acceptedForumTermsAt ? "/forum" : "/forum/terms", // path: acceptedForumTermsAt ? "/forum" : "/forum/terms",
path: "/forum",
isActive: true, isActive: true,
disabled: false, disabled: false,
}, },

View File

@@ -57,9 +57,19 @@ export async function apiAdminForumListReportCommentById({
} }
} }
export async function apiAdminForumDeactivateComment({ id }: { id: string }) { export async function apiAdminForumDeactivateComment({
id,
data,
}: {
id: string;
data: { senderId: string };
}) {
console.log("data", data)
try { try {
const response = await apiConfig.put(`/mobile/admin/forum/${id}/comment`); const response = await apiConfig.put(`/mobile/admin/forum/${id}/comment`, {
data: data,
});
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;
@@ -81,9 +91,17 @@ export async function apiAdminForumListReportPostingById({
} }
} }
export async function apiAdminForumDeactivatePosting({ id }: { id: string }) { export async function apiAdminForumDeactivatePosting({
id,
data,
}: {
id: string;
data: { senderId: string };
}) {
try { try {
const response = await apiConfig.put(`/mobile/admin/forum/${id}`); const response = await apiConfig.put(`/mobile/admin/forum/${id}`, {
data: data,
});
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;

View File

@@ -119,3 +119,62 @@ export async function apiForumDeleteComment({ id }: { id: string }) {
throw error; throw error;
} }
} }
export async function apiForumGetReportPosting({id}: {id:string}) {
try {
const response = await apiConfig.get(`/mobile/forum/${id}/preview-report-posting`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiForumGetReportComment({id}: {id:string}) {
try {
const response = await apiConfig.get(`/mobile/forum/${id}/preview-report-comment`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiForumCreateReportPosting({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.post(
`/mobile/forum/${id}/report-posting`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiForumCreateReportCommentar({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.post(
`/mobile/forum/${id}/report-commentar`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -77,46 +77,6 @@ export async function apiMasterForumReportList() {
// ================== END MASTER FORUM ================== // // ================== END MASTER FORUM ================== //
export async function apiForumCreateReportPosting({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.post(
`/mobile/forum/${id}/report-posting`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiForumCreateReportCommentar({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.post(
`/mobile/forum/${id}/report-commentar`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}
// ================== START MASTER INVESTMENT ================== // // ================== START MASTER INVESTMENT ================== //
export async function apiMasterInvestment({ export async function apiMasterInvestment({

15
types/type-forum.ts Normal file
View File

@@ -0,0 +1,15 @@
export interface TypeForum_CommentProps {
id: string;
isActive: boolean;
komentar: string;
createdAt: Date;
authorId: string;
Author: {
id: string;
username: string;
Profile: {
id: string;
imageId: string;
};
};
}

View File

@@ -5,11 +5,11 @@ const badWordsIndonesia = [
'anjing', 'babi', 'bangsat', 'bodoh', 'goblok', 'idiot', 'jancok', 'jembut', 'kampret', 'anjing', 'babi', 'bangsat', 'bodoh', 'goblok', 'idiot', 'jancok', 'jembut', 'kampret',
'kontol', 'memek', 'ngentot', 'peler', 'puki', 'sialan', 'tai', 'tolol', 'wibu', 'kontol', 'memek', 'ngentot', 'peler', 'puki', 'sialan', 'tai', 'tolol', 'wibu',
'anjingg', 'babbii', 'bangsaat', 'gobllokk', 'jancokk', 'kontoll', 'memekk', 'ngentott', 'anjingg', 'babbii', 'bangsaat', 'gobllokk', 'jancokk', 'kontoll', 'memekk', 'ngentott',
'pelerr', 'puuki', 'sialann', 'taii', 'tololl', 'wibuu', 'pelerr', 'puuki', 'sialann', 'taii', 'tololl', 'wibuu', 'cicing',
// 🔥 Kata Sindiran & Penghinaan // 🔥 Kata Sindiran & Penghinaan
'bego', 'dungu', 'edan', 'gila', 'goblog', 'kampang', 'kampret', 'keparat', 'lonte', 'bego', 'dungu', 'edan', 'gila', 'goblog', 'kampang', 'kampret', 'keparat', 'lonte',
'main mata', 'monyet', 'najis', 'ngeyel', 'ngibul', 'ngomong seenaknya', 'ngurangin', 'monyet', 'najis', 'ngeyel', 'ngibul', 'ngomong seenaknya', 'ngurangin',
'ngutang', 'ngurusin urusan orang', 'pemalas', 'pengecut', 'penipu', 'sinting', 'ngutang', 'ngurusin urusan orang', 'pemalas', 'pengecut', 'penipu', 'sinting',
'begoo', 'dunguu', 'goblogg', 'kampangg', 'keparatt', 'lontee', 'monyyet', 'najiss', 'begoo', 'dunguu', 'goblogg', 'kampangg', 'keparatt', 'lontee', 'monyyet', 'najiss',
'ngeyell', 'ngibull', 'ngomongg seenaknya', 'nguranginn', 'ngutangg', 'pemalass', 'ngeyell', 'ngibull', 'ngomongg seenaknya', 'nguranginn', 'ngutangg', 'pemalass',
@@ -23,18 +23,17 @@ const badWordsIndonesia = [
// 💸 Kata Spam / Promosi Ilegal // 💸 Kata Spam / Promosi Ilegal
'judi', 'togel', 'slot', 'casino', 'poker', 'qq', 'bandar', 'agen', 'link', 'wa', 'judi', 'togel', 'slot', 'casino', 'poker', 'qq', 'bandar', 'agen', 'link', 'wa',
'whatsapp', 'telepon', 'nomor', 'hp', 'sms', 'grup', 'join', 'daftar', 'bonus', 'deposit', 'withdraw',
'deposit', 'withdraw', 'uang', 'duit', 'rp', 'ratusan', 'juta', 'milyar',
'judii', 'togell', 'slotss', 'casinoo', 'pokerr', 'qqq', 'bandarr', 'agenn', 'linkk', 'judii', 'togell', 'slotss', 'casinoo', 'pokerr', 'qqq', 'bandarr', 'agenn', 'linkk',
'waa', 'whatsappp', 'teleponn', 'nomorr', 'hpp', 'smss', 'grupp', 'jooin', 'daftarr', 'waa',
'bonuss', 'depositt', 'withdraww', 'uangs', 'duitt', 'rpp', 'ratusann', 'jutaa', 'milyarr', 'depositt', 'withdraww', 'rpp',
// 🧩 Variasi Penulisan (Bypass Filter) // 🧩 Variasi Penulisan (Bypass Filter)
'a*njing', 'b*b*i', 'b*ngsat', 'g*blok', 'k*nt*l', 'm*m*k', 'n*g*nt*t', 'p*l*r', 'a*njing', 'b*b*i', 'b*ngsat', 'g*blok', 'k*nt*l', 'm*m*k', 'n*g*nt*t', 'p*l*r',
't*i', 't*l*l', 'j*n*c*k', 'j*m*b*t', 'k*m*p*r*t', 's*i*l*a*n', 'w*b*u', 't*i', 't*l*l', 'j*n*c*k', 'j*m*b*t', 'k*m*p*r*t', 's*i*l*a*n', 'w*b*u',
'a.n.j.i.n.g', 'b.a.b.i', 'b.a.n.g.s.a.t', 'g.o.b.l.o.k', 'k.o.n.t.o.l', 'm.e.m.e.k', 'a.n.j.i.n.g', 'b.a.b.i', 'b.a.n.g.s.a.t', 'g.o.b.l.o.k', 'k.o.n.t.o.l', 'm.e.m.e.k',
'n.g.e.n.t.o.t', 'p.e.l.e.r', 't.a.i', 't.o.l.o.l', 'j.a.n.c.o.k', 'j.e.m.b.u.t', 'n.g.e.n.t.o.t', 'p.e.l.e.r', 't.a.i', 't.o.l.o.l', 'j.a.n.c.o.k', 'j.e.m.b.u.t',
'k.a.m.p.r.e.t', 's.i.a.l.a.n', 'w.i.b.u', 'k.a.m.p.r.e.t', 's.i.a.l.a.n', 'w.i.b.u', 'c.i.c.i.n.g',
// 📱 Variasi dengan Angka & Simbol // 📱 Variasi dengan Angka & Simbol
'4nj1ng', 'b4b1', 'b4ngs4t', 'g0bl0k', 'k0nt0l', 'm3m3k', 'ng3nt0t', 'p3l3r', '4nj1ng', 'b4b1', 'b4ngs4t', 'g0bl0k', 'k0nt0l', 'm3m3k', 'ng3nt0t', 'p3l3r',
@@ -43,8 +42,6 @@ const badWordsIndonesia = [
'p3l3rr', 't4ii', 't0l0ll', 'j4nc0kk', 'j3mbutt', 'k4mpr3tt', 's14l4nn', 'w1buu', 'p3l3rr', 't4ii', 't0l0ll', 'j4nc0kk', 'j3mbutt', 'k4mpr3tt', 's14l4nn', 'w1buu',
// 🗣️ Kata yang Sering Digunakan dalam Konteks Negatif // 🗣️ Kata yang Sering Digunakan dalam Konteks Negatif
'dasar', 'kamu', 'kau', 'lu', 'lo', 'gue', 'gua', 'kita', 'kami', 'mereka',
'dasarr', 'kamuu', 'kauu', 'luu', 'loo', 'guee', 'guua', 'kitaa', 'kamii', 'merekaa',
'dasar bodoh', 'dasar goblok', 'dasar bangsat', 'dasar idiot', 'dasar sialan', 'dasar bodoh', 'dasar goblok', 'dasar bangsat', 'dasar idiot', 'dasar sialan',
'dasar bego', 'dasar dungu', 'dasar edan', 'dasar gila', 'dasar sinting', 'dasar bego', 'dasar dungu', 'dasar edan', 'dasar gila', 'dasar sinting',
'dasar pemalas', 'dasar pengecut', 'dasar penipu', 'dasar najis', 'dasar kampret', 'dasar pemalas', 'dasar pengecut', 'dasar penipu', 'dasar najis', 'dasar kampret',