fixing
This commit is contained in:
commit
6490679901
|
|
@ -26,12 +26,13 @@ import {
|
|||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
AdministrationLevelList,
|
||||
checkRolePlacementsAvailability,
|
||||
getListCompetencies,
|
||||
getListExperiences,
|
||||
saveUserInternal,
|
||||
saveUserRolePlacements,
|
||||
} from "@/service/management-user/management-user";
|
||||
import { loading } from "@/config/swal";
|
||||
import { error, loading } from "@/config/swal";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
const FormSchema = z.object({
|
||||
|
|
@ -134,6 +135,26 @@ export default function AddExpertForm() {
|
|||
};
|
||||
|
||||
loading();
|
||||
|
||||
// check availability first
|
||||
var placementArr: any[] = [];
|
||||
placementRows.forEach((row: any) => {
|
||||
placementArr.push({
|
||||
roleId: Number(row.roleId),
|
||||
userLevelId: Number(row.userLevelId),
|
||||
});
|
||||
});
|
||||
|
||||
const dataReqAvail = {
|
||||
placements: placementArr,
|
||||
};
|
||||
const resAvail = await checkRolePlacementsAvailability(dataReqAvail);
|
||||
if (resAvail?.error) {
|
||||
close();
|
||||
error(resAvail.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const res = await saveUserInternal(dataReq);
|
||||
const resData = res?.data?.data;
|
||||
const userProfileId = resData?.id;
|
||||
|
|
|
|||
|
|
@ -49,11 +49,11 @@ const columns: ColumnDef<any>[] = [
|
|||
header: "Jumlah Amplifikasi",
|
||||
cell: ({ row }) => <span>{row.getValue("link")}</span>,
|
||||
},
|
||||
// {
|
||||
// accessorKey: "status",
|
||||
// header: "Status",
|
||||
// cell: ({ row }) => <span>{row.getValue("status")}</span>,
|
||||
// },
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({ row }) => <span>{row.getValue("status")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "date",
|
||||
header: "Tanggal Penarikan",
|
||||
|
|
@ -77,12 +77,12 @@ const columns: ColumnDef<any>[] = [
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<Link
|
||||
href={`/admin/media-tracking/detail/${row.original.id}`}
|
||||
>
|
||||
<Link href={`/admin/media-tracking/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" />
|
||||
Detail
|
||||
View
|
||||
{row.original.mediaUpload.fileType.secondaryName &&
|
||||
row.original.mediaUpload.fileType.secondaryName.toLowerCase()}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuContent>
|
||||
|
|
|
|||
|
|
@ -58,16 +58,6 @@ const columns: ColumnDef<any>[] = [
|
|||
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "isStaticBanner",
|
||||
header: "Static Banner",
|
||||
cell: ({ row }) => (
|
||||
<StaticToogle
|
||||
id={row.original.id}
|
||||
initChecked={row.original.isStaticBanner}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "statusName",
|
||||
header: "Status Banner",
|
||||
|
|
@ -75,7 +65,6 @@ const columns: ColumnDef<any>[] = [
|
|||
<StatusToogle id={row.original.id} initChecked={row.original.isBanner} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ import CustomPagination from "@/components/table/custom-pagination";
|
|||
const ContentListBanner = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
const [showData, setShowData] = React.useState("9");
|
||||
const [categories, setCategories] = React.useState<any>();
|
||||
const [data, setData] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
|
|
@ -412,7 +412,7 @@ const ContentListBanner = () => {
|
|||
/>
|
||||
</div>
|
||||
<img
|
||||
src={item.thumbnailLink || "/placeholder.jpg"}
|
||||
src={item.smallThumbnailLink || "/placeholder.jpg"}
|
||||
alt={item.title}
|
||||
className="w-full h-48 object-cover"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default function AdminBanner() {
|
|||
: "bg-white text-black "
|
||||
}`}
|
||||
>
|
||||
Kontent
|
||||
Konten
|
||||
</Button>
|
||||
<Button
|
||||
rounded="md"
|
||||
|
|
|
|||
|
|
@ -67,6 +67,16 @@ const columns: ColumnDef<any>[] = [
|
|||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "created",
|
||||
accessorKey: "created",
|
||||
header: "Dibuat Oleh",
|
||||
cell: ({ row }) => (
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-sm text-gray-500">{row.original.createdByName}</span><span className="text-xs font-bold">({row.original.createdByUserLevelName})</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
|
|
|
|||
|
|
@ -58,24 +58,13 @@ const columns: ColumnDef<any>[] = [
|
|||
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "isStaticBanner",
|
||||
header: "Static Banner",
|
||||
cell: ({ row }) => (
|
||||
<StaticToogle
|
||||
id={row.original.id}
|
||||
initChecked={row.original.isStaticBanner}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "statusName",
|
||||
header: "Status Banner",
|
||||
header: "Status Pop Up",
|
||||
cell: ({ row }) => (
|
||||
<StatusToogle id={row.original.id} initChecked={row.original.isBanner} />
|
||||
<StatusToogle id={row.original.id} initChecked={row.original.isInterstitial} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { setBanner } from "@/service/settings/settings";
|
||||
import { setBanner, setPopUp } from "@/service/settings/settings";
|
||||
|
||||
export default function StatusToogle(props: {
|
||||
id: number;
|
||||
|
|
@ -12,7 +12,7 @@ export default function StatusToogle(props: {
|
|||
const router = useRouter();
|
||||
|
||||
const disableBanner = async () => {
|
||||
const response = await setBanner(id, false);
|
||||
const response = await setPopUp(id, false);
|
||||
|
||||
if (response?.error) {
|
||||
toast({
|
||||
|
|
@ -25,7 +25,7 @@ export default function StatusToogle(props: {
|
|||
toast({
|
||||
title: "Success ",
|
||||
});
|
||||
router.push("/admin/settings/banner?dataChange=true");
|
||||
router.push("/admin/settings/popup?dataChange=true");
|
||||
};
|
||||
return (
|
||||
<Switch
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ import CustomPagination from "@/components/table/custom-pagination";
|
|||
const ContentListPopUp = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
const [showData, setShowData] = React.useState("9");
|
||||
const [categories, setCategories] = React.useState<any>();
|
||||
const [data, setData] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
|
|
@ -433,7 +433,7 @@ const ContentListPopUp = () => {
|
|||
/>
|
||||
</div>
|
||||
<img
|
||||
src={item.thumbnailLink || "/placeholder.jpg"}
|
||||
src={item.smallThumbnailLink || "/placeholder.jpg"}
|
||||
alt={item.title}
|
||||
className="w-full h-48 object-cover"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default function AdminPopup() {
|
|||
: "bg-white text-black "
|
||||
}`}
|
||||
>
|
||||
Kontent
|
||||
Konten
|
||||
</Button>
|
||||
<Button
|
||||
rounded="md"
|
||||
|
|
|
|||
|
|
@ -9,12 +9,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Label } from "@/components/ui/label";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Book,
|
||||
CheckCheck,
|
||||
Plus,
|
||||
Timer,
|
||||
} from "lucide-react";
|
||||
import { Book, CheckCheck, Plus, Timer } from "lucide-react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { EventContentArg } from "@fullcalendar/core";
|
||||
import EventModal from "./event-modal";
|
||||
|
|
@ -124,7 +119,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const t = useTranslations("CalendarApp");
|
||||
|
||||
|
||||
// Modal states
|
||||
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
||||
const [date, setDate] = useState<Date>(new Date());
|
||||
|
|
@ -171,7 +166,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const monthData = res?.data?.data;
|
||||
if (monthData) {
|
||||
const allEvents: CalendarEvent[] = [];
|
||||
// Map API data to the calendarEvents structure
|
||||
// Map API data to the calendarEvents structure
|
||||
const events = monthData?.map((event: any) => ({
|
||||
id: event.id.toString(),
|
||||
title: event.title,
|
||||
|
|
@ -207,7 +202,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
"",
|
||||
""
|
||||
);
|
||||
|
||||
|
||||
if (res?.error) {
|
||||
error(res?.message || "Failed to fetch yearly events");
|
||||
return;
|
||||
|
|
@ -230,17 +225,20 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const filteredEvents = calendarEvents.filter((event) => {
|
||||
if (!selectedCategory.length) return false;
|
||||
console.log("Event category : ", selectedCategory);
|
||||
|
||||
|
||||
const eventCategories = event.extendedProps.calendar
|
||||
?.split(",")
|
||||
.map((val: string) => val.trim());
|
||||
|
||||
const allCategoryId = ["1", "2", "3", "4", "5"];
|
||||
// Cek apakah SEMUA validTypeIds ada di typeIdsInData
|
||||
const hasAllCategories = allCategoryId.every(categoryId => selectedCategory.includes(categoryId));
|
||||
const hasAllCategories = allCategoryId.every((categoryId) =>
|
||||
selectedCategory.includes(categoryId)
|
||||
);
|
||||
|
||||
return eventCategories?.some((cat: string) =>
|
||||
selectedCategory.includes(cat) || (hasAllCategories && cat == "0")
|
||||
return eventCategories?.some(
|
||||
(cat: string) =>
|
||||
selectedCategory.includes(cat) || (hasAllCategories && cat == "0")
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -266,7 +264,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
};
|
||||
|
||||
const handleCategorySelection = (category: string) => {
|
||||
setSelectedCategory(prev => {
|
||||
setSelectedCategory((prev) => {
|
||||
if (prev.includes(category)) {
|
||||
return prev.filter((c) => c !== category);
|
||||
} else {
|
||||
|
|
@ -313,7 +311,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const getEventColor = (type: EventType): string => {
|
||||
const typeSplit = type.split(",");
|
||||
const firstType = typeSplit[0] as EventType;
|
||||
|
||||
|
||||
const colors: Record<EventType, string> = {
|
||||
"0": "bg-black",
|
||||
"1": "bg-yellow-500",
|
||||
|
|
@ -322,7 +320,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
"4": "bg-orange-500",
|
||||
"5": "bg-green-400",
|
||||
};
|
||||
|
||||
|
||||
return colors[firstType] || "bg-gray-400";
|
||||
};
|
||||
|
||||
|
|
@ -346,15 +344,22 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
|
||||
const renderEventContent = (eventInfo: any) => {
|
||||
const { title } = eventInfo.event;
|
||||
const { createdByName, isPublish, calendar } = eventInfo.event.extendedProps;
|
||||
const { createdByName, isPublish, calendar } =
|
||||
eventInfo.event.extendedProps;
|
||||
const bgColor = getEventColor(calendar);
|
||||
const colorList = getEventColorList(calendar);
|
||||
|
||||
return (
|
||||
<div className={`w-full p-2 mb-2 rounded-md text-white text-sm flex justify-between items-stretch ${bgColor}`}>
|
||||
<div
|
||||
className={`w-full p-2 mb-2 rounded-md text-white text-sm flex justify-between items-stretch ${bgColor}`}
|
||||
>
|
||||
<div className="flex-1">
|
||||
<div className="flex flex-row items-center">
|
||||
{isPublish === true ? <CheckCheck size={15} /> : <Timer size={15} />}
|
||||
{isPublish === true ? (
|
||||
<CheckCheck size={15} />
|
||||
) : (
|
||||
<Timer size={15} />
|
||||
)}
|
||||
<p className="ml-1">{title}</p>
|
||||
</div>
|
||||
<p className="ml-1 text-xs text-start mt-2">
|
||||
|
|
@ -386,7 +391,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
bgColor,
|
||||
colorList,
|
||||
}) => (
|
||||
<div
|
||||
<div
|
||||
className={`w-full p-2 mb-2 rounded-md text-white text-sm flex justify-between items-stretch cursor-pointer hover:opacity-80 ${bgColor}`}
|
||||
onClick={() => handleClickListItem(item)}
|
||||
>
|
||||
|
|
@ -461,8 +466,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
text={event.title}
|
||||
createdBy={event.createdByName}
|
||||
isPublish={event.isPublish}
|
||||
bgColor={getEventColor(event.agendaType as EventType)}
|
||||
colorList={getEventColorList(event.agendaType as EventType)}
|
||||
bgColor={getEventColor(
|
||||
event.agendaType as EventType
|
||||
)}
|
||||
colorList={getEventColorList(
|
||||
event.agendaType as EventType
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -537,7 +546,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
{t("addEvent")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
{roleId === 3 && userLevelId === 216 && (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
|
|
@ -593,7 +602,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
className={category.className}
|
||||
id={category.label}
|
||||
checked={selectedCategory.includes(category.value)}
|
||||
onCheckedChange={() => handleCategorySelection(category.value)}
|
||||
onCheckedChange={() =>
|
||||
handleCategorySelection(category.value)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={category.label}>{category.label}</Label>
|
||||
</li>
|
||||
|
|
@ -605,7 +616,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
<Card className="col-span-12 lg:col-span-8 2xl:col-span-9 pt-5">
|
||||
<CardContent className="dashcode-app-calendar">
|
||||
<FullCalendar
|
||||
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin]}
|
||||
plugins={[
|
||||
dayGridPlugin,
|
||||
timeGridPlugin,
|
||||
interactionPlugin,
|
||||
listPlugin,
|
||||
]}
|
||||
headerToolbar={{
|
||||
left: "prev,next today",
|
||||
center: "title",
|
||||
|
|
@ -636,7 +652,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
handleDateChange(info.view.currentStart, info.view.currentEnd);
|
||||
handleViewChange(info.view.type);
|
||||
}}
|
||||
viewClassNames={activeView === "listYear" ? "hide-calendar-grid" : ""}
|
||||
viewClassNames={
|
||||
activeView === "listYear" ? "hide-calendar-grid" : ""
|
||||
}
|
||||
/>
|
||||
|
||||
{activeView === "listYear" && (
|
||||
|
|
@ -657,7 +675,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
|
||||
<EventModal
|
||||
open={sheetOpen}
|
||||
onClose={handleCloseModal}
|
||||
|
|
@ -669,4 +687,4 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default CalendarView;
|
||||
export default CalendarView;
|
||||
|
|
|
|||
|
|
@ -148,10 +148,12 @@ const EventModal = ({
|
|||
satker: false,
|
||||
international: false,
|
||||
});
|
||||
|
||||
const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType
|
||||
const [selectedPolda, setSelectedPolda] = React.useState([]); // Untuk data Polda
|
||||
const [selectedSatker, setSelectedSatker] = React.useState([]);
|
||||
const [selectedPolres, setSelectedPolres] = React.useState([]);
|
||||
const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
|
||||
const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
|
||||
const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
|
||||
const isDetailMode = true;
|
||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
|
|
@ -174,10 +176,7 @@ const EventModal = ({
|
|||
const detail = res?.data?.data;
|
||||
setDetailData(detail);
|
||||
|
||||
const description = res?.data?.data?.description;
|
||||
console.log("description", res?.data?.data?.description);
|
||||
|
||||
// Set nilai awal description ke form control
|
||||
const description = detail?.description;
|
||||
if (description) {
|
||||
setValue("description", description);
|
||||
}
|
||||
|
|
@ -196,17 +195,56 @@ const EventModal = ({
|
|||
attachments?.filter((file: any) => file.fileTypeId == 4)
|
||||
);
|
||||
|
||||
const agendaType = detail?.agendaType;
|
||||
setWilayahPublish({
|
||||
semua: agendaType === "all",
|
||||
nasional: agendaType === "mabes",
|
||||
polda: agendaType === "polda",
|
||||
polres: agendaType === "polres",
|
||||
satker: agendaType === "satker",
|
||||
international: agendaType === "international",
|
||||
});
|
||||
}
|
||||
const rawAgendaTypes = detail?.agendaType?.split(",") || []; // ["0","1","2","3","4","5"]
|
||||
const assignedToLevel = detail?.assignedToLevel?.split(",") || [];
|
||||
|
||||
const wilayahState = {
|
||||
semua: false,
|
||||
nasional: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
satker: false,
|
||||
international: false,
|
||||
};
|
||||
|
||||
rawAgendaTypes.forEach((type: any) => {
|
||||
switch (type) {
|
||||
case "0":
|
||||
wilayahState.semua = true;
|
||||
break;
|
||||
case "1":
|
||||
wilayahState.nasional = true;
|
||||
break;
|
||||
case "2":
|
||||
wilayahState.polda = true;
|
||||
break;
|
||||
case "3":
|
||||
wilayahState.polres = true;
|
||||
break;
|
||||
case "4":
|
||||
wilayahState.satker = true;
|
||||
break;
|
||||
case "5":
|
||||
wilayahState.international = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
setWilayahPublish(wilayahState);
|
||||
|
||||
// Atur unit berdasarkan agendaType
|
||||
if (rawAgendaTypes.includes("2")) {
|
||||
setSelectedPolda(assignedToLevel);
|
||||
}
|
||||
if (rawAgendaTypes.includes("3")) {
|
||||
setSelectedPolres(assignedToLevel);
|
||||
}
|
||||
if (rawAgendaTypes.includes("4")) {
|
||||
setSelectedSatker(assignedToLevel);
|
||||
}
|
||||
}
|
||||
fetchDetailData();
|
||||
}, [event, setValue]);
|
||||
|
||||
|
|
@ -233,33 +271,39 @@ const EventModal = ({
|
|||
|
||||
const toggleWilayah = (key: string) => {
|
||||
setWilayahPublish((prev: any) => {
|
||||
const newState = { ...prev, [key]: !prev[key] };
|
||||
let newState = { ...prev };
|
||||
|
||||
// Handle "semua" logic to check all options
|
||||
if (key === "semua" && newState.semua) {
|
||||
setAgendaType("all");
|
||||
return {
|
||||
semua: true,
|
||||
nasional: true,
|
||||
polda: true,
|
||||
polres: true,
|
||||
satker: true,
|
||||
international: true,
|
||||
// Jika key === semua dan sebelumnya belum aktif, aktifkan semua
|
||||
if (key === "semua") {
|
||||
const newChecked = !prev.semua;
|
||||
newState = {
|
||||
semua: newChecked,
|
||||
nasional: newChecked,
|
||||
polda: newChecked,
|
||||
polres: newChecked,
|
||||
satker: newChecked,
|
||||
international: newChecked,
|
||||
};
|
||||
|
||||
if (newChecked) {
|
||||
setAgendaType("0,1,2,3,4,5");
|
||||
} else {
|
||||
setAgendaType("");
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
// Uncheck "semua" if any other option is selected
|
||||
if (key !== "semua") {
|
||||
newState.semua = false;
|
||||
}
|
||||
// Jika key bukan "semua"
|
||||
newState[key] = !prev[key];
|
||||
newState.semua = false; // Uncheck "semua" jika yang dipilih adalah individu
|
||||
|
||||
// Set agendaType based on the selected checkbox
|
||||
if (newState.nasional) setAgendaType("mabes");
|
||||
else if (newState.polda) setAgendaType("polda");
|
||||
else if (newState.polres) setAgendaType("polres");
|
||||
else if (newState.satker) setAgendaType("satker");
|
||||
else if (newState.international) setAgendaType("international");
|
||||
else setAgendaType(""); // Reset if no checkbox is selected
|
||||
// Hitung ulang agendaType berdasarkan pilihan
|
||||
const selectedKeys = Object.entries(newState)
|
||||
.filter(([k, v]) => v && k !== "semua")
|
||||
.map(([k]) => wilayahValueMap[k]);
|
||||
|
||||
setAgendaType(selectedKeys.join(","));
|
||||
|
||||
return newState;
|
||||
});
|
||||
|
|
@ -269,12 +313,25 @@ const EventModal = ({
|
|||
const agendaTypeList: string[] = [];
|
||||
const assignedToLevelList: string[] = [];
|
||||
|
||||
// Mapping dari checkbox wilayah ke agendaType
|
||||
Object.keys(wilayahPublish).forEach((key) => {
|
||||
if (wilayahPublish[key as keyof typeof wilayahPublish]) {
|
||||
agendaTypeList.push(wilayahValueMap[key]);
|
||||
}
|
||||
});
|
||||
// // Mapping dari checkbox wilayah ke agendaType
|
||||
// Object.keys(wilayahPublish).forEach((key) => {
|
||||
// if (wilayahPublish[key as keyof typeof wilayahPublish]) {
|
||||
// agendaTypeList.push(wilayahValueMap[key]);
|
||||
// }
|
||||
// });
|
||||
|
||||
if (wilayahPublish.semua) {
|
||||
agendaTypeList.push("0", "1", "2", "3", "4", "5");
|
||||
} else {
|
||||
Object.keys(wilayahPublish).forEach((key) => {
|
||||
if (
|
||||
wilayahPublish[key as keyof typeof wilayahPublish] &&
|
||||
key !== "semua"
|
||||
) {
|
||||
agendaTypeList.push(wilayahValueMap[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unit-unit berdasarkan wilayah yang aktif
|
||||
if (wilayahPublish.polda && selectedPolda.length > 0) {
|
||||
|
|
@ -558,6 +615,7 @@ const EventModal = ({
|
|||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
router.push(redirect);
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -784,7 +842,8 @@ const EventModal = ({
|
|||
{wilayahPublish.polda && (
|
||||
<UnitMapping
|
||||
unit="Polda"
|
||||
isDetail={false}
|
||||
isDetail={isDetailMode} // jika Anda punya kondisi detail
|
||||
initData={selectedPolda}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolda(data)
|
||||
}
|
||||
|
|
@ -802,8 +861,9 @@ const EventModal = ({
|
|||
</label>
|
||||
{wilayahPublish.polres && (
|
||||
<UnitMapping
|
||||
isDetail={false}
|
||||
unit="Polres"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedPolres}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolres(data)
|
||||
}
|
||||
|
|
@ -821,8 +881,9 @@ const EventModal = ({
|
|||
</label>
|
||||
{wilayahPublish.satker && (
|
||||
<UnitMapping
|
||||
isDetail={false}
|
||||
unit="Satker"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedSatker}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedSatker(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,14 +97,23 @@ const useTableColumns = () => {
|
|||
cell: ({ row }) => {
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const creatorGroupParentLevelId = row.original.creatorGroupParentLevelId;
|
||||
|
||||
let displayText = "-";
|
||||
if (isPublish && !isPublishOnPolda) {
|
||||
displayText = "Mabes";
|
||||
} else if (isPublish && isPublishOnPolda) {
|
||||
displayText = "Mabes & Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Mabes & Satker";
|
||||
} else {
|
||||
displayText = "Mabes & Polda";
|
||||
}
|
||||
} else if (!isPublish && isPublishOnPolda) {
|
||||
displayText = "Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Satker";
|
||||
} else {
|
||||
displayText = "Polda";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -100,14 +100,23 @@ const useTableColumns = () => {
|
|||
cell: ({ row }) => {
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const creatorGroupParentLevelId = row.original.creatorGroupParentLevelId;
|
||||
|
||||
let displayText = "-";
|
||||
if (isPublish && !isPublishOnPolda) {
|
||||
displayText = "Mabes";
|
||||
} else if (isPublish && isPublishOnPolda) {
|
||||
displayText = "Mabes & Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Mabes & Satker";
|
||||
} else {
|
||||
displayText = "Mabes & Polda";
|
||||
}
|
||||
} else if (!isPublish && isPublishOnPolda) {
|
||||
displayText = "Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Satker";
|
||||
} else {
|
||||
displayText = "Polda";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -97,14 +97,23 @@ const useTableColumns = () => {
|
|||
cell: ({ row }) => {
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const creatorGroupParentLevelId = row.original.creatorGroupParentLevelId;
|
||||
|
||||
let displayText = "-";
|
||||
if (isPublish && !isPublishOnPolda) {
|
||||
displayText = "Mabes";
|
||||
} else if (isPublish && isPublishOnPolda) {
|
||||
displayText = "Mabes & Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Mabes & Satker";
|
||||
} else {
|
||||
displayText = "Mabes & Polda";
|
||||
}
|
||||
} else if (!isPublish && isPublishOnPolda) {
|
||||
displayText = "Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Satker";
|
||||
} else {
|
||||
displayText = "Polda";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -97,14 +97,23 @@ const useTableColumns = () => {
|
|||
cell: ({ row }) => {
|
||||
const isPublish = row.original.isPublish;
|
||||
const isPublishOnPolda = row.original.isPublishOnPolda;
|
||||
const creatorGroupParentLevelId = row.original.creatorGroupParentLevelId;
|
||||
|
||||
let displayText = "-";
|
||||
if (isPublish && !isPublishOnPolda) {
|
||||
displayText = "Mabes";
|
||||
} else if (isPublish && isPublishOnPolda) {
|
||||
displayText = "Mabes & Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Mabes & Satker";
|
||||
} else {
|
||||
displayText = "Mabes & Polda";
|
||||
}
|
||||
} else if (!isPublish && isPublishOnPolda) {
|
||||
displayText = "Polda";
|
||||
if (Number(creatorGroupParentLevelId) == 761) {
|
||||
displayText = "Satker";
|
||||
} else {
|
||||
displayText = "Polda";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { Link, useRouter } from "@/components/navigation";
|
|||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { deleteBlog } from "@/service/blog/blog";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
import { error, loading, close } from "@/lib/swal";
|
||||
import { useTranslations } from "next-intl";
|
||||
import axios from "axios";
|
||||
|
||||
|
|
@ -126,6 +126,7 @@ const useTableColumns = ({
|
|||
|
||||
const handleDownload = async (id: string) => {
|
||||
try {
|
||||
loading();
|
||||
const response = await axios.get(
|
||||
`https://netidhub.com/api/media/report/download?id=${id}`,
|
||||
{
|
||||
|
|
@ -140,6 +141,7 @@ const useTableColumns = ({
|
|||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Download failed", error);
|
||||
MySwal.fire({
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export default function ExecutiveDataDashboard() {
|
|||
const state = Cookies.get("state");
|
||||
const provState = Cookies.get("state-prov");
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
const [refreshTicket, setRefreshTicket] = useState(true);
|
||||
|
||||
const [ticket1, setTicket1] = useState("");
|
||||
const [ticket2, setTicket2] = useState("");
|
||||
|
|
@ -44,6 +45,7 @@ export default function ExecutiveDataDashboard() {
|
|||
const [ticket4, setTicket4] = useState("");
|
||||
const [ticket5, setTicket5] = useState("");
|
||||
const [ticket6, setTicket6] = useState("");
|
||||
const [ticket7, setTicket7] = useState("");
|
||||
const [isInternational, setIsInternational] = useState([false, false, false]);
|
||||
|
||||
const baseUrl = "https://analytic.sitani.info/";
|
||||
|
|
@ -54,13 +56,40 @@ export default function ExecutiveDataDashboard() {
|
|||
const view1 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[0]
|
||||
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
|
||||
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=ALL"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=ALL"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`
|
||||
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`;
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`;
|
||||
|
||||
const view2 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-content-interaction-per-satker?polda-selected=ALL"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-satker?polda-selected=ALL"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-satker?polda-selected=SATKER POLRI&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-satker?polda-selected=SATKER POLRI&`;
|
||||
|
||||
const view3 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-content-category-per-polda-new?polda-selected=ALL"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-polda-new?polda-selected=ALL"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-polda-new?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-polda-new?polda-selected=${state}&`;
|
||||
|
||||
const view4 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=ALL"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=ALL"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=SATKER POLRI&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=SATKER POLRI&`;
|
||||
|
||||
const view5 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
|
||||
|
|
@ -69,7 +98,7 @@ export default function ExecutiveDataDashboard() {
|
|||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`;
|
||||
|
||||
const view3 =
|
||||
const view6 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?"
|
||||
|
|
@ -78,59 +107,14 @@ export default function ExecutiveDataDashboard() {
|
|||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`;
|
||||
|
||||
const view4 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`;
|
||||
|
||||
const view5 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`;
|
||||
|
||||
const view6 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[1]
|
||||
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`;
|
||||
|
||||
const view7 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-penugasan?"
|
||||
: "views/2023_09_db-penugasan_rev100/db-penugasan?"
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-penugasan-vertical-bar?"
|
||||
: "views/2023_09_db-penugasan_rev100/db-penugasan-vertical-bar?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`
|
||||
: `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`;
|
||||
|
||||
const view8 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[2]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori-top10?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda-new?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda-new?polda-selected=${state}&`;
|
||||
|
||||
const view9 =
|
||||
levelName == "MABES POLRI"
|
||||
? isInternational[3]
|
||||
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori?"
|
||||
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori?"
|
||||
: safeLevelName.includes("POLDA")
|
||||
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`
|
||||
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`;
|
||||
? `views/2023_09_db-penugasan_rev100/db-penugasan-vertical-bar?polda-selected=${state}&`
|
||||
: `views/2023_09_db-penugasan_rev100/db-penugasan-vertical-bar?polda-selected=${state}&`;
|
||||
|
||||
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
|
||||
|
||||
|
|
@ -153,10 +137,13 @@ export default function ExecutiveDataDashboard() {
|
|||
|
||||
const response6 = await generateTicket();
|
||||
setTicket6(response6?.data?.data);
|
||||
|
||||
const response7 = await generateTicket();
|
||||
setTicket7(response7?.data?.data);
|
||||
}
|
||||
|
||||
initState();
|
||||
}, [isInternational]);
|
||||
}, [isInternational, refreshTicket]);
|
||||
|
||||
// Hooks
|
||||
useEffect(() => {
|
||||
|
|
@ -178,7 +165,7 @@ export default function ExecutiveDataDashboard() {
|
|||
<SiteBreadcrumb />
|
||||
<div>
|
||||
<div className="my-3">
|
||||
<Tabs defaultValue="content-publish" className="w-full">
|
||||
<Tabs defaultValue="content-publish" className="w-full" onValueChange={() => setRefreshTicket(!refreshTicket)}>
|
||||
<TabsList className="flex-wrap bg-black">
|
||||
<TabsTrigger
|
||||
value="content-publish"
|
||||
|
|
@ -217,19 +204,17 @@ export default function ExecutiveDataDashboard() {
|
|||
{/* Polda */}
|
||||
{(levelNumber === "1" || levelNumber === "2") && (
|
||||
<Card
|
||||
className={`rounded-sm p-3 ${
|
||||
levelNumber === "2" ? "w-full" : "w-full"
|
||||
}`}
|
||||
className={`rounded-sm p-3 w-full`}
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
{/* <div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Polda
|
||||
</p>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="my-5">
|
||||
{ticket1 == "w-full" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view4 + param}`}
|
||||
src={`${baseUrl + view1 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
className="w-full"
|
||||
|
|
@ -237,7 +222,7 @@ export default function ExecutiveDataDashboard() {
|
|||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${url + ticket1}/${view4}${param}`}
|
||||
src={`${url + ticket1}/${view1}${param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
|
|
@ -248,60 +233,26 @@ export default function ExecutiveDataDashboard() {
|
|||
)}
|
||||
|
||||
{/* Satker */}
|
||||
{(levelNumber === "1" || levelNumber === "3") && (
|
||||
{(levelNumber === "1") && (
|
||||
<Card
|
||||
className={`rounded-sm p-3 ${
|
||||
levelNumber === "3" ? "w-full" : "w-full"
|
||||
}`}
|
||||
className={`rounded-sm p-3 w-full`}
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
{/* <div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Satker
|
||||
</p>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="my-5">
|
||||
{ticket2 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view6 + param}`}
|
||||
src={`${baseUrl + view2 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${url + ticket2}/${view6}${param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Polres */}
|
||||
{(levelNumber === "1" || levelNumber === "2") && (
|
||||
<Card
|
||||
className={`rounded-sm p-3 ${
|
||||
levelNumber === "2" ? "w-full" : "w-full"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Upload konten hari ini Polres
|
||||
</p>
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket3 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view5 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${url + ticket3}/${view5}${param}`}
|
||||
src={`${url + ticket2}/${view2}${param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
|
|
@ -313,82 +264,63 @@ export default function ExecutiveDataDashboard() {
|
|||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="content-category">
|
||||
<Card className="px-3 py-3">
|
||||
<p className="text-lg">
|
||||
<b>
|
||||
{isInternational[2]
|
||||
? "INTERACTION OF THE MOST POPULAR CATEGORIES AND TITLES"
|
||||
: "INTERAKSI KATEGORI DAN JUDUL TERPOPULER"}
|
||||
</b>
|
||||
</p>
|
||||
{levelName === "MABES POLRI" ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
<p>{t("choose_category")}</p>
|
||||
<div className="flex flex-row gap-1 border-2 w-fit">
|
||||
<Button
|
||||
onClick={() => handleInternational(2, false)}
|
||||
className={` hover:text-white rounded-none
|
||||
${
|
||||
isInternational[2]
|
||||
? "bg-white text-black "
|
||||
: "bg-black text-white "
|
||||
}`}
|
||||
>
|
||||
Indonesia
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleInternational(2, true)}
|
||||
className={`hover:text-white rounded-none ${
|
||||
isInternational[1]
|
||||
? "bg-black text-white "
|
||||
: "bg-white text-black "
|
||||
}
|
||||
`}
|
||||
>
|
||||
{t("international")}
|
||||
</Button>
|
||||
{(levelNumber === "1" || levelNumber === "2") && (
|
||||
<Card className="px-3 py-3">
|
||||
<div className="my-5">
|
||||
{ticket3 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view3 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket3}/${view3}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<div className="my-5">
|
||||
{ticket3 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view8 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket3}/${view8}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{levelNumber === "1" && (
|
||||
<Card className="px-3 py-3">
|
||||
<div className="my-5">
|
||||
{ticket4 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view4 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket4}/${view4}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="popular-content">
|
||||
<Card className="rounded-sm p-3 h-[750px]">
|
||||
<div className="flex flex-row justify-between">
|
||||
<p className="text-base font-semibold">
|
||||
Konten Paling Populer
|
||||
</p>
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket4 == "" ? (
|
||||
{ticket5 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view2 + param}`}
|
||||
src={`${baseUrl + view5 + param}`}
|
||||
width="100%"
|
||||
height="650"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket4}/${view2}${param}`}`}
|
||||
src={`${`${url + ticket5}/${view5}${param}`}`}
|
||||
width="100%"
|
||||
height="650"
|
||||
frameBorder="0"
|
||||
|
|
@ -400,22 +332,17 @@ export default function ExecutiveDataDashboard() {
|
|||
|
||||
<TabsContent value="heatmap">
|
||||
<Card className="rounded-sm p-3 h-[750px]">
|
||||
<div className="flex flex-row justify-between mx-3">
|
||||
<p className="text-base font-semibold">
|
||||
Heatmap Konten dan Kategori dengan Interaksi
|
||||
</p>
|
||||
</div>
|
||||
<div className="my-5">
|
||||
{ticket5 == "" ? (
|
||||
{ticket6 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view3 + param}`}
|
||||
src={`${baseUrl + view6 + param}`}
|
||||
width="100%"
|
||||
height="600"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket5}/${view3}${param}`}`}
|
||||
src={`${`${url + ticket6}/${view6}${param}`}`}
|
||||
width="100%"
|
||||
height="600"
|
||||
frameBorder="0"
|
||||
|
|
@ -425,60 +352,25 @@ export default function ExecutiveDataDashboard() {
|
|||
</Card>
|
||||
</TabsContent>
|
||||
<TabsContent value="task">
|
||||
<div className="grid grid-cols-12 gap-5">
|
||||
<div className="lg:col-span-12 col-span-12">
|
||||
<Card className="px-3 py-3">
|
||||
{levelName === "MABES POLRI" ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
<p>{t("choose_category")}</p>
|
||||
<div className="flex flex-row gap-1 border-2 w-fit">
|
||||
<Button
|
||||
onClick={() => handleInternational(2, false)}
|
||||
className={` hover:text-white rounded-none
|
||||
${
|
||||
isInternational[2]
|
||||
? "bg-white text-black "
|
||||
: "bg-black text-white "
|
||||
}`}
|
||||
>
|
||||
Indonesia
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleInternational(2, true)}
|
||||
className={`hover:text-white rounded-none ${
|
||||
isInternational[1]
|
||||
? "bg-black text-white "
|
||||
: "bg-white text-black "
|
||||
}
|
||||
`}
|
||||
>
|
||||
{t("international")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<div className="my-5">
|
||||
{ticket3 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view7 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket3}/${view7}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="rounded-sm p-3 h-[750px]">
|
||||
<div className="my-5">
|
||||
{ticket7 == "" ? (
|
||||
<iframe
|
||||
src={`${baseUrl + view7 + param}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${`${url + ticket7}/${view7}${param}`}`}
|
||||
width="100%"
|
||||
height="750"
|
||||
frameBorder="0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import { ChevronDownIcon } from "lucide-react";
|
|||
import { getOperatorUser } from "@/service/management-user/management-user";
|
||||
|
||||
const taskSchema = z.object({
|
||||
message: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ export default function FormQuestionsReply() {
|
|||
title: data.title,
|
||||
description: data.description,
|
||||
priorityId: selectedPriority,
|
||||
statusId: selectedStatus,
|
||||
statusId: 1,
|
||||
typeId: detailTickets?.typeId,
|
||||
parentCommentId: detailTickets?.feedId,
|
||||
operatorTeam: selectedOperator.join(","),
|
||||
|
|
@ -507,7 +507,7 @@ export default function FormQuestionsReply() {
|
|||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="message"
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
generateDataArticle,
|
||||
generateDataRewrite,
|
||||
getDetailArticle,
|
||||
getGenerateKeywords,
|
||||
getGenerateTitle,
|
||||
|
|
@ -55,6 +56,7 @@ import dynamic from "next/dynamic";
|
|||
import { getCsrfToken } from "@/service/auth";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -82,6 +84,10 @@ export default function FormAudio() {
|
|||
const router = useRouter();
|
||||
const editor = useRef(null);
|
||||
type AudioSchema = z.infer<typeof audioSchema>;
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
|
|
@ -97,6 +103,11 @@ export default function FormAudio() {
|
|||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
useState("professional");
|
||||
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
|
||||
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
|
||||
|
||||
const [selectedSEO, setSelectedSEO] = useState<string>("");
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||
|
|
@ -111,7 +122,6 @@ export default function FormAudio() {
|
|||
null
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
|
|
@ -133,6 +143,8 @@ export default function FormAudio() {
|
|||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
|
||||
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
|
||||
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [publishedFor, setPublishedFor] = useState<string[]>([]);
|
||||
|
||||
|
|
@ -155,15 +167,11 @@ export default function FormAudio() {
|
|||
|
||||
const audioSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
|
||||
.or(
|
||||
z.literal(articleBody || "").refine((val) => val.length > 0, {
|
||||
message: "Deskripsi diperlukan.",
|
||||
})
|
||||
),
|
||||
description: z.string().optional(),
|
||||
descriptionOri: z.string().optional(), // Original editor
|
||||
rewriteDescription: z.string().optional(), // Rewrite editor
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
|
|
@ -175,6 +183,11 @@ export default function FormAudio() {
|
|||
formState: { errors },
|
||||
} = useForm<AudioSchema>({
|
||||
resolver: zodResolver(audioSchema),
|
||||
defaultValues: {
|
||||
description: "",
|
||||
descriptionOri: "",
|
||||
rewriteDescription: "",
|
||||
},
|
||||
});
|
||||
|
||||
const doGenerateMainKeyword = async () => {
|
||||
|
|
@ -437,12 +450,24 @@ export default function FormAudio() {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (articleBody) {
|
||||
// Set ke dua field jika rewrite juga aktif
|
||||
setValue("description", articleBody);
|
||||
setValue("rewriteDescription", articleBody);
|
||||
}
|
||||
}, [articleBody, setValue]);
|
||||
|
||||
const save = async (data: AudioSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
const finalDescription = articleBody || data.description;
|
||||
if (!finalDescription.trim()) {
|
||||
const finalDescription = isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
}
|
||||
|
|
@ -719,6 +744,45 @@ export default function FormAudio() {
|
|||
}
|
||||
}, [title, getValues, setValue]);
|
||||
|
||||
const handleRewriteClick = async () => {
|
||||
setIsContentRewriteClicked(true);
|
||||
|
||||
const request = {
|
||||
style: selectedWritingStyle,
|
||||
lang: "id",
|
||||
contextType: "text",
|
||||
urlContext: null,
|
||||
context: editorContent, // Ambil isi editor original
|
||||
createdBy: roleId,
|
||||
sentiment: "Humorous",
|
||||
clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ",
|
||||
};
|
||||
|
||||
const res = await generateDataRewrite(request);
|
||||
close();
|
||||
|
||||
if (res?.error) {
|
||||
console.error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newArticleId = res?.data?.data?.id;
|
||||
setIsGeneratedArticle(true);
|
||||
|
||||
setArticleIds((prevIds: string[]) => {
|
||||
if (prevIds.length < 3) {
|
||||
return [...prevIds, newArticleId];
|
||||
} else {
|
||||
const updatedIds = [...prevIds];
|
||||
updatedIds[2] = newArticleId;
|
||||
return updatedIds;
|
||||
}
|
||||
});
|
||||
|
||||
Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds));
|
||||
setShowRewriteEditor(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col lg:flex-row gap-10">
|
||||
|
|
@ -861,7 +925,7 @@ export default function FormAudio() {
|
|||
placeholder="Enter Main Keyword"
|
||||
/>
|
||||
{/* )}
|
||||
/> */}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
|
|
@ -911,7 +975,7 @@ export default function FormAudio() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<Label>{t("special-instructions")} (Optional)</Label>
|
||||
<Label>{t("special-instructions")}(Optional)</Label>
|
||||
<div className="mt-3">
|
||||
<Controller
|
||||
control={control}
|
||||
|
|
@ -940,7 +1004,7 @@ export default function FormAudio() {
|
|||
</div>
|
||||
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
<div className="mt-3 pb-0 flex flex-row">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<p
|
||||
key={index}
|
||||
|
|
@ -960,50 +1024,155 @@ export default function FormAudio() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/audio/update-seo/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
>
|
||||
{t("update")}
|
||||
</Button>
|
||||
</Link>
|
||||
{t("update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">Loading Proses Data...</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody || value}
|
||||
{!isSwitchOn && (
|
||||
<>
|
||||
<RadioGroup
|
||||
onValueChange={(value) => setSelectedFileType(value)}
|
||||
value={selectedFileType}
|
||||
className=" grid-cols-1"
|
||||
>
|
||||
<div className="">
|
||||
<RadioGroupItem value="original" id="original-file" />
|
||||
<Label htmlFor="original-file">
|
||||
Select Original Description
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="descriptionOri"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={value}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-semibold">Content Rewrite</p>
|
||||
<div className="my-2">
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={handleRewriteClick}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Content Rewrite
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showRewriteEditor && (
|
||||
<div>
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<Button
|
||||
type="button"
|
||||
key={index}
|
||||
className={`mr-3 px-3 py-2 rounded-md ${
|
||||
selectedArticleId === id
|
||||
? "bg-green-500 text-white"
|
||||
: "border-2 border-green-500 bg-white text-green-500 hover:bg-green-500 hover:text-white hover:border-green-500"
|
||||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{"Narasi " + (index + 1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center space-x-2 mt-3">
|
||||
<RadioGroupItem value="rewrite" id="rewrite-file" />
|
||||
<Label htmlFor="rewrite-file">
|
||||
Select Description Rewrite
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("file-rewrite")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rewriteDescription"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setRewriteEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</>
|
||||
)}
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("select-file")}</Label>
|
||||
{/* <Input
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
generateDataArticle,
|
||||
generateDataRewrite,
|
||||
getDetailArticle,
|
||||
getGenerateKeywords,
|
||||
getGenerateTitle,
|
||||
|
|
@ -94,6 +95,7 @@ export default function FormImage() {
|
|||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
|
|
@ -116,7 +118,10 @@ export default function FormImage() {
|
|||
null
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
useState("professional");
|
||||
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
|
||||
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
|
|
@ -125,6 +130,8 @@ export default function FormImage() {
|
|||
|
||||
const [content, setContent] = useState("");
|
||||
|
||||
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState("");
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
|
|
@ -140,6 +147,7 @@ export default function FormImage() {
|
|||
let uploadPersen = 0;
|
||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||
const [counterProgress, setCounterProgress] = useState(0);
|
||||
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
|
||||
|
||||
const [files, setFiles] = useState<FileWithPreview[]>([]);
|
||||
const [filesTemp, setFilesTemp] = useState<File[]>([]);
|
||||
|
|
@ -164,16 +172,10 @@ export default function FormImage() {
|
|||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
|
||||
.or(
|
||||
z.literal(articleBody || "").refine((val) => val.length > 0, {
|
||||
message: "Deskripsi diperlukan.",
|
||||
})
|
||||
),
|
||||
description: z.string().optional(),
|
||||
descriptionOri: z.string().optional(),
|
||||
rewriteDescription: z.string().optional(),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -184,6 +186,11 @@ export default function FormImage() {
|
|||
formState: { errors },
|
||||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
defaultValues: {
|
||||
description: "",
|
||||
descriptionOri: "",
|
||||
rewriteDescription: "",
|
||||
},
|
||||
});
|
||||
|
||||
const doGenerateMainKeyword = async () => {
|
||||
|
|
@ -446,13 +453,25 @@ export default function FormImage() {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (articleBody) {
|
||||
setValue("description", articleBody);
|
||||
setValue("rewriteDescription", articleBody);
|
||||
}
|
||||
}, [articleBody, setValue]);
|
||||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
const finalDescription = articleBody || data.description;
|
||||
// const finalDescription = articleBody || data.description;
|
||||
const finalDescription = isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
|
||||
if (!finalDescription.trim()) {
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
}
|
||||
|
|
@ -716,6 +735,45 @@ export default function FormImage() {
|
|||
}
|
||||
}, [title, getValues, setValue]);
|
||||
|
||||
const handleRewriteClick = async () => {
|
||||
setIsContentRewriteClicked(true);
|
||||
|
||||
const request = {
|
||||
style: selectedWritingStyle,
|
||||
lang: "id",
|
||||
contextType: "text",
|
||||
urlContext: null,
|
||||
context: editorContent, // Ambil isi editor original
|
||||
createdBy: roleId,
|
||||
sentiment: "Humorous",
|
||||
clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ",
|
||||
};
|
||||
|
||||
const res = await generateDataRewrite(request);
|
||||
close();
|
||||
|
||||
if (res?.error) {
|
||||
console.error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newArticleId = res?.data?.data?.id;
|
||||
setIsGeneratedArticle(true);
|
||||
|
||||
setArticleIds((prevIds: string[]) => {
|
||||
if (prevIds.length < 3) {
|
||||
return [...prevIds, newArticleId];
|
||||
} else {
|
||||
const updatedIds = [...prevIds];
|
||||
updatedIds[2] = newArticleId;
|
||||
return updatedIds;
|
||||
}
|
||||
});
|
||||
|
||||
Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds));
|
||||
setShowRewriteEditor(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col lg:flex-row gap-10">
|
||||
|
|
@ -930,6 +988,7 @@ export default function FormImage() {
|
|||
color="primary"
|
||||
onClick={handleGenerateArtikel}
|
||||
size="sm"
|
||||
type="button"
|
||||
>
|
||||
Generate Article
|
||||
</Button>
|
||||
|
|
@ -972,32 +1031,139 @@ export default function FormImage() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">Loading Proses Data...</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody || value}
|
||||
{!isSwitchOn && (
|
||||
<>
|
||||
<RadioGroup
|
||||
onValueChange={(value) => setSelectedFileType(value)}
|
||||
value={selectedFileType}
|
||||
className=" grid-cols-1"
|
||||
>
|
||||
<div className="">
|
||||
<RadioGroupItem value="original" id="original-file" />
|
||||
<Label htmlFor="original-file">
|
||||
Select Original Description
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="descriptionOri"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={value}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-semibold">Content Rewrite</p>
|
||||
<div className="my-2">
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={handleRewriteClick}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Content Rewrite
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showRewriteEditor && (
|
||||
<div>
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<Button
|
||||
type="button"
|
||||
key={index}
|
||||
className={`mr-3 px-3 py-2 rounded-md ${
|
||||
selectedArticleId === id
|
||||
? "bg-green-500 text-white"
|
||||
: "border-2 border-green-500 bg-white text-green-500 hover:bg-green-500 hover:text-white hover:border-green-500"
|
||||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{"Narasi " + (index + 1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center space-x-2 mt-3">
|
||||
<RadioGroupItem value="rewrite" id="rewrite-file" />
|
||||
<Label htmlFor="rewrite-file">
|
||||
Select Description Rewrite
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("file-rewrite")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rewriteDescription"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setRewriteEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</>
|
||||
)}
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("select-file")}</Label>
|
||||
{/* <Input
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -16,7 +16,7 @@ import * as z from "zod";
|
|||
import { Upload } from "tus-js-client";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
import { redirect, useParams, useRouter } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
generateDataArticle,
|
||||
generateDataRewrite,
|
||||
getDetailArticle,
|
||||
getGenerateKeywords,
|
||||
getGenerateTitle,
|
||||
|
|
@ -83,11 +84,15 @@ export default function FormTeks() {
|
|||
const editor = useRef(null);
|
||||
type TeksSchema = z.infer<typeof teksSchema>;
|
||||
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
|
|
@ -102,6 +107,10 @@ export default function FormTeks() {
|
|||
const [editingArticleId, setEditingArticleId] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
useState("professional");
|
||||
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
|
||||
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
|
||||
|
||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
|
|
@ -109,8 +118,9 @@ export default function FormTeks() {
|
|||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
|
||||
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
|
|
@ -157,14 +167,9 @@ export default function FormTeks() {
|
|||
|
||||
const teksSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
|
||||
.or(
|
||||
z.literal(articleBody || "").refine((val) => val.length > 0, {
|
||||
message: "Deskripsi diperlukan.",
|
||||
})
|
||||
),
|
||||
description: z.string().optional(),
|
||||
descriptionOri: z.string().optional(), // Original editor
|
||||
rewriteDescription: z.string().optional(),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
|
@ -177,6 +182,11 @@ export default function FormTeks() {
|
|||
formState: { errors },
|
||||
} = useForm<TeksSchema>({
|
||||
resolver: zodResolver(teksSchema),
|
||||
defaultValues: {
|
||||
description: "",
|
||||
descriptionOri: "",
|
||||
rewriteDescription: "",
|
||||
},
|
||||
});
|
||||
|
||||
const doGenerateMainKeyword = async () => {
|
||||
|
|
@ -439,12 +449,26 @@ export default function FormTeks() {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (articleBody) {
|
||||
// Set ke dua field jika rewrite juga aktif
|
||||
setValue("description", articleBody);
|
||||
setValue("rewriteDescription", articleBody);
|
||||
}
|
||||
}, [articleBody, setValue]);
|
||||
|
||||
const save = async (data: TeksSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
const finalDescription = articleBody || data.description;
|
||||
if (!finalDescription.trim()) {
|
||||
|
||||
const finalDescription = isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
}
|
||||
|
|
@ -709,6 +733,45 @@ export default function FormTeks() {
|
|||
}
|
||||
}, [title, getValues, setValue]);
|
||||
|
||||
const handleRewriteClick = async () => {
|
||||
setIsContentRewriteClicked(true);
|
||||
|
||||
const request = {
|
||||
style: selectedWritingStyle,
|
||||
lang: "id",
|
||||
contextType: "text",
|
||||
urlContext: null,
|
||||
context: editorContent, // Ambil isi editor original
|
||||
createdBy: roleId,
|
||||
sentiment: "Humorous",
|
||||
clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ",
|
||||
};
|
||||
|
||||
const res = await generateDataRewrite(request);
|
||||
close();
|
||||
|
||||
if (res?.error) {
|
||||
console.error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newArticleId = res?.data?.data?.id;
|
||||
setIsGeneratedArticle(true);
|
||||
|
||||
setArticleIds((prevIds: string[]) => {
|
||||
if (prevIds.length < 3) {
|
||||
return [...prevIds, newArticleId];
|
||||
} else {
|
||||
const updatedIds = [...prevIds];
|
||||
updatedIds[2] = newArticleId;
|
||||
return updatedIds;
|
||||
}
|
||||
});
|
||||
|
||||
Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds));
|
||||
setShowRewriteEditor(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col lg:flex-row gap-10">
|
||||
|
|
@ -851,7 +914,7 @@ export default function FormTeks() {
|
|||
placeholder="Enter Main Keyword"
|
||||
/>
|
||||
{/* )}
|
||||
/> */}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
|
|
@ -901,7 +964,7 @@ export default function FormTeks() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<Label>{t("special-instructions")} (Optional)</Label>
|
||||
<Label>{t("special-instructions")}(Optional)</Label>
|
||||
<div className="mt-3">
|
||||
<Controller
|
||||
control={control}
|
||||
|
|
@ -923,13 +986,14 @@ export default function FormTeks() {
|
|||
color="primary"
|
||||
onClick={handleGenerateArtikel}
|
||||
size="sm"
|
||||
type="button"
|
||||
>
|
||||
Generate Article
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
<div className="mt-3 pb-0 flex flex-row">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<p
|
||||
key={index}
|
||||
|
|
@ -949,50 +1013,155 @@ export default function FormTeks() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/teks/update-seo/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
>
|
||||
{t("update")}
|
||||
</Button>
|
||||
</Link>
|
||||
{t("update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">Loading Proses Data...</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody || value}
|
||||
{!isSwitchOn && (
|
||||
<>
|
||||
<RadioGroup
|
||||
onValueChange={(value) => setSelectedFileType(value)}
|
||||
value={selectedFileType}
|
||||
className=" grid-cols-1"
|
||||
>
|
||||
<div className="">
|
||||
<RadioGroupItem value="original" id="original-file" />
|
||||
<Label htmlFor="original-file">
|
||||
Select Original Description
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="descriptionOri"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={value}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-semibold">Content Rewrite</p>
|
||||
<div className="my-2">
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={handleRewriteClick}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Content Rewrite
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showRewriteEditor && (
|
||||
<div>
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<Button
|
||||
type="button"
|
||||
key={index}
|
||||
className={`mr-3 px-3 py-2 rounded-md ${
|
||||
selectedArticleId === id
|
||||
? "bg-green-500 text-white"
|
||||
: "border-2 border-green-500 bg-white text-green-500 hover:bg-green-500 hover:text-white hover:border-green-500"
|
||||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{"Narasi " + (index + 1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center space-x-2 mt-3">
|
||||
<RadioGroupItem value="rewrite" id="rewrite-file" />
|
||||
<Label htmlFor="rewrite-file">
|
||||
Select Description Rewrite
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("file-rewrite")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rewriteDescription"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setRewriteEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</>
|
||||
)}
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("select-file")}</Label>
|
||||
{/* <Input
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import * as z from "zod";
|
|||
import { Upload } from "tus-js-client";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
import { redirect, useParams, useRouter } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
generateDataArticle,
|
||||
generateDataRewrite,
|
||||
getDetailArticle,
|
||||
getGenerateKeywords,
|
||||
getGenerateTitle,
|
||||
|
|
@ -82,12 +83,15 @@ export default function FormVideo() {
|
|||
const router = useRouter();
|
||||
const editor = useRef(null);
|
||||
type VideoSchema = z.infer<typeof videoSchema>;
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||
|
||||
const t = useTranslations("Form");
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
|
|
@ -97,6 +101,11 @@ export default function FormVideo() {
|
|||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
useState("professional");
|
||||
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
|
||||
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
|
||||
|
||||
const [selectedSEO, setSelectedSEO] = useState<string>("");
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||
|
|
@ -111,13 +120,17 @@ export default function FormVideo() {
|
|||
null
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
// const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
|
||||
|
||||
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
|
||||
|
||||
const [selectedTarget, setSelectedTarget] = useState("");
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
|
|
@ -155,14 +168,9 @@ export default function FormVideo() {
|
|||
|
||||
const videoSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
description: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." })
|
||||
.or(
|
||||
z.literal(articleBody || "").refine((val) => val.length > 0, {
|
||||
message: "Deskripsi diperlukan.",
|
||||
})
|
||||
),
|
||||
description: z.string().optional(),
|
||||
descriptionOri: z.string().optional(), // Original editor
|
||||
rewriteDescription: z.string().optional(),
|
||||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
|
@ -175,6 +183,11 @@ export default function FormVideo() {
|
|||
formState: { errors },
|
||||
} = useForm<VideoSchema>({
|
||||
resolver: zodResolver(videoSchema),
|
||||
defaultValues: {
|
||||
description: "",
|
||||
descriptionOri: "",
|
||||
rewriteDescription: "",
|
||||
},
|
||||
});
|
||||
|
||||
const doGenerateMainKeyword = async () => {
|
||||
|
|
@ -437,15 +450,30 @@ export default function FormVideo() {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (articleBody) {
|
||||
// Set ke dua field jika rewrite juga aktif
|
||||
setValue("description", articleBody);
|
||||
setValue("rewriteDescription", articleBody);
|
||||
}
|
||||
}, [articleBody, setValue]);
|
||||
|
||||
const save = async (data: VideoSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
const finalDescription = articleBody || data.description;
|
||||
if (!finalDescription.trim()) {
|
||||
// const finalDescription = articleBody || data.description;
|
||||
const finalDescription = isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
let requestData: {
|
||||
title: string;
|
||||
description: string;
|
||||
|
|
@ -726,6 +754,45 @@ export default function FormVideo() {
|
|||
}
|
||||
}, [title, getValues, setValue]);
|
||||
|
||||
const handleRewriteClick = async () => {
|
||||
setIsContentRewriteClicked(true);
|
||||
|
||||
const request = {
|
||||
style: selectedWritingStyle,
|
||||
lang: "id",
|
||||
contextType: "text",
|
||||
urlContext: null,
|
||||
context: editorContent, // Ambil isi editor original
|
||||
createdBy: roleId,
|
||||
sentiment: "Humorous",
|
||||
clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ",
|
||||
};
|
||||
|
||||
const res = await generateDataRewrite(request);
|
||||
close();
|
||||
|
||||
if (res?.error) {
|
||||
console.error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newArticleId = res?.data?.data?.id;
|
||||
setIsGeneratedArticle(true);
|
||||
|
||||
setArticleIds((prevIds: string[]) => {
|
||||
if (prevIds.length < 3) {
|
||||
return [...prevIds, newArticleId];
|
||||
} else {
|
||||
const updatedIds = [...prevIds];
|
||||
updatedIds[2] = newArticleId;
|
||||
return updatedIds;
|
||||
}
|
||||
});
|
||||
|
||||
Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds));
|
||||
setShowRewriteEditor(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col lg:flex-row gap-10">
|
||||
|
|
@ -867,7 +934,7 @@ export default function FormVideo() {
|
|||
placeholder="Enter Main Keyword"
|
||||
/>
|
||||
{/* )}
|
||||
/> */}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
|
|
@ -917,7 +984,7 @@ export default function FormVideo() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<Label>{t("special-instructions")} (Optional)</Label>
|
||||
<Label>{t("special-instructions")}(Optional)</Label>
|
||||
<div className="mt-3">
|
||||
<Controller
|
||||
control={control}
|
||||
|
|
@ -939,13 +1006,14 @@ export default function FormVideo() {
|
|||
color="primary"
|
||||
onClick={handleGenerateArtikel}
|
||||
size="sm"
|
||||
type="button"
|
||||
>
|
||||
Generate Article
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
<div className="mt-3 pb-0 flex flex-row">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<p
|
||||
key={index}
|
||||
|
|
@ -965,50 +1033,155 @@ export default function FormVideo() {
|
|||
<div className="pt-3">
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
{selectedArticleId && (
|
||||
<Link
|
||||
href={`/contributor/content/video/update-seo/${selectedArticleId}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
className="mb-2"
|
||||
size="sm"
|
||||
variant={"outline"}
|
||||
color="primary"
|
||||
>
|
||||
{t("update")}
|
||||
</Button>
|
||||
</Link>
|
||||
{t("update")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">Loading Proses Data...</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={articleBody || value}
|
||||
{!isSwitchOn && (
|
||||
<>
|
||||
<RadioGroup
|
||||
onValueChange={(value) => setSelectedFileType(value)}
|
||||
value={selectedFileType}
|
||||
className=" grid-cols-1"
|
||||
>
|
||||
<div className="">
|
||||
<RadioGroupItem value="original" id="original-file" />
|
||||
<Label htmlFor="original-file">
|
||||
Select Original Description
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("description")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="descriptionOri"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setEditorContent(value);
|
||||
}}
|
||||
initialData={value}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{errors.description?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.description.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-semibold">Content Rewrite</p>
|
||||
<div className="my-2">
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={handleRewriteClick}
|
||||
className="bg-blue-500 text-white py-2 px-4 rounded"
|
||||
>
|
||||
Content Rewrite
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showRewriteEditor && (
|
||||
<div>
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 pb-0 flex flex-row ">
|
||||
{articleIds.map((id: string, index: number) => (
|
||||
<Button
|
||||
type="button"
|
||||
key={index}
|
||||
className={`mr-3 px-3 py-2 rounded-md ${
|
||||
selectedArticleId === id
|
||||
? "bg-green-500 text-white"
|
||||
: "border-2 border-green-500 bg-white text-green-500 hover:bg-green-500 hover:text-white hover:border-green-500"
|
||||
}`}
|
||||
onClick={() => handleArticleIdClick(id)}
|
||||
>
|
||||
{"Narasi " + (index + 1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center space-x-2 mt-3">
|
||||
<RadioGroupItem value="rewrite" id="rewrite-file" />
|
||||
<Label htmlFor="rewrite-file">
|
||||
Select Description Rewrite
|
||||
</Label>
|
||||
</div>
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("file-rewrite")}</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rewriteDescription"
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isLoadingData ? (
|
||||
<div className="flex justify-center items-center h-40">
|
||||
<p className="text-gray-500">
|
||||
Loading Proses Data...
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<CustomEditor
|
||||
onChange={(value: any) => {
|
||||
onChange(value);
|
||||
setRewriteEditorContent(value);
|
||||
}}
|
||||
initialData={articleBody || value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</>
|
||||
)}
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>{t("select-file")}</Label>
|
||||
{/* <Input
|
||||
|
|
|
|||
|
|
@ -202,6 +202,23 @@ export default function FormAskExpert() {
|
|||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels: Set<number> = new Set(
|
||||
details.assignedToLevel.split(",").map((x: any) => Number(x))
|
||||
);
|
||||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.assignedToUsers) {
|
||||
const userIds = details.assignedToUsers.split(",").map(Number);
|
||||
setCheckedLevels(new Set(userIds));
|
||||
}
|
||||
|
||||
if (details?.expertCompetencies) {
|
||||
const compIds = details.expertCompetencies.split(",").map(Number);
|
||||
setSelectedCompetencies(new Set(compIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -515,7 +532,7 @@ export default function FormAskExpert() {
|
|||
return (
|
||||
<Card>
|
||||
<div className="px-6 py-6">
|
||||
<p className="text-lg font-semibold mb-3">{t("form-task")}</p>
|
||||
<p className="text-lg font-semibold mb-3">{t("form-task-ta")}</p>
|
||||
{detail !== undefined ? (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5">
|
||||
|
|
@ -617,7 +634,14 @@ export default function FormAskExpert() {
|
|||
}
|
||||
className="mr-3"
|
||||
/>
|
||||
{expert.fullname}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-bold">
|
||||
{expert.fullname}
|
||||
</div>
|
||||
<div className="italic">
|
||||
({expert.username})
|
||||
</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -625,6 +649,46 @@ export default function FormAskExpert() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
{checkedLevels.size > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-sm text-gray-600 mb-2 block">
|
||||
Tenaga Ahli Terpilih ({checkedLevels.size})
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(checkedLevels).map((expertId) => {
|
||||
const expert = listExpert?.find(
|
||||
(exp: any) => exp.id === expertId
|
||||
);
|
||||
return expert ? (
|
||||
<div
|
||||
key={expert.id}
|
||||
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<span>{expert.fullname}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxChange(expert.id)}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
|
||||
title="Remove expert"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ export default function FormDoItYourself() {
|
|||
return (
|
||||
<Card>
|
||||
<div className="px-6 py-6">
|
||||
<p className="text-lg font-semibold mb-3">{t("form-task")}</p>
|
||||
<p className="text-lg font-semibold mb-3">{t("form-task-ta-do")}</p>
|
||||
{detail !== undefined ? (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5">
|
||||
|
|
|
|||
|
|
@ -10,42 +10,23 @@ import * as z from "zod";
|
|||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import JoditEditor from "jodit-react";
|
||||
import {
|
||||
acceptAssignment,
|
||||
acceptAssignmentTa,
|
||||
createAssignmentResponse,
|
||||
createTask,
|
||||
deleteAssignmentResponse,
|
||||
deleteTask,
|
||||
finishTask,
|
||||
finishTaskTa,
|
||||
getAcceptance,
|
||||
getAcceptanceAssignmentStatus,
|
||||
getAssignmentResponseList,
|
||||
getMediaUpload,
|
||||
getMediaUploadTa,
|
||||
getTask,
|
||||
getTaskTa,
|
||||
getUserLevelForAssignments,
|
||||
getUserLevelForExpert,
|
||||
} from "@/service/task";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Dock,
|
||||
DotSquare,
|
||||
ImageIcon,
|
||||
|
|
@ -61,14 +42,20 @@ import { close, error, loading } from "@/lib/swal";
|
|||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||
import { successCallback } from "@/config/swal";
|
||||
import FileUploader from "../shared/file-uploader";
|
||||
import { AudioRecorder } from "react-audio-voice-recorder";
|
||||
import Image from "next/image";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import WavesurferPlayer from "@wavesurfer/react";
|
||||
import WaveSurfer from "wavesurfer.js";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { getListCompetencies } from "@/service/management-user/management-user";
|
||||
|
||||
const taskSchema = z.object({
|
||||
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -252,7 +239,11 @@ export default function FormTaskTaDetail() {
|
|||
const [acceptAcceptance, setAcceptAcceptance] = useState<AcceptanceData[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const [listExpert, setListExpert] = useState<any[]>([]);
|
||||
const [userCompetencies, setUserCompetencies] = useState<any[]>([]);
|
||||
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
|
||||
new Set()
|
||||
);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
|
|
@ -327,37 +318,37 @@ export default function FormTaskTaDetail() {
|
|||
// setPlatformTypeVisible(selectedValue === 2);
|
||||
// };
|
||||
|
||||
const handleExpertiseOutputChange = (
|
||||
key: keyof typeof expertise,
|
||||
value: boolean
|
||||
) => {
|
||||
if (key === "semua") {
|
||||
const newState = {
|
||||
semua: value,
|
||||
komunikasi: value,
|
||||
hukum: value,
|
||||
bahasa: value,
|
||||
ekonomi: value,
|
||||
politik: value,
|
||||
sosiologi: value,
|
||||
ilmuadministrasipemerintah: value,
|
||||
ti: value,
|
||||
};
|
||||
setExpertiseOutput(newState);
|
||||
} else {
|
||||
const updated = {
|
||||
...expertise,
|
||||
[key]: value,
|
||||
};
|
||||
// const handleExpertiseOutputChange = (
|
||||
// key: keyof typeof expertise,
|
||||
// value: boolean
|
||||
// ) => {
|
||||
// if (key === "semua") {
|
||||
// const newState = {
|
||||
// semua: value,
|
||||
// komunikasi: value,
|
||||
// hukum: value,
|
||||
// bahasa: value,
|
||||
// ekonomi: value,
|
||||
// politik: value,
|
||||
// sosiologi: value,
|
||||
// ilmuadministrasipemerintah: value,
|
||||
// ti: value,
|
||||
// };
|
||||
// setExpertiseOutput(newState);
|
||||
// } else {
|
||||
// const updated = {
|
||||
// ...expertise,
|
||||
// [key]: value,
|
||||
// };
|
||||
|
||||
const allChecked = ["video", "audio", "image", "text"].every(
|
||||
(k) => updated[k as keyof typeof expertise]
|
||||
);
|
||||
// const allChecked = ["video", "audio", "image", "text"].every(
|
||||
// (k) => updated[k as keyof typeof expertise]
|
||||
// );
|
||||
|
||||
updated.semua = allChecked;
|
||||
setExpertiseOutput(updated);
|
||||
}
|
||||
};
|
||||
// updated.semua = allChecked;
|
||||
// setExpertiseOutput(updated);
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleExpertOutputChange = (
|
||||
key: keyof typeof expert,
|
||||
|
|
@ -383,6 +374,68 @@ export default function FormTaskTaDetail() {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getDataAdditional();
|
||||
}, []);
|
||||
|
||||
async function getDataAdditional() {
|
||||
const resCompetencies = await getListCompetencies();
|
||||
console.log("competency", resCompetencies);
|
||||
setUserCompetencies(resCompetencies?.data?.data);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchListExpert() {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await getUserLevelForExpert(id);
|
||||
setListExpert(response?.data?.data);
|
||||
console.log("tenaga ahli", response?.data?.data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching Polda/Polres data:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
fetchListExpert();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchExpertsForCompetencies = async () => {
|
||||
const allExperts: any[] = [];
|
||||
|
||||
for (const compId of Array.from(selectedCompetencies)) {
|
||||
const response = await getUserLevelForExpert(compId);
|
||||
const experts = response?.data?.data || [];
|
||||
allExperts.push(...experts);
|
||||
}
|
||||
|
||||
const uniqueExperts = Array.from(
|
||||
new Map(allExperts.map((e) => [e.id, e])).values()
|
||||
);
|
||||
|
||||
setListExpert(uniqueExperts);
|
||||
};
|
||||
|
||||
if (selectedCompetencies.size > 0) {
|
||||
fetchExpertsForCompetencies();
|
||||
} else {
|
||||
setListExpert([]);
|
||||
}
|
||||
}, [selectedCompetencies]);
|
||||
|
||||
const handleCompetencyChange = async (competencyId: number) => {
|
||||
setSelectedCompetencies((prev) => {
|
||||
const updated = new Set(prev);
|
||||
if (updated.has(competencyId)) {
|
||||
updated.delete(competencyId);
|
||||
} else {
|
||||
updated.add(competencyId);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchPoldaPolres() {
|
||||
setIsLoading(true);
|
||||
|
|
@ -458,6 +511,16 @@ export default function FormTaskTaDetail() {
|
|||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.assignedToUsers) {
|
||||
const userIds = details.assignedToUsers.split(",").map(Number);
|
||||
setCheckedLevels(new Set(userIds));
|
||||
}
|
||||
|
||||
if (details?.expertCompetencies) {
|
||||
const compIds = details.expertCompetencies.split(",").map(Number);
|
||||
setSelectedCompetencies(new Set(compIds));
|
||||
}
|
||||
|
||||
const attachment = details?.files;
|
||||
setImageUploadedFiles(
|
||||
attachment?.filter((file: any) => file.fileTypeId == 1)
|
||||
|
|
@ -542,7 +605,7 @@ export default function FormTaskTaDetail() {
|
|||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/en/contributor/task");
|
||||
router.push("/en/contributor/task-ta");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -682,7 +745,7 @@ export default function FormTaskTaDetail() {
|
|||
const handleAcceptAcceptance = async () => {
|
||||
loading();
|
||||
console.log("Id user :", userId);
|
||||
const response = await acceptAssignment(id);
|
||||
const response = await acceptAssignmentTa(id);
|
||||
|
||||
if (response?.error) {
|
||||
error(response?.message);
|
||||
|
|
@ -773,7 +836,7 @@ export default function FormTaskTaDetail() {
|
|||
);
|
||||
|
||||
async function finishAssignment() {
|
||||
const response = finishTask(id);
|
||||
const response = finishTaskTa(id);
|
||||
|
||||
// if (response.error) {
|
||||
// error(response.message);
|
||||
|
|
@ -1011,48 +1074,98 @@ export default function FormTaskTaDetail() {
|
|||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expertise).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expertise[key as keyof typeof expertise]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertiseOutputChange(
|
||||
key as keyof typeof expertise,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
id={`comp-${item.id}`}
|
||||
checked={selectedCompetencies.has(item.id)}
|
||||
onCheckedChange={() => handleCompetencyChange(item.id)}
|
||||
disabled
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("choose-expert")}</Label>
|
||||
{/* <Label>{t("choose-expert")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expert).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expert[key as keyof typeof expert]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertOutputChange(
|
||||
key as keyof typeof expert,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[{"Pilih Tenaga Ahli"}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listExpert?.map((expert: any) => (
|
||||
<div key={expert.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(expert.id)}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(expert.id)
|
||||
}
|
||||
className="mr-3"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-bold">
|
||||
{expert.fullname}
|
||||
</div>
|
||||
<div className="italic">
|
||||
({expert.username})
|
||||
</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div> */}
|
||||
{checkedLevels.size > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-sm text-gray-600 mb-2 block">
|
||||
Tenaga Ahli Terpilih ({checkedLevels.size})
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(checkedLevels).map((expertId) => {
|
||||
const expert = listExpert?.find(
|
||||
(exp: any) => exp.id === expertId
|
||||
);
|
||||
return expert ? (
|
||||
<div
|
||||
key={expert.id}
|
||||
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<span>{expert.fullname}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxChange(expert.id)}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
|
||||
title="Remove expert"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div></div>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
getTask,
|
||||
getTaskTa,
|
||||
getUserLevelForAssignments,
|
||||
getUserLevelForExpert,
|
||||
} from "@/service/task";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -40,6 +41,7 @@ import {
|
|||
Dock,
|
||||
ImageIcon,
|
||||
Music,
|
||||
Trash2,
|
||||
VideoIcon,
|
||||
} from "lucide-react";
|
||||
import FileUploader from "../shared/file-uploader";
|
||||
|
|
@ -52,6 +54,7 @@ import { Upload } from "tus-js-client";
|
|||
import { error, loading } from "@/lib/swal";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { getListCompetencies } from "@/service/management-user/management-user";
|
||||
|
||||
const taskSchema = z.object({
|
||||
// uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -148,11 +151,18 @@ export default function FormTaskTaEdit() {
|
|||
const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan
|
||||
const [type, setType] = useState<string>("1");
|
||||
const [selectedTarget, setSelectedTarget] = useState("3,4");
|
||||
|
||||
const [listExpert, setListExpert] = useState<any[]>([]);
|
||||
const [userCompetencies, setUserCompetencies] = useState<any[]>([]);
|
||||
const [selectedCompetencies, setSelectedCompetencies] = useState<Set<number>>(
|
||||
new Set()
|
||||
);
|
||||
|
||||
const [detail, setDetail] = useState<taskDetail>();
|
||||
const [urlInputs, setUrlInputs] = useState<Url[]>([]);
|
||||
const [refresh] = useState(false);
|
||||
const [listDest, setListDest] = useState([]); // Data Polda dan Polres
|
||||
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
||||
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
|
||||
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||
|
|
@ -247,6 +257,68 @@ export default function FormTaskTaEdit() {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getDataAdditional();
|
||||
}, []);
|
||||
|
||||
async function getDataAdditional() {
|
||||
const resCompetencies = await getListCompetencies();
|
||||
console.log("competency", resCompetencies);
|
||||
setUserCompetencies(resCompetencies?.data?.data);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchListExpert() {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await getUserLevelForExpert(id);
|
||||
setListExpert(response?.data?.data);
|
||||
console.log("tenaga ahli", response?.data?.data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching Polda/Polres data:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
fetchListExpert();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchExpertsForCompetencies = async () => {
|
||||
const allExperts: any[] = [];
|
||||
|
||||
for (const compId of Array.from(selectedCompetencies)) {
|
||||
const response = await getUserLevelForExpert(compId);
|
||||
const experts = response?.data?.data || [];
|
||||
allExperts.push(...experts);
|
||||
}
|
||||
|
||||
const uniqueExperts = Array.from(
|
||||
new Map(allExperts.map((e) => [e.id, e])).values()
|
||||
);
|
||||
|
||||
setListExpert(uniqueExperts);
|
||||
};
|
||||
|
||||
if (selectedCompetencies.size > 0) {
|
||||
fetchExpertsForCompetencies();
|
||||
} else {
|
||||
setListExpert([]);
|
||||
}
|
||||
}, [selectedCompetencies]);
|
||||
|
||||
const handleCompetencyChange = async (competencyId: number) => {
|
||||
setSelectedCompetencies((prev) => {
|
||||
const updated = new Set(prev);
|
||||
if (updated.has(competencyId)) {
|
||||
updated.delete(competencyId);
|
||||
} else {
|
||||
updated.add(competencyId);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchPoldaPolres() {
|
||||
setIsLoading(true);
|
||||
|
|
@ -285,12 +357,22 @@ export default function FormTaskTaEdit() {
|
|||
}
|
||||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set(
|
||||
details.assignedToLevel.split(",").map(Number)
|
||||
const levels: Set<number> = new Set(
|
||||
details.assignedToLevel.split(",").map((x: any) => Number(x))
|
||||
);
|
||||
setCheckedLevels(levels);
|
||||
}
|
||||
|
||||
if (details?.assignedToUsers) {
|
||||
const userIds = details.assignedToUsers.split(",").map(Number);
|
||||
setCheckedLevels(new Set(userIds));
|
||||
}
|
||||
|
||||
if (details?.expertCompetencies) {
|
||||
const compIds = details.expertCompetencies.split(",").map(Number);
|
||||
setSelectedCompetencies(new Set(compIds));
|
||||
}
|
||||
|
||||
const attachment = details?.files;
|
||||
setImageUploadedFiles(
|
||||
attachment?.filter((file: any) => file.fileTypeId == 1)
|
||||
|
|
@ -376,7 +458,7 @@ export default function FormTaskTaEdit() {
|
|||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/en/contributor/task");
|
||||
router.push("/en/contributor/task-ta");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -393,9 +475,9 @@ export default function FormTaskTaEdit() {
|
|||
});
|
||||
};
|
||||
|
||||
const handlePoldaPolresChange = () => {
|
||||
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||
};
|
||||
// const handlePoldaPolresChange = () => {
|
||||
// return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
||||
// };
|
||||
|
||||
const handleUnitChange = (
|
||||
key: keyof typeof unitSelection,
|
||||
|
|
@ -454,6 +536,10 @@ export default function FormTaskTaEdit() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleExpertChange = () => {
|
||||
return Array.from(checkedLevels).join(",");
|
||||
};
|
||||
|
||||
const save = async (data: TaskSchema) => {
|
||||
const fileTypeMapping = {
|
||||
all: "1",
|
||||
|
|
@ -483,16 +569,11 @@ export default function FormTaskTaEdit() {
|
|||
const requestData: {
|
||||
id?: any;
|
||||
title: string;
|
||||
assignedToLevel: any;
|
||||
assignedToUsers: any;
|
||||
assignmentTypeId: string;
|
||||
fileTypeOutput: string;
|
||||
narration: string;
|
||||
platformType: string | null;
|
||||
assignmentMainTypeId: any;
|
||||
assignmentType: string;
|
||||
assignedToRole: string;
|
||||
broadcastType: string;
|
||||
expertCompetencies: string;
|
||||
attachmentUrl: string[];
|
||||
} = {
|
||||
|
|
@ -500,17 +581,12 @@ export default function FormTaskTaEdit() {
|
|||
// assignmentType,
|
||||
// assignmentCategory,
|
||||
id: detail?.id || null,
|
||||
assignedToLevel: handlePoldaPolresChange(),
|
||||
assignedToUsers: assignmentPurposeString,
|
||||
assignedToUsers: handleExpertChange(),
|
||||
assignedToRole: selectedTarget,
|
||||
assignmentType: taskType,
|
||||
broadcastType: broadcastType,
|
||||
assignmentMainTypeId: mainType,
|
||||
assignmentTypeId: type,
|
||||
fileTypeOutput: selectedOutputs,
|
||||
narration: data.naration,
|
||||
platformType: "",
|
||||
expertCompetencies: "1,2,3",
|
||||
expertCompetencies: Array.from(selectedCompetencies).join(","),
|
||||
title: data.title,
|
||||
attachmentUrl: links,
|
||||
};
|
||||
|
|
@ -808,47 +884,97 @@ export default function FormTaskTaEdit() {
|
|||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("areas-expertise")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expertise).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
{userCompetencies?.map((item: any) => (
|
||||
<div className="flex items-center gap-2" key={item.id}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expertise[key as keyof typeof expertise]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertiseOutputChange(
|
||||
key as keyof typeof expertise,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
id={`comp-${item.id}`}
|
||||
checked={selectedCompetencies.has(item.id)}
|
||||
onCheckedChange={() => handleCompetencyChange(item.id)}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
<Label htmlFor={`comp-${item.id}`}>{item.name}</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
<Label>{t("choose-expert")}</Label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{Object.keys(expert).map((key) => (
|
||||
<div className="flex items-center gap-2" key={key}>
|
||||
<Checkbox
|
||||
id={key}
|
||||
checked={expert[key as keyof typeof expert]}
|
||||
onCheckedChange={(value) =>
|
||||
handleExpertOutputChange(
|
||||
key as keyof typeof expert,
|
||||
value as boolean
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Label htmlFor={key}>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[{"Pilih Tenaga Ahli"}]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Daftar Tenaga Ahli</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listExpert?.map((expert: any) => (
|
||||
<div key={expert.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<Checkbox
|
||||
checked={checkedLevels.has(expert.id)}
|
||||
onCheckedChange={() =>
|
||||
handleCheckboxChange(expert.id)
|
||||
}
|
||||
className="mr-3"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-bold">
|
||||
{expert.fullname}
|
||||
</div>
|
||||
<div className="italic">
|
||||
({expert.username})
|
||||
</div>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
{checkedLevels.size > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-sm text-gray-600 mb-2 block">
|
||||
Tenaga Ahli Terpilih ({checkedLevels.size})
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(checkedLevels).map((expertId) => {
|
||||
const expert = listExpert?.find(
|
||||
(exp: any) => exp.id === expertId
|
||||
);
|
||||
return expert ? (
|
||||
<div
|
||||
key={expert.id}
|
||||
className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1.5 rounded-full border border-blue-200"
|
||||
>
|
||||
<span>{expert.fullname}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxChange(expert.id)}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800 hover:bg-blue-200 rounded-full p-0.5 transition-colors"
|
||||
title="Remove expert"
|
||||
>
|
||||
<svg
|
||||
className="w-3 h-3"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-2">
|
||||
|
|
@ -1079,13 +1205,42 @@ export default function FormTaskTaEdit() {
|
|||
/>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="mt-4 bg-green-500 text-white px-4 py-2 rounded"
|
||||
onClick={handleAddLink}
|
||||
>
|
||||
{t("add-links")}
|
||||
</button>
|
||||
<div className="mt-4 space-y-2">
|
||||
<Label className="">{t("news-links")}</Label>
|
||||
{links.map((link, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 mt-2"
|
||||
>
|
||||
<Input
|
||||
type="url"
|
||||
className="border rounded p-2 w-full"
|
||||
placeholder={`Masukkan link berita ${index + 1}`}
|
||||
value={link}
|
||||
onChange={(e) =>
|
||||
handleLinkChange(index, e.target.value)
|
||||
}
|
||||
/>
|
||||
{links.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
className="bg-red-500 text-white px-3 py-1 rounded"
|
||||
onClick={() => handleRemoveRow(index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
type="button"
|
||||
className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
|
||||
onClick={handleAddRow}
|
||||
size="sm"
|
||||
>
|
||||
{t("add-links")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ const ContentCategory = (props: { group?: string; type: string }) => {
|
|||
alt="category"
|
||||
width={2560}
|
||||
height={1440}
|
||||
src={category?.smallThumbnailLink}
|
||||
src={category?.thumbnailLink}
|
||||
className="w-full lg:h-[300px] h-40 object-cover group-hover:scale-110 transition-transform duration-300"
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Image from "next/image";
|
|||
const regions = [
|
||||
{ name: "ITWASUM POLRI", slug: "itwasum", logo: "/logo/satker/ITWASUM.png" },
|
||||
{
|
||||
name: "BAINTELKAM POLRI",
|
||||
name: "BAINTELKAM POLRI ",
|
||||
slug: "baintelkam",
|
||||
logo: "/logo/satker/BAINTELKAM.png",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -71,22 +71,22 @@ export default function DashboardVisualization() {
|
|||
setIsInternational(updatedIsInternational);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchUrl() {
|
||||
console.log("Fetch tableau");
|
||||
const urlView = `${url + ticket1}/${view1}${param}`;
|
||||
console.log("Fetch tableau ", urlView);
|
||||
const urlRender = await fetch(urlView)
|
||||
.then((response) => {
|
||||
console.log("Tableau res : ", response);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Tableau error: ", error);
|
||||
});
|
||||
}
|
||||
// useEffect(() => {
|
||||
// async function fetchUrl() {
|
||||
// console.log("Fetch tableau");
|
||||
// const urlView = `${url + ticket1}/${view1}${param}`;
|
||||
// console.log("Fetch tableau ", urlView);
|
||||
// const urlRender = await fetch(urlView)
|
||||
// .then((response) => {
|
||||
// console.log("Tableau res : ", response);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.log("Tableau error: ", error);
|
||||
// });
|
||||
// }
|
||||
|
||||
fetchUrl();
|
||||
}, [ticket1]);
|
||||
// fetchUrl();
|
||||
// }, [ticket1]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 bg-white rounded-lg p-3">
|
||||
|
|
|
|||
80
lib/menus.ts
80
lib/menus.ts
|
|
@ -3627,20 +3627,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "contest",
|
||||
menus: [
|
||||
{
|
||||
id: "contest",
|
||||
href: "/shared/contest",
|
||||
label: t("contest"),
|
||||
active: pathname.includes("/contest"),
|
||||
icon: "ic:outline-emoji-events",
|
||||
submenus: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// groupLabel: "",
|
||||
// id: "contest",
|
||||
// menus: [
|
||||
// {
|
||||
// id: "contest",
|
||||
// href: "/shared/contest",
|
||||
// label: t("contest"),
|
||||
// active: pathname.includes("/contest"),
|
||||
// icon: "ic:outline-emoji-events",
|
||||
// submenus: [],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "communication",
|
||||
|
|
@ -3896,16 +3896,16 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
icon: "material-symbols:map-search-outline",
|
||||
submenus: [
|
||||
{
|
||||
href: "/admin/media-tracking/media-online",
|
||||
label: "Media Online",
|
||||
active: pathname === "/media-tracking/media-online",
|
||||
href: "/admin/media-tracking/tracking-berita",
|
||||
label: "Tracking Beritra",
|
||||
active: pathname === "/admin/media-tracking/tracking-berita",
|
||||
icon: "heroicons:arrow-trending-up",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/admin/media-tracking/tb-news",
|
||||
label: "Tracking Berita Hari Ini",
|
||||
active: pathname === "/media-tracking/news",
|
||||
href: "/admin/media-tracking/results",
|
||||
label: "Hasil",
|
||||
active: pathname === "/admin/media-tracking/results",
|
||||
icon: "heroicons:arrow-trending-up",
|
||||
children: [],
|
||||
},
|
||||
|
|
@ -4079,20 +4079,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// groupLabel: "",
|
||||
// id: "performance-satker",
|
||||
// menus: [
|
||||
// {
|
||||
// id: "performance-polres",
|
||||
// href: "/admin/performance-satker",
|
||||
// label: t("performance-satker"),
|
||||
// active: pathname.includes("/admin/performance-satker"),
|
||||
// icon: "ant-design:signal-filled",
|
||||
// submenus: [],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "performance-satker",
|
||||
menus: [
|
||||
{
|
||||
id: "performance-polres",
|
||||
href: "/admin/performance-satker",
|
||||
label: t("performance-satker"),
|
||||
active: pathname.includes("/admin/performance-satker"),
|
||||
icon: "ant-design:signal-filled",
|
||||
submenus: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupLabel: "",
|
||||
id: "media-tracking",
|
||||
|
|
@ -4105,16 +4105,16 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
icon: "material-symbols:map-search-outline",
|
||||
submenus: [
|
||||
{
|
||||
href: "/admin/media-tracking/media-online",
|
||||
label: "Media Online",
|
||||
active: pathname === "/media-tracking/media-online",
|
||||
href: "/admin/media-tracking/tracking-berita",
|
||||
label: "Tracking Beritra",
|
||||
active: pathname === "/admin/media-tracking/tracking-berita",
|
||||
icon: "heroicons:arrow-trending-up",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
href: "/admin/media-tracking/tb-news",
|
||||
label: "Tracking Berita Hari Ini",
|
||||
active: pathname === "/media-tracking/news",
|
||||
href: "/admin/media-tracking/results",
|
||||
label: "Hasil",
|
||||
active: pathname === "/admin/media-tracking/results",
|
||||
icon: "heroicons:arrow-trending-up",
|
||||
children: [],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -784,6 +784,8 @@
|
|||
"assignment-type": "Assignment Type",
|
||||
"description-task": "Description Task",
|
||||
"form-task": "Form Task",
|
||||
"form-task-ta": "Form Ask the Expert",
|
||||
"form-task-ta-do": "Form Do it yourself",
|
||||
"assignment-selection": "Assignment Recipient",
|
||||
"custom": "Costum",
|
||||
"assigment-type": "Assigment Type",
|
||||
|
|
|
|||
|
|
@ -571,47 +571,33 @@
|
|||
"divisionNews": "Berita Satker",
|
||||
"areaCoverage": "Liputan Wilayah & Satker",
|
||||
"calendar": "KALENDER ACARA",
|
||||
"january": "Januari",
|
||||
"february": "February",
|
||||
"march": "March",
|
||||
"may": "May",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "Agustus",
|
||||
"october": "Oktober",
|
||||
"december": "Desember",
|
||||
"eventList": "Daftar Acara",
|
||||
"noEvent": "Tidak ada acara yang tersedia",
|
||||
"eventDetails": "Detail Acara",
|
||||
"date": "Tanggal:",
|
||||
"selectEvent": "Pilih acara untuk melihat detail",
|
||||
"regionalPolice": "Polda Jajaran",
|
||||
"policeDivision": "Satuan Kerja Polri",
|
||||
"close": "Tutup",
|
||||
"survey1": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
|
||||
"survey2": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri. Mohon luangkan waktu beberapa menit untuk mengisi survei ini.",
|
||||
"survey3": "SURVEY SEKARANG",
|
||||
"survey4": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
|
||||
"survey5": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri.",
|
||||
"survey6": "1. Seberapa sering Anda mengakses MediaHub Polri?",
|
||||
"survey7": "2. Bagaimana pengalaman Anda dalam mengakses website ini?",
|
||||
"survey8": "3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub Polri?",
|
||||
"survey9": "4. Apakah Anda merasa website ini membantu dalam mendapatkan informasi terkait Polri?",
|
||||
"survey10": "5. Apa saran atau masukan Anda?"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"january": "Januari",
|
||||
"february": "February",
|
||||
"march": "March",
|
||||
"may": "May",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "Agustus",
|
||||
"october": "Oktober",
|
||||
"december": "Desember",
|
||||
"eventList": "Daftar Acara",
|
||||
"noEvent": "Tidak ada acara yang tersedia",
|
||||
"eventDetails": "Detail Acara",
|
||||
"date": "Tanggal:",
|
||||
"selectEvent": "Pilih acara untuk melihat detail",
|
||||
"regionalPolice": "Polda Jajaran",
|
||||
"policeDivision": "Satuan Kerja Polri",
|
||||
"close": "Tutup",
|
||||
"survey1": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
|
||||
"survey2": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri. Mohon luangkan waktu beberapa menit untuk mengisi survei ini.",
|
||||
"survey3": "SURVEY SEKARANG",
|
||||
"survey4": "SURVEI KEPUASAN PENGGUNA MEDIAHUB POLRI",
|
||||
"survey5": "Kami menghargai pendapat Anda! Survei ini bertujuan untuk meningkatkan kualitas layanan MediaHub Polri.",
|
||||
"survey6": "1. Seberapa sering Anda mengakses MediaHub Polri?",
|
||||
"survey7": "2. Bagaimana pengalaman Anda dalam mengakses website ini?",
|
||||
"survey8": "3. Seberapa puas Anda dengan informasi yang tersedia di MediaHub Polri?",
|
||||
"survey9": "4. Apakah Anda merasa website ini membantu dalam mendapatkan informasi terkait Polri?",
|
||||
"survey10": "5. Apa saran atau masukan Anda?"
|
||||
},
|
||||
"FilterPage": {
|
||||
"image": "Foto",
|
||||
|
|
@ -798,6 +784,8 @@
|
|||
"assignment-type": "Jenis Penugasan",
|
||||
"description-task": "Narasi Penugasan",
|
||||
"form-task": "Form Penugasan",
|
||||
"form-task-ta": "Form Tanya Tenaga Ahli",
|
||||
"form-task-ta-do": "Form Tenaga Ahli Kurasi",
|
||||
"assignment-selection": "Penerima Tugas",
|
||||
"custom": "Kostum",
|
||||
"assigment-type": "Jenis Tugas",
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export async function getTagsBySubCategoryId(subCategory: any) {
|
|||
}
|
||||
|
||||
export async function listEnableCategory(type: any) {
|
||||
const url = `media/categories/list/enable?enablePage=0&sort=desc&sortBy=id&type=${type}`;
|
||||
const url = `media/categories/list?enablePage=0&sort=desc&sortBy=id&type=${type}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ export async function saveUserInternal(data: any) {
|
|||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function checkRolePlacementsAvailability(data: any) {
|
||||
const url = "users/role-placements/availability";
|
||||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function saveUserRolePlacements(data: any) {
|
||||
const url = "users/role-placements";
|
||||
return httpPostInterceptor(url, data);
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ export async function acceptAssignment(id: any) {
|
|||
return httpPostInterceptor(url, id);
|
||||
}
|
||||
|
||||
export async function acceptAssignmentTa(id: any) {
|
||||
const url = `assignment-expert/acceptance?id=${id}`;
|
||||
return httpPostInterceptor(url, id);
|
||||
}
|
||||
|
||||
export async function postFinishAcceptance(id: any) {
|
||||
const url = `assignment/finish-acceptance?id=${id}`;
|
||||
return httpPostInterceptor(url, id);
|
||||
|
|
@ -128,6 +133,11 @@ export async function finishTask(id: any) {
|
|||
return httpPostInterceptor(url);
|
||||
}
|
||||
|
||||
export async function finishTaskTa(id: any) {
|
||||
const url = `assignment-expert/finish?id=${id}`;
|
||||
return httpPostInterceptor(url);
|
||||
}
|
||||
|
||||
export async function listTaskTa(
|
||||
page: any,
|
||||
title: string = "",
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ const LoadScript = () => {
|
|||
const script = document.createElement("script");
|
||||
script.src = "https://cdn.userway.org/widget.js";
|
||||
script.setAttribute("data-account", "X36s1DpjqB");
|
||||
script.setAttribute("data-position", "3");
|
||||
script.setAttribute("data-position", "5");
|
||||
script.async = true;
|
||||
document.head.appendChild(script);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue