feat:update konten, update task,fix logo header admin
This commit is contained in:
commit
b9fc0a63e4
|
|
@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import ExternalDraggingevent from "./dragging-events";
|
import ExternalDraggingevent from "./dragging-events";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { EventContentArg } from "@fullcalendar/core";
|
import { EventContentArg } from "@fullcalendar/core";
|
||||||
|
|
@ -19,6 +19,12 @@ import { getAgendaSettingsList } from "@/service/agenda-setting/agenda-setting";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { CalendarCategory } from "./data";
|
import { CalendarCategory } from "./data";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
|
||||||
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
interface CalendarViewProps {
|
interface CalendarViewProps {
|
||||||
|
|
@ -26,6 +32,21 @@ interface CalendarViewProps {
|
||||||
categories: CalendarCategory[];
|
categories: CalendarCategory[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CalendarEvent = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
start: Date;
|
||||||
|
end: Date;
|
||||||
|
createBy: string;
|
||||||
|
createdByName: string;
|
||||||
|
allDay: boolean;
|
||||||
|
extendedProps: {
|
||||||
|
calendar: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const INITIAL_YEAR = dayjs().format("YYYY");
|
const INITIAL_YEAR = dayjs().format("YYYY");
|
||||||
const INITIAL_MONTH = dayjs().format("M");
|
const INITIAL_MONTH = dayjs().format("M");
|
||||||
|
|
||||||
|
|
@ -43,6 +64,33 @@ export interface AgendaSettingsAPIResponse {
|
||||||
createdById: number | null;
|
createdById: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface YearlyData {
|
||||||
|
january?: Event[];
|
||||||
|
february?: Event[];
|
||||||
|
march?: Event[];
|
||||||
|
april?: Event[];
|
||||||
|
may?: Event[];
|
||||||
|
june?: Event[];
|
||||||
|
july?: Event[];
|
||||||
|
august?: Event[];
|
||||||
|
september?: Event[];
|
||||||
|
october?: Event[];
|
||||||
|
november?: Event[];
|
||||||
|
december?: Event[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MonthCardProps {
|
||||||
|
monthId: keyof YearlyData;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ListItemProps {
|
||||||
|
item: any;
|
||||||
|
text: string
|
||||||
|
createdBy: string;
|
||||||
|
bgColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface APIResponse {
|
interface APIResponse {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
message: any;
|
message: any;
|
||||||
|
|
@ -63,6 +111,8 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
// event canvas state
|
// event canvas state
|
||||||
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
||||||
const [date, setDate] = React.useState<Date>(new Date());
|
const [date, setDate] = React.useState<Date>(new Date());
|
||||||
|
const [activeView, setActiveView] = useState("dayGridMonth");
|
||||||
|
const [yearlyData, setYearlyData] = useState();
|
||||||
|
|
||||||
const [dragEvents] = useState([
|
const [dragEvents] = useState([
|
||||||
{ title: "New Event Planning", id: "101", tag: "business" },
|
{ title: "New Event Planning", id: "101", tag: "business" },
|
||||||
|
|
@ -75,6 +125,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCalendarEvents();
|
getCalendarEvents();
|
||||||
|
getYearlyEvents();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCalendarEvents = async () => {
|
const getCalendarEvents = async () => {
|
||||||
|
|
@ -82,6 +133,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
console.log("API Response:", res);
|
console.log("API Response:", res);
|
||||||
|
|
||||||
if (res?.error) {
|
if (res?.error) {
|
||||||
|
error(res?.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +157,15 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
console.log("event", events);
|
console.log("event", events);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getYearlyEvents = async () => {
|
||||||
|
const res = await getAgendaSettingsList(INITIAL_YEAR, '', '');
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setYearlyData(res?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedCategory(categories?.map((c) => c.value));
|
setSelectedCategory(categories?.map((c) => c.value));
|
||||||
}, [categories]);
|
}, [categories]);
|
||||||
|
|
@ -212,11 +273,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
|
|
||||||
// event click
|
// event click
|
||||||
const handleEventClick = (arg: any) => {
|
const handleEventClick = (arg: any) => {
|
||||||
|
console.log("Event Click ", arg);
|
||||||
setSelectedEventDate(null);
|
setSelectedEventDate(null);
|
||||||
setSheetOpen(true);
|
setSheetOpen(true);
|
||||||
setApiEvents(arg);
|
setApiEvents(arg);
|
||||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle close modal
|
// handle close modal
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setSheetOpen(false);
|
setSheetOpen(false);
|
||||||
|
|
@ -225,6 +288,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
};
|
};
|
||||||
const handleDateClick = (arg: any) => {
|
const handleDateClick = (arg: any) => {
|
||||||
setSheetOpen(true);
|
setSheetOpen(true);
|
||||||
|
console.log("Date :", arg);
|
||||||
setSelectedEventDate(arg);
|
setSelectedEventDate(arg);
|
||||||
setApiEvents([]);
|
setApiEvents([]);
|
||||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||||
|
|
@ -266,6 +330,142 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDateChange = (startDate: string, endDate: string) => {
|
||||||
|
console.log(startDate + " - " + endDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewChange = (viewType: string) => {
|
||||||
|
console.log(viewType);
|
||||||
|
setActiveView(viewType);
|
||||||
|
};
|
||||||
|
|
||||||
|
const months: Array<{ id: keyof YearlyData; label: string }> = [
|
||||||
|
{ id: 'january', label: 'Januari' },
|
||||||
|
{ id: 'february', label: 'Februari' },
|
||||||
|
{ id: 'march', label: 'Maret' },
|
||||||
|
{ id: 'april', label: 'April' },
|
||||||
|
{ id: 'may', label: 'Mei' },
|
||||||
|
{ id: 'june', label: 'Juni' },
|
||||||
|
{ id: 'july', label: 'Juli' },
|
||||||
|
{ id: 'august', label: 'Agustus' },
|
||||||
|
{ id: 'september', label: 'September' },
|
||||||
|
{ id: 'october', label: 'Oktober' },
|
||||||
|
{ id: 'november', label: 'November' },
|
||||||
|
{ id: 'december', label: 'Desember' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEventColor = (type: Event['type']): string => {
|
||||||
|
const colors: Record<Event['type'], string> = {
|
||||||
|
mabes: 'bg-yellow-500',
|
||||||
|
polda: 'bg-blue-400',
|
||||||
|
polres: 'bg-slate-400',
|
||||||
|
international: 'bg-green-400'
|
||||||
|
};
|
||||||
|
return colors[type];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickListItem = (item: any) => {
|
||||||
|
const formattedEvent: CalendarEvent = {
|
||||||
|
id: item.id.toString(),
|
||||||
|
title: item.title,
|
||||||
|
start: new Date(item.startDate),
|
||||||
|
end: new Date(item.endDate),
|
||||||
|
createBy: "Mabes Polri - Approver", // Sesuaikan dengan data yang sebenarnya jika ada
|
||||||
|
createdByName: item.createdByName,
|
||||||
|
allDay: true,
|
||||||
|
extendedProps: {
|
||||||
|
calendar: item.agendaType,
|
||||||
|
description: item.description
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const finalEvent: any = {
|
||||||
|
event: formattedEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Event click custom : ", finalEvent);
|
||||||
|
|
||||||
|
setSelectedEventDate(null);
|
||||||
|
setSheetOpen(true);
|
||||||
|
setApiEvents(finalEvent);
|
||||||
|
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem: React.FC<ListItemProps> = ({ item, text, createdBy, bgColor }) => (
|
||||||
|
<div className={`w-full p-1 mb-2 rounded-md text-white text-sm ${bgColor}`} onClick={() => handleClickListItem(item)}>
|
||||||
|
<p className="ml-1">{text}</p>
|
||||||
|
<p className="ml-1 text-xs text-start mt-2">
|
||||||
|
Create By: {createdBy}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const MonthCard: React.FC<MonthCardProps> = (props: any) => {
|
||||||
|
const { monthId, label } = props;
|
||||||
|
const events: any = yearlyData?.[monthId];
|
||||||
|
const displayedEvents = events?.slice(0, 3);
|
||||||
|
const hasMoreEvents = events?.length > 3;
|
||||||
|
return (
|
||||||
|
<div className="flex-1 bg-white rounded-lg shadow-sm border border-gray-200 pb-3 mr-1">
|
||||||
|
<div className="py-3">
|
||||||
|
<h4 className="font-bold text-center">{label}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-2">
|
||||||
|
{events?.length === 0 ? (
|
||||||
|
<div className="mt-1 py-2 rounded-lg bg-white border border-black">
|
||||||
|
<p className="text-center">Belum ada data</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{displayedEvents?.map((event: any, index: number) => (
|
||||||
|
<ListItem
|
||||||
|
key={index}
|
||||||
|
item={event}
|
||||||
|
text={event.title}
|
||||||
|
createdBy={event.createdByName}
|
||||||
|
bgColor={getEventColor(event.agendaType)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{hasMoreEvents && (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button className="w-full" size="sm">
|
||||||
|
Lihat lebih banyak
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
side="top"
|
||||||
|
className="p-0 overflow-hidden bg-transparent shadow-none border-none"
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="bg-default p-2 rounded-t-lg">
|
||||||
|
<CardTitle className="text-default-foreground text-base py-0">{label}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="p-2 text-sm">
|
||||||
|
<div className="max-h-[300px] overflow-y-auto border rounded-md border-gray-300 p-2">
|
||||||
|
{events.map((event: any, index: number) => (
|
||||||
|
<ListItem
|
||||||
|
key={index}
|
||||||
|
item={event}
|
||||||
|
text={event.title}
|
||||||
|
createdBy={event.createdByName}
|
||||||
|
bgColor={getEventColor(event.agendaType)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
||||||
|
|
@ -344,7 +544,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
headerToolbar={{
|
headerToolbar={{
|
||||||
left: "prev,next today",
|
left: "prev,next today",
|
||||||
center: "title",
|
center: "title",
|
||||||
right: "dayGridMonth,listWeek,",
|
right: "listYear,dayGridMonth,listMonth",
|
||||||
|
}}
|
||||||
|
views={{
|
||||||
|
listYear: {
|
||||||
|
type: "dayGridMonth",
|
||||||
|
buttonText: "Year",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
events={displayedEvents}
|
events={displayedEvents}
|
||||||
editable={true}
|
editable={true}
|
||||||
|
|
@ -358,9 +564,47 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
eventClassNames={handleClassName}
|
eventClassNames={handleClassName}
|
||||||
dateClick={handleDateClick}
|
dateClick={handleDateClick}
|
||||||
eventClick={handleEventClick}
|
eventClick={handleEventClick}
|
||||||
initialView="dayGridMonth"
|
initialView="listYear"
|
||||||
eventContent={renderEventContent}
|
eventContent={renderEventContent}
|
||||||
|
viewDidMount={(info) => handleViewChange(info.view.type)}
|
||||||
|
datesSet={(info: any) => {
|
||||||
|
console.log(info);
|
||||||
|
handleDateChange(info.view.currentStart, info.view.currentEnd);
|
||||||
|
handleViewChange(info.view.type);
|
||||||
|
}}
|
||||||
|
viewClassNames={activeView === "listYear" ? "hide-calendar-grid" : ""}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{activeView === "listYear" && (
|
||||||
|
<div className="custom-ui">
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(0, 3).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Second Row */}
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(3, 6).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Third Row */}
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(6, 9).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Fourth Row */}
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(9, 12).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -376,17 +620,3 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CalendarView;
|
export default CalendarView;
|
||||||
|
|
||||||
export type CalendarEvent = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
start: Date;
|
|
||||||
end: Date;
|
|
||||||
createBy: string;
|
|
||||||
createdByName: string;
|
|
||||||
allDay: boolean;
|
|
||||||
extendedProps: {
|
|
||||||
calendar: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,8 @@ const EventModal = ({
|
||||||
}
|
}
|
||||||
fetchPoldaPolres();
|
fetchPoldaPolres();
|
||||||
console.log("Event", event);
|
console.log("Event", event);
|
||||||
|
|
||||||
|
console.log("Event click modal : ", event);
|
||||||
}, [agendaType]);
|
}, [agendaType]);
|
||||||
|
|
||||||
const handleCheckboxChange = (levelId: number) => {
|
const handleCheckboxChange = (levelId: number) => {
|
||||||
|
|
@ -299,6 +301,7 @@ const EventModal = ({
|
||||||
const file = new File([blob], "voiceNote.webm", { type: "audio/webm" });
|
const file = new File([blob], "voiceNote.webm", { type: "audio/webm" });
|
||||||
setAudioFile(file);
|
setAudioFile(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteAudio = () => {
|
const handleDeleteAudio = () => {
|
||||||
// Remove the audio file by setting state to null
|
// Remove the audio file by setting state to null
|
||||||
setAudioFile(null);
|
setAudioFile(null);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Progress } from "@/components/ui/progress";
|
||||||
|
import { getUserFeedbacks } from "@/service/master/faq";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { stringify } from "querystring";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function UserFeedback() {
|
||||||
|
const [listData, setListData] = useState<any>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initState();
|
||||||
|
}, []);
|
||||||
|
async function initState() {
|
||||||
|
const response = await getUserFeedbacks();
|
||||||
|
console.log("ssss", response?.data?.data);
|
||||||
|
setListData(response?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderStar = (count: number) => {
|
||||||
|
const mapped = [1, 2, 3, 4, 5];
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
{mapped?.map((row) =>
|
||||||
|
row < count + 1 ? (
|
||||||
|
<Icon key={row} icon="emojione:star" width={33} />
|
||||||
|
) : (
|
||||||
|
<Icon key={row} icon="emojione-monotone:star" width={33} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="flex flex-col gap-2 bg-white p-4">
|
||||||
|
<p className="text-lg">Hasil Feedback</p>
|
||||||
|
<div className="grid grid-cols-2 gap-5">
|
||||||
|
{listData?.map(
|
||||||
|
(list: any) =>
|
||||||
|
list?.avgScore !== "NaN" && (
|
||||||
|
<div
|
||||||
|
key={list?.id}
|
||||||
|
className="flex flex-col gap-2 bg-gray-100 rounded-md p-5"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p className="text-3xl">{parseInt(list?.avgScore)}</p>
|
||||||
|
{renderStar(parseInt(list?.avgScore))}
|
||||||
|
</div>
|
||||||
|
<p className="font-semibold">{list?.question}</p>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p className="w-[120px]">Penilaian 5</p>
|
||||||
|
<Progress
|
||||||
|
value={parseInt(list?.score5)}
|
||||||
|
className="w-[70%]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p className="w-[120px]">Penilaian 4</p>
|
||||||
|
<Progress
|
||||||
|
value={parseInt(list?.score4)}
|
||||||
|
className="w-[70%]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p className="w-[120px]">Penilaian 3</p>
|
||||||
|
<Progress
|
||||||
|
value={parseInt(list?.score3)}
|
||||||
|
className="w-[70%]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p className="w-[120px]">Penilaian 2</p>
|
||||||
|
<Progress
|
||||||
|
value={parseInt(list?.score2)}
|
||||||
|
className="w-[70%]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p className="w-[120px]">Penilaian 1</p>
|
||||||
|
<Progress
|
||||||
|
value={parseInt(list?.score1)}
|
||||||
|
className="w-[70%]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ import {
|
||||||
listTicketingInternal,
|
listTicketingInternal,
|
||||||
} from "@/service/communication/communication";
|
} from "@/service/communication/communication";
|
||||||
|
|
||||||
const EscalationTable = () => {
|
const CollaborationTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -254,4 +254,4 @@ const EscalationTable = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EscalationTable;
|
export default CollaborationTable;
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,10 @@ export default function CollaborationPage() {
|
||||||
loading();
|
loading();
|
||||||
const response = await deleteCollabDiscussion(dataId);
|
const response = await deleteCollabDiscussion(dataId);
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
toast({
|
||||||
|
title: "Sukses hapus",
|
||||||
|
});
|
||||||
|
setReplyValue("");
|
||||||
close();
|
close();
|
||||||
initState();
|
initState();
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +82,7 @@ export default function CollaborationPage() {
|
||||||
loading();
|
loading();
|
||||||
const data = {
|
const data = {
|
||||||
ticketId: id,
|
ticketId: id,
|
||||||
replyValue,
|
message: replyValue,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,14 +100,6 @@ const columns: ColumnDef<any>[] = [
|
||||||
View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ import {
|
||||||
import { listTicketingInternal } from "@/service/communication/communication";
|
import { listTicketingInternal } from "@/service/communication/communication";
|
||||||
import { Link } from "@/components/navigation";
|
import { Link } from "@/components/navigation";
|
||||||
|
|
||||||
const TableAudio = () => {
|
const InternalTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -260,4 +260,4 @@ const TableAudio = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TableAudio;
|
export default InternalTable;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import InternalTable from "./internal/components/internal-table";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
|
||||||
import CollaborationTable from "./collaboration/components/collabroation-table";
|
import CollaborationTable from "./collaboration/components/collabroation-table";
|
||||||
|
|
@ -10,6 +9,7 @@ import { useState } from "react";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { PlusIcon } from "lucide-react";
|
import { PlusIcon } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import InternalTable from "./internal/components/internal-table";
|
||||||
|
|
||||||
const CommunicationPage = () => {
|
const CommunicationPage = () => {
|
||||||
const [tab, setTab] = useState("Komunikasi");
|
const [tab, setTab] = useState("Komunikasi");
|
||||||
|
|
|
||||||
|
|
@ -36,22 +36,35 @@ import {
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import { getFaqList } from "@/service/master/faq";
|
import columns from "./columns";
|
||||||
import columns from "./column";
|
import {
|
||||||
|
listDataAudio,
|
||||||
|
listDataImage,
|
||||||
|
listDataVideo,
|
||||||
|
} from "@/service/content/content";
|
||||||
|
import {
|
||||||
|
getTicketingCollaborationPagination,
|
||||||
|
getTicketingEscalationPagination,
|
||||||
|
listTicketingInternal,
|
||||||
|
} from "@/service/communication/communication";
|
||||||
|
|
||||||
const FaqTable = () => {
|
const CollaborationSpvTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -64,13 +77,17 @@ const FaqTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [limit, setLimit] = React.useState(10);
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -94,7 +111,7 @@ const FaqTable = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get('page');
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
setPage(Number(pageFromUrl));
|
setPage(Number(pageFromUrl));
|
||||||
}
|
}
|
||||||
|
|
@ -102,54 +119,91 @@ const FaqTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit]);
|
}, [page, showData]);
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await getFaqList();
|
const res = await getTicketingCollaborationPagination(
|
||||||
const contentData = res?.data?.data;
|
page - 1,
|
||||||
|
showData,
|
||||||
|
search
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
setTotalData(contentData?.totalElements || contentData?.length);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(contentData?.totalPages || 1);
|
setTotalPage(data?.totalPages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching tasks:", error);
|
console.error("Error fetching tasks:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center px-6">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Judul..."
|
placeholder="Search pertanyaan..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<DropdownMenu>
|
||||||
<Input
|
<DropdownMenuTrigger asChild>
|
||||||
placeholder="Filter Status..."
|
<Button size="md" variant="outline">
|
||||||
value={
|
1 - {showData} Data
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
</Button>
|
||||||
}
|
</DropdownMenuTrigger>
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
<DropdownMenuRadioGroup
|
||||||
}
|
value={showData}
|
||||||
className="max-w-sm "
|
onValueChange={setShowData}
|
||||||
/>
|
>
|
||||||
</div>
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|
@ -191,9 +245,13 @@ const FaqTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaqTable;
|
export default CollaborationSpvTable;
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuItem,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "question",
|
|
||||||
header: "Question",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "answer",
|
|
||||||
header: "Answer",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Actions",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "Nos",
|
||||||
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Pertanyaan",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "commentFromUserName",
|
||||||
|
header: "CreateBy",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "Type",
|
||||||
|
header: "Channel",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const type = row.original.type;
|
||||||
|
return <span className="normal-case">{type?.name || "N/A"}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Waktu",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdAt = row.getValue("createdAt") as
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
const formattedDate =
|
||||||
|
createdAt && !isNaN(new Date(createdAt).getTime())
|
||||||
|
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
||||||
|
: "-";
|
||||||
|
return <span className="whitespace-nowrap">{formattedDate}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "isActive",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive
|
||||||
|
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive
|
||||||
|
const statusStyles = isActive
|
||||||
|
? "bg-green-100 text-green-600" // Gaya untuk "Open"
|
||||||
|
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge className={`rounded-full px-5 ${statusStyles}`}>{status}</Badge>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/supervisor/communications/forward/detail/${row.original.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center"
|
||||||
|
>
|
||||||
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
|
Detail
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormCollaboration from "@/components/form/communication/collaboration-form";
|
||||||
|
|
||||||
|
const CollaborationCreatePage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormCollaboration />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollaborationCreatePage;
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
"use client";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import Select, { MultiValue } from "react-select";
|
||||||
|
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import {
|
||||||
|
getCuratorUser,
|
||||||
|
saveCollaborationTeams,
|
||||||
|
} from "@/service/communication/communication";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { stringify } from "querystring";
|
||||||
|
|
||||||
|
const assigneeOptions = [
|
||||||
|
{ value: "mahedi", label: "Mahedi Amin", image: "/images/avatar/av-1.svg" },
|
||||||
|
{ value: "sovo", label: "Sovo Haldar", image: "/images/avatar/av-2.svg" },
|
||||||
|
{
|
||||||
|
value: "rakibul",
|
||||||
|
label: "Rakibul Islam",
|
||||||
|
image: "/images/avatar/av-3.svg",
|
||||||
|
},
|
||||||
|
{ value: "pritom", label: "Pritom Miha", image: "/images/avatar/av-4.svg" },
|
||||||
|
];
|
||||||
|
export default function UsersCard(props: { team: any; fetchData: () => void }) {
|
||||||
|
const params = useParams();
|
||||||
|
const id = params?.id;
|
||||||
|
const [openSearch, setOpenSearch] = useState(false);
|
||||||
|
const [selectedUsers, setSelectedUsers] = useState<any>([]);
|
||||||
|
const [allUser, setAllUser] = useState([]);
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getUser() {
|
||||||
|
const res = await getCuratorUser();
|
||||||
|
|
||||||
|
if (res?.data !== null) {
|
||||||
|
const rawUser = res?.data?.data?.content;
|
||||||
|
const optionArr: any = [];
|
||||||
|
|
||||||
|
rawUser.map((option: any) => {
|
||||||
|
optionArr.push({
|
||||||
|
id: option.id,
|
||||||
|
label: option.username,
|
||||||
|
value: option.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setAllUser(optionArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getUser();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const inviteHandle = () => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Undang Pengguna Ke Kolom Diskusi?",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
inviteUser();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async function inviteUser() {
|
||||||
|
const userId: any = [];
|
||||||
|
|
||||||
|
selectedUsers?.map((user: any) => {
|
||||||
|
userId.push(user.id);
|
||||||
|
});
|
||||||
|
loading();
|
||||||
|
const res = await saveCollaborationTeams(String(id), userId);
|
||||||
|
|
||||||
|
if (res?.error) {
|
||||||
|
toast({ title: stringify(res?.message) });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
|
||||||
|
toast({ title: "Berhasil menambahkan user" });
|
||||||
|
props.fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col bg-white p-4 rounded-t-xl w-[30%]">
|
||||||
|
<div className="flex justify-between items-center mb-3">
|
||||||
|
<div className="flex flex-row gap-3 items-center">
|
||||||
|
<p>Kolaborator</p>
|
||||||
|
<p className="p-2 rounded-full bg-slate-300 w-5 h-5 flex justify-center items-center">
|
||||||
|
{props?.team?.length}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => setOpenSearch(!openSearch)}>
|
||||||
|
<Icon icon="material-symbols:search" width={24} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{openSearch && (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Select
|
||||||
|
className="react-select my-2 transition-shadow"
|
||||||
|
classNamePrefix="select"
|
||||||
|
options={allUser}
|
||||||
|
isMulti
|
||||||
|
onChange={(selectedOption) => setSelectedUsers(selectedOption)}
|
||||||
|
placeholder="Select users"
|
||||||
|
/>
|
||||||
|
{selectedUsers?.length > 0 && (
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button color="primary" size="sm" onClick={inviteHandle}>
|
||||||
|
Invie
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="mt-3 flex flex-col gap-2">
|
||||||
|
{props?.team?.map((list: any) => (
|
||||||
|
<div key={list.id} className="flex flex-col">
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<p className="text-sm">{list?.fullname}</p>
|
||||||
|
<p className="text-xs">{list?.username}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator className="my-2" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
"use client";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
|
import {
|
||||||
|
deleteCollabDiscussion,
|
||||||
|
getCollabDiscussion,
|
||||||
|
getCuratorUser,
|
||||||
|
getTicketCollaborationTeams,
|
||||||
|
saveCollabDiscussion,
|
||||||
|
} from "@/service/communication/communication";
|
||||||
|
import {
|
||||||
|
getLocaleTimestamp,
|
||||||
|
htmlToString,
|
||||||
|
textEllipsis,
|
||||||
|
} from "@/utils/globals";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
import { stringify } from "querystring";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import UsersCard from "./component/users-card";
|
||||||
|
|
||||||
|
export default function CollaborationPage() {
|
||||||
|
const params = useParams();
|
||||||
|
const id = params?.id;
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [detailTickets, setDetailTickets] = useState<any>();
|
||||||
|
const [teams, setTeams] = useState([]);
|
||||||
|
const [listDiscussion, setListDiscussion] = useState([]);
|
||||||
|
const [replyValue, setReplyValue] = useState("");
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getCollabTeam() {
|
||||||
|
if (id != undefined) {
|
||||||
|
const res = await getTicketCollaborationTeams(String(id));
|
||||||
|
|
||||||
|
if (res?.data !== null) {
|
||||||
|
console.log("response teams:", res);
|
||||||
|
|
||||||
|
setDetailTickets(res?.data?.data?.tickets);
|
||||||
|
setTeams(res?.data?.data?.userTeam);
|
||||||
|
console.log("userteam", res?.data?.data?.userTeam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
getCollabTeam();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
async function initState() {
|
||||||
|
if (id != undefined) {
|
||||||
|
loading();
|
||||||
|
const responseGet = await getCollabDiscussion(String(id));
|
||||||
|
console.log("get res data", responseGet?.data?.data);
|
||||||
|
close();
|
||||||
|
setListDiscussion(responseGet?.data?.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteDataSuggestion(dataId: string | number) {
|
||||||
|
loading();
|
||||||
|
const response = await deleteCollabDiscussion(dataId);
|
||||||
|
console.log(response);
|
||||||
|
toast({
|
||||||
|
title: "Sukses hapus",
|
||||||
|
});
|
||||||
|
setReplyValue("");
|
||||||
|
close();
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendDiscussionParent() {
|
||||||
|
if (replyValue?.length > 0) {
|
||||||
|
loading();
|
||||||
|
const data = {
|
||||||
|
ticketId: id,
|
||||||
|
message: replyValue,
|
||||||
|
parentId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await saveCollabDiscussion(data);
|
||||||
|
if (response?.error) {
|
||||||
|
toast({
|
||||||
|
title: stringify(response?.message),
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
toast({
|
||||||
|
title: "Sukses Komen",
|
||||||
|
});
|
||||||
|
setReplyValue("");
|
||||||
|
|
||||||
|
close();
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="flex flex-row gap-4 pt-6">
|
||||||
|
<div className="flex flex-col gap-4 w-[70%]">
|
||||||
|
<div className="bg-white flex flex-col rounded-t-xl">
|
||||||
|
<div className="flex flex-row gap-2 bg-slate-300 rounded-t-xl p-4 items-center">
|
||||||
|
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<p className="text-sm">
|
||||||
|
<span className="font-semibold">
|
||||||
|
{detailTickets?.commentFromUserName}
|
||||||
|
</span>{" "}
|
||||||
|
mengirimkan komentar untuk
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={detailTickets?.feed?.permalink_url}
|
||||||
|
className="font-weight-bold"
|
||||||
|
>
|
||||||
|
{textEllipsis(detailTickets?.feed?.message, 25)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs">
|
||||||
|
{" "}
|
||||||
|
{`${new Date(detailTickets?.createdAt).getDate()}-${
|
||||||
|
new Date(detailTickets?.createdAt).getMonth() + 1
|
||||||
|
}-${new Date(
|
||||||
|
detailTickets?.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
detailTickets?.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
|
detailTickets?.createdAt
|
||||||
|
).getMinutes()}`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-4"> {htmlToString(detailTickets?.message)}</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white rounded-b-xl flex flex-col p-4">
|
||||||
|
<p className="font-bold text-sm">{listDiscussion?.length} Respon</p>
|
||||||
|
<Separator className="my-2" />
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2 max-h-[360px]">
|
||||||
|
{listDiscussion?.length > 1 ? (
|
||||||
|
listDiscussion?.map((data: any) => (
|
||||||
|
<div key={data?.id} className="flex flex-col ">
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||||
|
<div className="flex flex-col w-full">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<p className="text-sm font-semibold">
|
||||||
|
{data?.messageFrom?.fullname}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs">
|
||||||
|
{getLocaleTimestamp(new Date(data?.createdAt))}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<p className="text-sm">{data?.message}</p>
|
||||||
|
<a
|
||||||
|
onClick={() => deleteDataSuggestion(data?.id)}
|
||||||
|
className="text-destructive cursor-pointer text-xs"
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator className="my-2" />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p>Belum Ada Tanggapan</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Textarea
|
||||||
|
value={replyValue}
|
||||||
|
onChange={(e) => setReplyValue(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end py-3">
|
||||||
|
<Button
|
||||||
|
className="w-fit"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={sendDiscussionParent}
|
||||||
|
>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>{" "}
|
||||||
|
<UsersCard team={teams} fetchData={() => initState()} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,37 +1,25 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import FaqTable from "./components/table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus, PlusIcon } from "lucide-react";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import CollaborationSpvTable from "../collaboration/components/collabroation-table";
|
||||||
|
|
||||||
const FaqPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<Card>
|
<div className="flex justify-between py-3">
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
<p className="text-lg">Kolaborasi</p>
|
||||||
<CardTitle>
|
|
||||||
<div className="flex items-center">
|
<Link href="/supervisor/communications/collaboration/create">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<Button color="primary" size="md">
|
||||||
FAQ Data
|
<PlusIcon />
|
||||||
</div>
|
Kolaborasi baru
|
||||||
<div className="flex-none">
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
|
||||||
New FAQ
|
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<CollaborationSpvTable />
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<FaqTable />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuItem,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "question",
|
|
||||||
header: "Question",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "answer",
|
|
||||||
header: "Answer",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Actions",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Pertanyaan",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "commentFromUserName",
|
||||||
|
header: "Penerima",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "type",
|
||||||
|
header: "Penerima",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const type = row.original.type; // Akses properti category
|
||||||
|
return <span className="normal-case">{type?.name || "N/A"}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Waktu",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdAt = row.getValue("createdAt") as
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
const formattedDate =
|
||||||
|
createdAt && !isNaN(new Date(createdAt).getTime())
|
||||||
|
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
||||||
|
: "-";
|
||||||
|
return <span className="whitespace-nowrap">{formattedDate}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "isActive",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive
|
||||||
|
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive
|
||||||
|
const statusStyles = isActive
|
||||||
|
? "bg-green-100 text-green-600" // Gaya untuk "Open"
|
||||||
|
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge className={`rounded-full px-5 ${statusStyles}`}>{status}</Badge>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<Link
|
||||||
|
href={`/supervisor/communications/forward/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
|
View
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -36,22 +36,34 @@ import {
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import { getFaqList } from "@/service/master/faq";
|
import columns from "./columns";
|
||||||
import columns from "./column";
|
import {
|
||||||
|
listDataAudio,
|
||||||
|
listDataImage,
|
||||||
|
listDataVideo,
|
||||||
|
} from "@/service/content/content";
|
||||||
|
import {
|
||||||
|
getTicketingEscalationPagination,
|
||||||
|
listTicketingInternal,
|
||||||
|
} from "@/service/communication/communication";
|
||||||
|
|
||||||
const FaqTable = () => {
|
const EscalationTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -64,13 +76,17 @@ const FaqTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [limit, setLimit] = React.useState(10);
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -94,7 +110,7 @@ const FaqTable = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get('page');
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
setPage(Number(pageFromUrl));
|
setPage(Number(pageFromUrl));
|
||||||
}
|
}
|
||||||
|
|
@ -102,30 +118,58 @@ const FaqTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit]);
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await getFaqList();
|
const res = await getTicketingEscalationPagination(
|
||||||
const contentData = res?.data?.data;
|
page - 1,
|
||||||
|
Number(showData),
|
||||||
|
search
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
setTotalData(contentData?.totalElements || contentData?.length);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(contentData?.totalPages || 1);
|
setTotalPage(data?.totalPages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching tasks:", error);
|
console.error("Error fetching tasks:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value); // Perbarui state search
|
||||||
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center px-6">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -134,21 +178,39 @@ const FaqTable = () => {
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Judul..."
|
placeholder="Search Judul..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<DropdownMenu>
|
||||||
<Input
|
<DropdownMenuTrigger asChild>
|
||||||
placeholder="Filter Status..."
|
<Button size="md" variant="outline">
|
||||||
value={
|
1 - {showData} Data
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
</Button>
|
||||||
}
|
</DropdownMenuTrigger>
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
<DropdownMenuRadioGroup
|
||||||
}
|
value={showData}
|
||||||
className="max-w-sm "
|
onValueChange={setShowData}
|
||||||
/>
|
>
|
||||||
</div>
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
|
|
@ -191,9 +253,13 @@ const FaqTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaqTable;
|
export default EscalationTable;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTask from "@/components/form/task/task-form";
|
||||||
|
import FormTaskDetail from "@/components/form/task/task-detail-form";
|
||||||
|
import FormDetailInternal from "@/components/form/communication/internal-detail-form";
|
||||||
|
import FormDetailEscalation from "@/components/form/communication/escalation-detail-form";
|
||||||
|
|
||||||
|
const EscalationDetailPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormDetailEscalation />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EscalationDetailPage;
|
||||||
|
|
@ -1,37 +1,18 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import FaqTable from "./components/table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
|
import EscalationTable from "./components/escalation-table";
|
||||||
|
|
||||||
const FaqPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<Card>
|
<div className="flex justify-between py-3">
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
<p className="text-lg">Eskalasi</p>
|
||||||
<CardTitle>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
|
||||||
FAQ Data
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<EscalationTable />
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
|
||||||
New FAQ
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<FaqTable />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuItem,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "question",
|
|
||||||
header: "Question",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "answer",
|
|
||||||
header: "Answer",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "actions",
|
|
||||||
accessorKey: "action",
|
|
||||||
header: "Actions",
|
|
||||||
enableHiding: false,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Open menu</span>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default columns;
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Pertanyaan",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case"> {row.getValue("title")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdBy",
|
||||||
|
header: "Pengirim",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdBy = row.original.createdBy; // Akses properti category
|
||||||
|
return (
|
||||||
|
<span className="normal-case">{createdBy?.fullname || "N/A"}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "sendTo",
|
||||||
|
header: "Penerima",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const sendTo = row.original.sendTo; // Akses properti category
|
||||||
|
return <span className="normal-case">{sendTo?.fullname || "N/A"}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Waktu",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdAt = row.getValue("createdAt") as
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
const formattedDate =
|
||||||
|
createdAt && !isNaN(new Date(createdAt).getTime())
|
||||||
|
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
||||||
|
: "-";
|
||||||
|
return <span className="whitespace-nowrap">{formattedDate}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<Link
|
||||||
|
href={`/supervisor/communications/internal/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
|
View
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={`/supervisor/communications/internal/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -35,23 +35,34 @@ import {
|
||||||
Trash2,
|
Trash2,
|
||||||
TrendingDown,
|
TrendingDown,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
|
UploadIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import { getFaqList } from "@/service/master/faq";
|
import columns from "./columns";
|
||||||
import columns from "./column";
|
import {
|
||||||
|
listDataAudio,
|
||||||
|
listDataImage,
|
||||||
|
listDataVideo,
|
||||||
|
} from "@/service/content/content";
|
||||||
|
import { listTicketingInternal } from "@/service/communication/communication";
|
||||||
|
import { Link } from "@/components/navigation";
|
||||||
|
|
||||||
const FaqTable = () => {
|
const InternalSpvTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -64,13 +75,19 @@ const FaqTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [limit, setLimit] = React.useState(10);
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
|
|
@ -94,7 +111,7 @@ const FaqTable = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get('page');
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
setPage(Number(pageFromUrl));
|
setPage(Number(pageFromUrl));
|
||||||
}
|
}
|
||||||
|
|
@ -102,21 +119,46 @@ const FaqTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, limit]);
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const res = await getFaqList();
|
const res = await listTicketingInternal(
|
||||||
const contentData = res?.data?.data;
|
page - 1,
|
||||||
|
Number(showData),
|
||||||
|
search
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * limit + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
setTotalData(contentData?.totalElements || contentData?.length);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(contentData?.totalPages || 1);
|
setTotalPage(data?.totalPages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching tasks:", error);
|
console.error("Error fetching tasks:", error);
|
||||||
}
|
}
|
||||||
|
|
@ -124,8 +166,8 @@ const FaqTable = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto ">
|
<div className="w-full overflow-x-auto ">
|
||||||
<div className="flex justify-between items-center px-6">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-4 w-4 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
|
@ -134,21 +176,39 @@ const FaqTable = () => {
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Judul..."
|
placeholder="Search Judul..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<DropdownMenu>
|
||||||
<Input
|
<DropdownMenuTrigger asChild>
|
||||||
placeholder="Filter Status..."
|
<Button size="md" variant="outline">
|
||||||
value={
|
1 - {showData} Data
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
</Button>
|
||||||
}
|
</DropdownMenuTrigger>
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
<DropdownMenuRadioGroup
|
||||||
}
|
value={showData}
|
||||||
className="max-w-sm "
|
onValueChange={setShowData}
|
||||||
/>
|
>
|
||||||
</div>
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
|
|
@ -191,9 +251,13 @@ const FaqTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaqTable;
|
export default InternalSpvTable;
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormInternal from "@/components/form/communication/internal-form";
|
||||||
|
|
||||||
|
const InternalCreatePage = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormInternal />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InternalCreatePage;
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTask from "@/components/form/task/task-form";
|
||||||
|
import FormTaskDetail from "@/components/form/task/task-detail-form";
|
||||||
|
import FormDetailInternal from "@/components/form/communication/internal-detail-form";
|
||||||
|
|
||||||
|
const InternalDetailPage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormDetailInternal />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InternalDetailPage;
|
||||||
|
|
@ -1,37 +1,27 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import FaqTable from "./components/table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Plus } from "lucide-react";
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { PlusIcon } from "lucide-react";
|
||||||
|
import InternalSpvTable from "../internal/components/internal-table";
|
||||||
|
|
||||||
const FaqPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card>
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
<div className="flex justify-between py-3">
|
||||||
<CardTitle>
|
<p className="text-lg">Pertanyaan Internal</p>
|
||||||
<div className="flex items-center">
|
<Link href="/supervisor/communications/internal/create">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<Button color="primary" size="md">
|
||||||
FAQ Data
|
<PlusIcon />
|
||||||
</div>
|
Pertanyaan baru
|
||||||
<div className="flex-none">
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
<Plus className="w-6 h-6 me-1.5"/>
|
|
||||||
New FAQ
|
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<InternalSpvTable />
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<FaqTable />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import FormTask from "@/components/form/task/task-form";
|
||||||
|
import FormTaskDetail from "@/components/form/task/task-detail-form";
|
||||||
|
import FormDetailInternal from "@/components/form/communication/internal-detail-form";
|
||||||
|
import FormEditInternal from "@/components/form/communication/internal-edit-form";
|
||||||
|
|
||||||
|
const InternalUpdatePage = async () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<FormEditInternal />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InternalUpdatePage;
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Link, usePathname, useRouter } from "@/i18n/routing";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { deleteDataFAQ } from "@/service/settings/settings";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { deleteQuestion } from "@/service/communication/communication";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span> {row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "message",
|
||||||
|
header: "Pertanyaan",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case"> {row.getValue("message")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "commentFromUserName",
|
||||||
|
header: "Penerima",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="normal-case">{row.getValue("commentFromUserName")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "type",
|
||||||
|
header: "Channel",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const type = row.original.type; // Akses properti category
|
||||||
|
return <span className="normal-case">{type?.name || "N/A"}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Waktu",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdAt = row.getValue("createdAt") as
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
const formattedDate =
|
||||||
|
createdAt && !isNaN(new Date(createdAt).getTime())
|
||||||
|
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
||||||
|
: "-";
|
||||||
|
return <span className="whitespace-nowrap">{formattedDate}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "isActive",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isActive = row.getValue("isActive") as boolean; // Ambil nilai isActive
|
||||||
|
const status = isActive ? "Open" : "Closed"; // Tentukan teks berdasarkan isActive
|
||||||
|
const statusStyles = isActive
|
||||||
|
? "bg-green-100 text-green-600" // Gaya untuk "Open"
|
||||||
|
: "bg-red-100 text-red-600"; // Gaya untuk "Closed"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge className={`rounded-full px-5 ${statusStyles}`}>{status}</Badge>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const router = useRouter();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
|
const questionDelete = async (id: string) => {
|
||||||
|
const response = await deleteQuestion(id);
|
||||||
|
console.log(response);
|
||||||
|
if (response?.error) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
variant: "destructive",
|
||||||
|
description: "Gagal Delete",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
toast({
|
||||||
|
title: "Sukses",
|
||||||
|
description: "Berhasil Delete",
|
||||||
|
});
|
||||||
|
router.push(`${pathname}?dataChange=true`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteValidation = async (id: string) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Delete Data",
|
||||||
|
text: "Apakah Anda yakin ingin menghapus data ini?",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Delete",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
questionDelete(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<Link
|
||||||
|
href={`/supervisor/communications/internal/update/${row?.original?.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center">
|
||||||
|
Jawab
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={`/supervisor/communications/forward/detail/${row?.original?.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none items-center">
|
||||||
|
Eskalasi
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<a onClick={() => deleteValidation(row?.original?.id)}>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-red-500 focus:text-primary-foreground rounded-none items-center">
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</a>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,320 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useParams, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./columns";
|
||||||
|
import {
|
||||||
|
listDataAudio,
|
||||||
|
listDataImage,
|
||||||
|
listDataVideo,
|
||||||
|
} from "@/service/content/content";
|
||||||
|
import {
|
||||||
|
getQuestionPagination,
|
||||||
|
getTicketingEscalationPagination,
|
||||||
|
listTicketingInternal,
|
||||||
|
} from "@/service/communication/communication";
|
||||||
|
import { usePathname, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
|
interface statisticType {
|
||||||
|
incomingTotal: number;
|
||||||
|
createdTicketTotal: number;
|
||||||
|
todayIncomingTotal: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QuestionsTable = (props: {
|
||||||
|
statisticData: (data: statisticType) => void;
|
||||||
|
}) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const params = useParams();
|
||||||
|
const title = params?.title;
|
||||||
|
const dataChange = searchParams?.get("dataChange");
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [search, setSearch] = React.useState<string>("");
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (dataChange) {
|
||||||
|
router.push(pathname);
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, [dataChange]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get("page");
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
setPagination({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: Number(showData),
|
||||||
|
});
|
||||||
|
}, [page, showData]);
|
||||||
|
|
||||||
|
let typingTimer: any;
|
||||||
|
const doneTypingInterval = 1500;
|
||||||
|
|
||||||
|
const handleKeyUp = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function doneTyping() {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
const typeNow =
|
||||||
|
title === "comment"
|
||||||
|
? "1"
|
||||||
|
: title === "facebook"
|
||||||
|
? "2"
|
||||||
|
: title === "instagram"
|
||||||
|
? "3"
|
||||||
|
: title === "x"
|
||||||
|
? "4"
|
||||||
|
: title === "youtube"
|
||||||
|
? "5"
|
||||||
|
: title === "emergency"
|
||||||
|
? "6"
|
||||||
|
: title === "email"
|
||||||
|
? "7"
|
||||||
|
: title === "inbox"
|
||||||
|
? "8"
|
||||||
|
: title === "whatsapp"
|
||||||
|
? "9"
|
||||||
|
: title === "tiktok"
|
||||||
|
? "10"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getQuestionPagination(
|
||||||
|
search,
|
||||||
|
page - 1,
|
||||||
|
typeNow,
|
||||||
|
showData
|
||||||
|
);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
const contentData = data?.page?.content;
|
||||||
|
console.log("contentDatassss : ", data);
|
||||||
|
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
});
|
||||||
|
setDataTable(contentData);
|
||||||
|
props.statisticData(data?.statistic);
|
||||||
|
setTotalData(data?.page?.totalElements);
|
||||||
|
setTotalPage(data?.page?.totalPages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto bg-white px-6 py-10">
|
||||||
|
<div className="text-xl capitalize flex flex-row gap-5 items-center">
|
||||||
|
{title == "all"
|
||||||
|
? "Pertanyaan Masuk"
|
||||||
|
: title === "inbox"
|
||||||
|
? "Pesan Masuk"
|
||||||
|
: title == "comment"
|
||||||
|
? "Komentar Konten"
|
||||||
|
: title === "emergency"
|
||||||
|
? "Emergency Issue"
|
||||||
|
: title}{" "}
|
||||||
|
<Badge color="secondary" className="text-xl font-normal">
|
||||||
|
{totalData}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center mt-6">
|
||||||
|
<div className="mt-3 flex flex-row items-center gap-2">
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
|
</InputGroupText>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search Judul..."
|
||||||
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button size="md" variant="outline">
|
||||||
|
1 - {showData} Data
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56 text-sm">
|
||||||
|
<DropdownMenuRadioGroup
|
||||||
|
value={showData}
|
||||||
|
onValueChange={setShowData}
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioItem value="10">
|
||||||
|
1 - 10 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="20">
|
||||||
|
1 - 20 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="25">
|
||||||
|
1 - 25 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="50">
|
||||||
|
1 - 50 Data
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QuestionsTable;
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
"use client";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import QuestionsTable from "./components/questions-table";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
interface statisticType {
|
||||||
|
incomingTotal: number;
|
||||||
|
createdTicketTotal: number;
|
||||||
|
todayIncomingTotal: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Questions() {
|
||||||
|
const [statisticData, setStatisticData] = useState<statisticType>({
|
||||||
|
incomingTotal: 0,
|
||||||
|
createdTicketTotal: 0,
|
||||||
|
todayIncomingTotal: 0,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SiteBreadcrumb />
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="flex flex-row gap-4">
|
||||||
|
<div className="flex flex-col gap-4 w-[30%]">
|
||||||
|
<div className="flex flex-row gap-10 bg-white rounded-sm px-10 py-6 items-center">
|
||||||
|
<Icon icon="ion:ticket" width={56} />
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<p className="text-3xl ">{statisticData.todayIncomingTotal}</p>
|
||||||
|
<p className="text-sm">Pertanyaan hari ini</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-10 bg-white rounded-sm px-10 py-6 items-center">
|
||||||
|
<Icon icon="ion:ticket" width={56} />
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<p className="text-3xl ">{statisticData.incomingTotal}</p>
|
||||||
|
<p className="text-sm">Pertanyaan masuk</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-10 bg-white rounded-sm px-10 py-6 items-center">
|
||||||
|
<Icon icon="ion:ticket" width={56} />
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<p className="text-3xl ">{statisticData.createdTicketTotal}</p>
|
||||||
|
<p className="text-sm">Tiket dibuat</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-[70%] bg-white py-6 px-10 rounded-sm flex flex-col gap-5 text-sm">
|
||||||
|
<p>Pertanyaan masuk berdasarkan sumber:</p>
|
||||||
|
<div className="grid grid-cols-2 gap-8 text-[#2563eb]">
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/emergency"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:emergency-home-rounded"
|
||||||
|
width={24}
|
||||||
|
color="#2563eb"
|
||||||
|
/>
|
||||||
|
Emergency Issue
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/email"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon icon="ic:baseline-email" width={22} color="#2563eb" />
|
||||||
|
Email
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/facebook"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon icon="dashicons:facebook" width={24} color="#2563eb" />
|
||||||
|
Facebook
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/x"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon icon="proicons:x-twitter" width={24} color="#2563eb" />X
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/instagram"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="ant-design:instagram-filled"
|
||||||
|
width={24}
|
||||||
|
color="#2563eb"
|
||||||
|
/>
|
||||||
|
Instagram
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/whatsapp"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width={24} color="#2563eb" />
|
||||||
|
Whatsapp
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/youtube"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon icon="mdi:youtube" width={24} color="#2563eb" />
|
||||||
|
Youtube
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/inbox"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:inbox"
|
||||||
|
width={24}
|
||||||
|
color="#2563eb"
|
||||||
|
/>
|
||||||
|
Pesan Masuk
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/supervisor/communications/questions/comment"
|
||||||
|
className="flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols-light:comment-rounded"
|
||||||
|
width={24}
|
||||||
|
color="#2563eb"
|
||||||
|
/>
|
||||||
|
Komentar Konten
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<QuestionsTable statisticData={(data) => setStatisticData(data)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import NewContent from "@/components/landing-page/new-content";
|
import NewContent from "@/components/landing-page/new-content";
|
||||||
|
|
@ -10,7 +10,11 @@ import { BarWave } from "react-cssfx-loading";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
const DetailAudio = () => {
|
const DetailAudio = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -29,8 +33,17 @@ const DetailAudio = () => {
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -41,6 +54,8 @@ const DetailAudio = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailAudio", response);
|
console.log("detailAudio", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -216,6 +231,59 @@ const DetailAudio = () => {
|
||||||
{ label: "XS", value: "800 x 450 px" },
|
{ label: "XS", value: "800 x 450 px" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="min-h-screen px-4 md:px-24 py-4">
|
<div className="min-h-screen px-4 md:px-24 py-4">
|
||||||
|
|
@ -310,6 +378,39 @@ const DetailAudio = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,11 @@ import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const Galery = (props: any) => {
|
const Galery = (props: any) => {
|
||||||
const [profile, setProfile] = useState<any>();
|
const [profile, setProfile] = useState<any>();
|
||||||
|
|
@ -20,7 +24,6 @@ const Galery = (props: any) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
// const { id } = router.query;
|
|
||||||
const page: any = searchParams?.get("page");
|
const page: any = searchParams?.get("page");
|
||||||
const title = searchParams?.get("title");
|
const title = searchParams?.get("title");
|
||||||
const category = searchParams?.get("category");
|
const category = searchParams?.get("category");
|
||||||
|
|
@ -28,7 +31,6 @@ const Galery = (props: any) => {
|
||||||
const { isInstitute, instituteId } = props;
|
const { isInstitute, instituteId } = props;
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userRoleId = getCookiesDecrypt("urie");
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [totalContent, setTotalContent] = useState();
|
const [totalContent, setTotalContent] = useState();
|
||||||
const [categoryFilter] = useState([]);
|
const [categoryFilter] = useState([]);
|
||||||
const [formatFilter] = useState([]);
|
const [formatFilter] = useState([]);
|
||||||
|
|
@ -42,6 +44,10 @@ const Galery = (props: any) => {
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
const [, setCopySuccess] = useState("");
|
const [, setCopySuccess] = useState("");
|
||||||
const [, setWishlistId] = useState();
|
const [, setWishlistId] = useState();
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDataVideo();
|
getDataVideo();
|
||||||
|
|
@ -219,8 +225,44 @@ const Galery = (props: any) => {
|
||||||
// toast.success("Link Berhasil Di Copy");
|
// toast.success("Link Berhasil Di Copy");
|
||||||
};
|
};
|
||||||
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [hasMounted, setHasMounted] = useState(false);
|
||||||
// Hooks
|
// Hooks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHasMounted(true);
|
setHasMounted(true);
|
||||||
|
|
@ -236,10 +278,10 @@ const Galery = (props: any) => {
|
||||||
<SidebarManagement />
|
<SidebarManagement />
|
||||||
<div className="w-2/3 p-12">
|
<div className="w-2/3 p-12">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold">Galeri Saya</h1>
|
<h1 className="text-2xl font-semibold mb-3">Galeri Saya</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col p-4">
|
<div className="flex flex-col">
|
||||||
<div className="mx-auto w-full max-w-7xl justify-start flex px-5 flex-col lg:flex-row gap-5 mb-4">
|
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col lg:flex-row gap-5 mb-4">
|
||||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|
@ -272,17 +314,54 @@ const Galery = (props: any) => {
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-0 lg:px-10">
|
<div className="">
|
||||||
{selectedTab == "video" ? (
|
{selectedTab == "video" ? (
|
||||||
contentVideo?.length > 0 ? (
|
contentVideo?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentVideo?.map((video: any) => (
|
{contentVideo?.map((video: any) => (
|
||||||
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={video?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||||
|
<div>
|
||||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg " />
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="flex justify-end items-end place-items-end">
|
||||||
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button className="w-full flex items-center gap-2">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(video?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
@ -296,11 +375,7 @@ const Galery = (props: any) => {
|
||||||
contentAudio?.length > 0 ? (
|
contentAudio?.length > 0 ? (
|
||||||
<div className=" grid grid-cols-1 gap-6 ">
|
<div className=" grid grid-cols-1 gap-6 ">
|
||||||
{contentAudio?.map((audio: any) => (
|
{contentAudio?.map((audio: any) => (
|
||||||
<Link
|
<div key={audio?.id} className="flex flex-col sm:flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||||
href={`/audio/detail/${audio?.mediaUpload?.slug}`}
|
|
||||||
key={audio?.id}
|
|
||||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
||||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
|
@ -309,8 +384,10 @@ const Galery = (props: any) => {
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col flex-1">
|
<div className="flex flex-col object-cover flex-1">
|
||||||
|
<Link href={`/audio/detail/${audio?.mediaUpload?.slug}`}>
|
||||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center gap-3">
|
<div className="flex items-center justify-center gap-3">
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
|
|
@ -327,7 +404,42 @@ const Galery = (props: any) => {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="flex justify-end items-end place-items-end">
|
||||||
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button className="w-full flex items-center gap-2">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(audio?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -339,12 +451,47 @@ const Galery = (props: any) => {
|
||||||
contentImage?.length > 0 ? (
|
contentImage?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentImage?.map((image: any) => (
|
{contentImage?.map((image: any) => (
|
||||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={image?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full h-full rounded-lg p-0">
|
||||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="flex justify-end items-end place-items-end">
|
||||||
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button className="w-full flex items-center gap-2">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(image?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
@ -357,7 +504,7 @@ const Galery = (props: any) => {
|
||||||
) : contentDocument.length > 0 ? (
|
) : contentDocument.length > 0 ? (
|
||||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{contentDocument?.map((document: any) => (
|
{contentDocument?.map((document: any) => (
|
||||||
<Link href={`/document/detail/${document?.mediaUpload?.slug}`} key={document?.id} className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
<div key={document?.id} className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||||
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
||||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
|
@ -368,7 +515,9 @@ const Galery = (props: any) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col flex-1 gap-2">
|
<div className="flex flex-col flex-1 gap-2">
|
||||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.mediaUpload?.title}</div>
|
<Link href={`/document/detail/${document?.mediaUpload?.slug}`} className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
|
||||||
|
{document?.mediaUpload?.title}
|
||||||
|
</Link>
|
||||||
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
||||||
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||||
|
|
@ -376,7 +525,42 @@ const Galery = (props: any) => {
|
||||||
Download Dokumen
|
Download Dokumen
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="flex justify-end items-end place-items-end">
|
||||||
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button className="w-full flex items-center gap-2">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(document?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,12 @@ import withReactContent from "sweetalert2-react-content";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const Galery = (props: any) => {
|
const Galery = (props: any) => {
|
||||||
const [profile, setProfile] = useState<any>();
|
const [profile, setProfile] = useState<any>();
|
||||||
|
|
@ -23,7 +26,6 @@ const Galery = (props: any) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
// const { id } = router.query;
|
|
||||||
const page: any = searchParams?.get("page");
|
const page: any = searchParams?.get("page");
|
||||||
const title = searchParams?.get("title");
|
const title = searchParams?.get("title");
|
||||||
const category = searchParams?.get("category");
|
const category = searchParams?.get("category");
|
||||||
|
|
@ -45,6 +47,10 @@ const Galery = (props: any) => {
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
const [, setCopySuccess] = useState("");
|
const [, setCopySuccess] = useState("");
|
||||||
const [, setWishlistId] = useState();
|
const [, setWishlistId] = useState();
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDataVideo();
|
getDataVideo();
|
||||||
|
|
@ -225,8 +231,44 @@ const Galery = (props: any) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [hasMounted, setHasMounted] = useState(false);
|
||||||
// Hooks
|
// Hooks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHasMounted(true);
|
setHasMounted(true);
|
||||||
|
|
@ -244,8 +286,8 @@ const Galery = (props: any) => {
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col p-4">
|
<div className="flex flex-col mt-4">
|
||||||
<div className="mx-auto w-full max-w-7xl justify-start flex px-5 flex-col lg:flex-row gap-5 mb-4">
|
<div className="mx-auto w-full max-w-7xl justify-start flex flex-col lg:flex-row gap-5 mb-4">
|
||||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|
@ -278,7 +320,7 @@ const Galery = (props: any) => {
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-0 lg:px-10">
|
<div className="px-2">
|
||||||
{selectedTab == "video" ? (
|
{selectedTab == "video" ? (
|
||||||
contentVideo?.length > 0 ? (
|
contentVideo?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
|
@ -289,37 +331,42 @@ const Galery = (props: any) => {
|
||||||
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
<Link href={`/video/detail/${video?.mediaUpload?.slug}`}>
|
||||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<a className="flex justify-end items-end place-items-end">
|
||||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
</a>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(video?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href={`/content-management/rewrite/create/${video?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<button onClick={() => copyToClip(video.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
<p className="text-base items-center font-semibold mb-3">Bagikan</p>
|
||||||
</button>
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
</DropdownMenuContent>
|
Kirim
|
||||||
</DropdownMenu>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(video?.id)} className="flex items-center gap-2 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -361,35 +408,41 @@ const Galery = (props: any) => {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<a className="flex justify-end items-end place-items-end">
|
||||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
</a>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(audio?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href={`/content-management/rewrite/create/${audio?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<button onClick={() => copyToClip(audio?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
</button>
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
</DropdownMenuContent>
|
Kirim
|
||||||
</DropdownMenu>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(audio?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -402,41 +455,47 @@ const Galery = (props: any) => {
|
||||||
contentImage?.length > 0 ? (
|
contentImage?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentImage?.map((image: any) => (
|
{contentImage?.map((image: any) => (
|
||||||
<Card key={image?.id} className="">
|
<Card key={image?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||||
</Link>
|
</Link>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<a className="flex justify-end items-end place-items-end">
|
||||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
</a>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(image?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href={`/content-management/rewrite/create/${image?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<button onClick={() => copyToClip(image?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
</button>
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
</DropdownMenuContent>
|
Kirim
|
||||||
</DropdownMenu>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(image?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
@ -470,35 +529,41 @@ const Galery = (props: any) => {
|
||||||
Download Dokumen
|
Download Dokumen
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<Popover>
|
||||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
<a>
|
<a className="flex justify-end items-end place-items-end">
|
||||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
</a>
|
</a>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent>
|
<PopoverContent className="w-40">
|
||||||
<DropdownMenuItem>
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
<button onClick={() => handleSaveWishlist(document?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
<Popover>
|
||||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
<PopoverTrigger asChild>
|
||||||
<p className="text-base font-semibold">Simpan</p>
|
<button className="w-full flex items-center gap-2">
|
||||||
</button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href={`/content-management/rewrite/create/${document?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<Icon icon="jam:write" fontSize={20} />
|
|
||||||
<p className="text-base font-semibold">Content Rewrite</p>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
|
||||||
<button onClick={() => copyToClip(document?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
|
||||||
<Icon icon="oi:share" fontSize={20} />
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
<p className="text-base font-semibold">Bagikan</p>
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
</button>
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
</DropdownMenuContent>
|
Kirim
|
||||||
</DropdownMenu>
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(document?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const imageSchema = z.object({
|
||||||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const page = (props: { states?: string }) => {
|
const page = (props: any) => {
|
||||||
const { states } = props;
|
const { states } = props;
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -255,7 +255,7 @@ const page = (props: { states?: string }) => {
|
||||||
const maxRetries = 20;
|
const maxRetries = 20;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const waitForStatusUpdate = async () => {
|
const waitForStatusUpdate: any = async () => {
|
||||||
while (retryCount < maxRetries) {
|
while (retryCount < maxRetries) {
|
||||||
const res = await getDetailArticle(id);
|
const res = await getDetailArticle(id);
|
||||||
const articleData = res?.data?.data;
|
const articleData = res?.data?.data;
|
||||||
|
|
@ -276,7 +276,7 @@ const page = (props: { states?: string }) => {
|
||||||
const articleImagesData = articleData?.imagesUrl?.split(",");
|
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||||
setValue("description", cleanArticleBody || "");
|
setValue("description", cleanArticleBody || "");
|
||||||
setArticleBody(cleanArticleBody || "");
|
setArticleBody(cleanArticleBody || "");
|
||||||
setDetailData(articleData);
|
setDetailArticle(articleData);
|
||||||
setSelectedArticleId(id);
|
setSelectedArticleId(id);
|
||||||
setArticleImages(articleImagesData || []);
|
setArticleImages(articleImagesData || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -404,7 +404,11 @@ const page = (props: { states?: string }) => {
|
||||||
{articleIds.map((id: any, index: any) => (
|
{articleIds.map((id: any, index: any) => (
|
||||||
<p
|
<p
|
||||||
key={index}
|
key={index}
|
||||||
className={`text-black m-1 ${selectedArticleId === id ? "bg-[#31ce36] cursor-pointer border border-[#31ce36]" : "bg-[#48abf7] cursor-pointer border border-[#48abf7]"}`}
|
className={`text-black border border-black w-fit rounded-md p-3 m-1 ${
|
||||||
|
selectedArticleId === id
|
||||||
|
? "text-[#31ce36] w-fit p-3 hover:bg-[#31ce36] hover:text-white cursor-pointer border border-[#31ce36]"
|
||||||
|
: "text-[#48abf7] p-3 hover:text-white hover:bg-[#48abf7] w-fit cursor-pointer border border-[#48abf7]"
|
||||||
|
}`}
|
||||||
onClick={() => handleArticleIdClick(id)}
|
onClick={() => handleArticleIdClick(id)}
|
||||||
>
|
>
|
||||||
{id}
|
{id}
|
||||||
|
|
@ -429,7 +433,7 @@ const page = (props: { states?: string }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{articleBody === null || articleBody === "" ? <div className="text-red-400 px-0 text-sm">Deskripsi tidak boleh kosong*</div> : ""}
|
{articleBody === null || articleBody === "" ? <p className="text-red-400 px-0 text-sm">Deskripsi tidak boleh kosong*</p> : ""}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { close, error, loading } from "@/config/swal";
|
||||||
import { useRouter } from "@/i18n/routing";
|
import { useRouter } from "@/i18n/routing";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { getContentRewrite, getInfoProfile } from "@/service/landing/landing";
|
import { getContentRewrite, getInfoProfile } from "@/service/landing/landing";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useParams, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
|
@ -15,15 +15,17 @@ import { generateDataArticle } from "@/service/content/ai";
|
||||||
import HeaderManagement from "@/components/landing-page/header-management";
|
import HeaderManagement from "@/components/landing-page/header-management";
|
||||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||||
import { saveContentRewrite } from "@/service/content/content";
|
import { saveContentRewrite } from "@/service/content/content";
|
||||||
|
import CustomEditor from "@/components/editor/custom-editor";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
const page = (props: any) => {
|
const page = () => {
|
||||||
const { states } = props;
|
|
||||||
const [profile, setProfile] = useState();
|
const [profile, setProfile] = useState();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [, setLoadingState] = useState(false);
|
const [, setLoadingState] = useState(false);
|
||||||
const id: any = searchParams?.get("title");
|
const getParams = useParams();
|
||||||
|
const id: any = getParams?.id;
|
||||||
const [content, setContent] = useState<any>([]);
|
const [content, setContent] = useState<any>([]);
|
||||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||||
const [listSuggestion, setListSuggestion] = useState();
|
const [listSuggestion, setListSuggestion] = useState();
|
||||||
|
|
@ -199,15 +201,15 @@ const page = (props: any) => {
|
||||||
<div className="text-xl font-bold mb-5">Detail Content Rewrite</div>
|
<div className="text-xl font-bold mb-5">Detail Content Rewrite</div>
|
||||||
<div className="p-8 border border-black rounded-lg">
|
<div className="p-8 border border-black rounded-lg">
|
||||||
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
||||||
{/* {content && ( */}
|
{content && (
|
||||||
<>
|
<>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="mb-3">
|
<div className="mb-1">
|
||||||
<p className="font-semibold">Judul</p>
|
<p className="font-bold text-base">Judul</p>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
className={`w-full p-2 border rounded-md mb-3 border-black ${errors.title ? "is-invalid" : ""}`}
|
className={`w-full mb-3 ${errors.title ? "is-invalid" : ""}`}
|
||||||
{...register("title", {
|
{...register("title", {
|
||||||
value: content?.title,
|
value: content?.title,
|
||||||
})}
|
})}
|
||||||
|
|
@ -216,23 +218,22 @@ const page = (props: any) => {
|
||||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-between lg:flex-row">
|
<div className="flex justify-between gap-3 lg:flex-row">
|
||||||
<div className="w-50%">
|
<div className="w-1/2">
|
||||||
<div className="mb-3">
|
<div className="mb-1">
|
||||||
<p className="font-semibold">Main Keyword</p>
|
<p className="font-semibold">Main Keyword</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" className="border mb-3 w-full rounded-md p-2 border-black" id="mainKeyword" name="mainKeyword" placeholder="Masukan Main Keyword disini!" defaultValue={content?.mainKeyword} />
|
<Input type="text" className="border mb-3 w-full rounded-md p-2 border-black" id="mainKeyword" name="mainKeyword" placeholder="Masukan Main Keyword disini!" defaultValue={content?.mainKeyword} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-50%">
|
<div className="w-1/2">
|
||||||
<div className="mb-3">
|
<div className="mb-1">
|
||||||
<label htmlFor="title" className="font-semibold">
|
<label htmlFor="title" className="font-semibold">
|
||||||
Additional Keyword{" "}
|
Additional Keyword{" "}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<Input
|
||||||
<input
|
|
||||||
className="border mb-3 rounded-md p-2 border-black"
|
className="border mb-3 rounded-md p-2 border-black"
|
||||||
type="text"
|
type="text"
|
||||||
id="additionalKeyword"
|
id="additionalKeyword"
|
||||||
|
|
@ -246,12 +247,12 @@ const page = (props: any) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col justify-between lg:flex-row">
|
<div className="flex gap-3 justify-between lg:flex-row">
|
||||||
<div className="w-50%">
|
<div className="w-1/2">
|
||||||
<div className="font-semibold mb-3">
|
<div className="font-semibold mb-1">
|
||||||
<label htmlFor="metaTitle">Meta Title</label>
|
<label htmlFor="metaTitle">Meta Title</label>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
className="border rounded-md mb-3 p-2 border-black"
|
className="border rounded-md mb-3 p-2 border-black"
|
||||||
id="metaTitle"
|
id="metaTitle"
|
||||||
|
|
@ -263,11 +264,11 @@ const page = (props: any) => {
|
||||||
defaultValue={content?.metaTitle}
|
defaultValue={content?.metaTitle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-50%">
|
<div className="w-1/2">
|
||||||
<div className="font-semibold mb-3">
|
<div className="font-semibold mb-1">
|
||||||
<label htmlFor="metaDescription">Meta Description</label>
|
<label htmlFor="metaDescription">Meta Description</label>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
className="border rounded-md mb-3 p-2 border-black"
|
className="border rounded-md mb-3 p-2 border-black"
|
||||||
id="metaDescription"
|
id="metaDescription"
|
||||||
|
|
@ -284,11 +285,11 @@ const page = (props: any) => {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="font-semibold mb-3">
|
<div className="font-semibold mb-3">
|
||||||
<label htmlFor="description">Deskripsi Artikel</label>
|
<label htmlFor="description">Deskripsi Artikel</label>
|
||||||
|
<CustomEditor onChange={(e: any) => {}} initialData={articleBody || ""} />
|
||||||
</div>
|
</div>
|
||||||
{articleBody === null || articleBody === "" ? <div className="w-full px-0 text-sm">Deskripsi tidak boleh kosong</div> : ""}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
{/* )} */}
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,13 @@ import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const page = (props: any) => {
|
const page = () => {
|
||||||
const [, setProfile] = useState();
|
const [, setProfile] = useState();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
@ -21,23 +26,21 @@ const page = (props: any) => {
|
||||||
const title = searchParams?.get("title");
|
const title = searchParams?.get("title");
|
||||||
const category = searchParams?.get("category");
|
const category = searchParams?.get("category");
|
||||||
const pages = page ? page - 1 : 0;
|
const pages = page ? page - 1 : 0;
|
||||||
|
|
||||||
const { isInstitute, instituteId } = props;
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userRoleId = getCookiesDecrypt("urie");
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [, setGetTotalPage] = useState();
|
const [, setGetTotalPage] = useState();
|
||||||
const [totalContent, setTotalContent] = useState<any>();
|
const [totalContent, setTotalContent] = useState<any>();
|
||||||
const [contentImage, setContentImage] = useState([]);
|
const [contentImage, setContentImage] = useState([]);
|
||||||
|
|
||||||
const [categoryFilter] = useState([]);
|
const [categoryFilter] = useState([]);
|
||||||
const [formatFilter] = useState([]);
|
const [formatFilter] = useState([]);
|
||||||
const [sortBy] = useState();
|
const [sortBy] = useState();
|
||||||
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
const [, setCopySuccess] = useState("");
|
const [, setCopySuccess] = useState("");
|
||||||
|
|
||||||
const [, setWishlistId] = useState();
|
const [, setWishlistId] = useState();
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
|
|
@ -153,6 +156,43 @@ const page = (props: any) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeaderManagement />
|
<HeaderManagement />
|
||||||
|
|
@ -162,16 +202,53 @@ const page = (props: any) => {
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold mb-4">Galeri Content Rewrite</h1>
|
<h1 className="text-2xl font-semibold mb-4">Galeri Content Rewrite</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-0 lg:px-10">
|
<div className="">
|
||||||
{contentImage?.length > 0 ? (
|
{contentImage?.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{contentImage?.map((image: any) => (
|
{contentImage?.map((image: any) => (
|
||||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={image?.id}>
|
||||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||||
|
<div className="">
|
||||||
<Link href={`/content-management/rewrite/detail/${image.id}`}>
|
<Link href={`/content-management/rewrite/detail/${image.id}`}>
|
||||||
<img src={image?.thumbnailUrl} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.thumbnailUrl} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.title}</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.title}</div>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="flex justify-end items-end place-items-end">
|
||||||
|
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-40">
|
||||||
|
<div className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button className="w-full flex items-center gap-2">
|
||||||
|
<Icon icon="oi:share" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold mb-3">Bagikan</p>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<a onClick={() => handleDelete(image?.id)} className="flex items-center gap-1 hover:text-red-800 w-full rounded-lg">
|
||||||
|
<Icon icon="fa:trash" fontSize={20} />
|
||||||
|
<p className="text-base font-semibold">Hapus</p>
|
||||||
|
</a>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import NewContent from "@/components/landing-page/new-content";
|
import NewContent from "@/components/landing-page/new-content";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
const DetailDocument = () => {
|
const DetailDocument = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -29,8 +33,17 @@ const DetailDocument = () => {
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -41,6 +54,8 @@ const DetailDocument = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailDocument", response);
|
console.log("detailDocument", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -216,6 +231,59 @@ const DetailDocument = () => {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="px-4 md:px-24 py-4">
|
<div className="px-4 md:px-24 py-4">
|
||||||
|
|
@ -307,6 +375,39 @@ const DetailDocument = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import NewContent from "@/components/landing-page/new-content";
|
import NewContent from "@/components/landing-page/new-content";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
|
||||||
const DetailInfo = () => {
|
const DetailInfo = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -29,8 +33,17 @@ const DetailInfo = () => {
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -41,6 +54,8 @@ const DetailInfo = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailImage", response);
|
console.log("detailImage", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -219,6 +234,59 @@ const DetailInfo = () => {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="min-h-screen px-4 md:px-24 py-4">
|
<div className="min-h-screen px-4 md:px-24 py-4">
|
||||||
|
|
@ -296,7 +364,7 @@ const DetailInfo = () => {
|
||||||
{sizes.map((size: any) => (
|
{sizes.map((size: any) => (
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
|
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
|
||||||
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
|
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={(e) => setImageSizeSelected(e.target.value)} className="text-red-600 focus:ring-red-600" />
|
||||||
<div className="text-sm">{size.label}</div>
|
<div className="text-sm">{size.label}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
|
|
@ -321,6 +389,39 @@ const DetailInfo = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ const FilterPage = () => {
|
||||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||||
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
|
||||||
<Link href={`/image/detail/${image?.slug}`}>
|
<Link href={`/image/detail/${image?.slug}`}>
|
||||||
<img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
<img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" />
|
||||||
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
|
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
|
||||||
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
||||||
{image?.clickCount}{" "}
|
{image?.clickCount}{" "}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import LayoutProvider from "@/providers/layout.provider";
|
||||||
|
import LayoutContentProvider from "@/providers/content.provider";
|
||||||
|
import DashCodeSidebar from "@/components/partials/sidebar";
|
||||||
|
import DashCodeFooter from "@/components/partials/footer";
|
||||||
|
import ThemeCustomize from "@/components/partials/customizer";
|
||||||
|
import DashCodeHeader from "@/components/partials/header";
|
||||||
|
|
||||||
|
import { redirect } from "@/components/navigation";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
|
||||||
|
const layout = async ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Navbar />
|
||||||
|
{children}
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default layout;
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Link, usePathname } from "@/i18n/routing";
|
||||||
|
import { getUserNotifications } from "@/service/landing/landing";
|
||||||
|
import { getTimestamp } from "@/utils/globals";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const InboxSection = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
const isUpdate = pathname.includes("update");
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const page: any = searchParams?.get("page");
|
||||||
|
|
||||||
|
const pages = page ? page - 1 : 0;
|
||||||
|
|
||||||
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
const [getTotalData, setGetTotalData] = useState();
|
||||||
|
const [, setGetTotalPage] = useState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getNotif() {
|
||||||
|
const response = await getUserNotifications(pages, 2);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotifUpdate() {
|
||||||
|
const response = await getUserNotifications(pages, 3);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdate) {
|
||||||
|
getNotifUpdate();
|
||||||
|
} else {
|
||||||
|
getNotif();
|
||||||
|
}
|
||||||
|
}, [pages]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl h-screen flex flex-col mx-auto p-4 lg:p-24 gap-5">
|
||||||
|
<div className="flex items-center justify-center mb-6">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="60px" height="60px" viewBox="0 0 24 24">
|
||||||
|
<g fill="#bb3523" fill-rule="evenodd" clip-rule="evenodd">
|
||||||
|
<path d="M16 9a4 4 0 1 1-8 0a4 4 0 0 1 8 0m-2 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
|
||||||
|
<path d="M12 1C5.925 1 1 5.925 1 12s4.925 11 11 11s11-4.925 11-11S18.075 1 12 1M3 12c0 2.09.713 4.014 1.908 5.542A8.99 8.99 0 0 1 12.065 14a8.98 8.98 0 0 1 7.092 3.458A9 9 0 1 0 3 12m9 9a8.96 8.96 0 0 1-5.672-2.012A6.99 6.99 0 0 1 12.065 16a6.99 6.99 0 0 1 5.689 2.92A8.96 8.96 0 0 1 12 21" />
|
||||||
|
</g>
|
||||||
|
</svg>{" "}
|
||||||
|
<h2 className="ml-4 text-[15px] lg:text-[32px] font-semibold text-gray-800">Pesan Masuk</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-center items-center gap-3">
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="flex flex-row gap-10 items-center justify-center">
|
||||||
|
<div>
|
||||||
|
<p className="bg-[#bb3523] py-1 px-3 rounded-full">Pesan Masuk</p>
|
||||||
|
</div>
|
||||||
|
<Link href={`/inbox/update`}>Update</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="py-10 px-8 w-[400px] mt-3 border border-black rounded-lg flex flex-col">
|
||||||
|
<h1 className="mb-3 text-lg font-semibold">List Notifikasi</h1>
|
||||||
|
<div className="hover:bg-slate-200 rounded-md">
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="flex flex-row items-center ml-1" href={"/" + list.redirectUrl}>
|
||||||
|
{(() => {
|
||||||
|
switch (Number(list.notificationTypeId)) {
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comment" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:upload" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="la:check-double" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comments" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:tasks" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:pencil-square" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:times-circle" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="text-[#bb3523]">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:info-circle" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block">{list.message}</span>
|
||||||
|
<span className="text-xs">{getTimestamp(new Date(list.createdAt))}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InboxSection;
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Link, usePathname } from "@/i18n/routing";
|
||||||
|
import { getUserNotifications } from "@/service/landing/landing";
|
||||||
|
import { getTimestamp } from "@/utils/globals";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const UpdateSection = () => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const isUpdate = pathname.includes("update");
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const page: any = searchParams?.get("page");
|
||||||
|
const pages = page ? page - 1 : 0;
|
||||||
|
|
||||||
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
const [getTotalData, setGetTotalData] = useState();
|
||||||
|
const [, setGetTotalPage] = useState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getNotif() {
|
||||||
|
const response = await getUserNotifications(pages, 2);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotifUpdate() {
|
||||||
|
const response = await getUserNotifications(pages, 3);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
setGetTotalData(response?.data?.data?.totalElements);
|
||||||
|
setGetTotalPage(response?.data?.data?.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdate) {
|
||||||
|
getNotifUpdate();
|
||||||
|
} else {
|
||||||
|
getNotif();
|
||||||
|
}
|
||||||
|
}, [pages]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl h-screen flex flex-col mx-auto p-4 lg:p-24 gap-5">
|
||||||
|
<div className="flex items-center justify-center mb-6">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="60px" height="60px" viewBox="0 0 24 24">
|
||||||
|
<g fill="#bb3523" fill-rule="evenodd" clip-rule="evenodd">
|
||||||
|
<path d="M16 9a4 4 0 1 1-8 0a4 4 0 0 1 8 0m-2 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
|
||||||
|
<path d="M12 1C5.925 1 1 5.925 1 12s4.925 11 11 11s11-4.925 11-11S18.075 1 12 1M3 12c0 2.09.713 4.014 1.908 5.542A8.99 8.99 0 0 1 12.065 14a8.98 8.98 0 0 1 7.092 3.458A9 9 0 1 0 3 12m9 9a8.96 8.96 0 0 1-5.672-2.012A6.99 6.99 0 0 1 12.065 16a6.99 6.99 0 0 1 5.689 2.92A8.96 8.96 0 0 1 12 21" />
|
||||||
|
</g>
|
||||||
|
</svg>{" "}
|
||||||
|
<h2 className="ml-4 text-[15px] lg:text-[32px] font-semibold text-gray-800">Update</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-center gap-3">
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="flex flex-row gap-10 items-center justify-center">
|
||||||
|
<div>
|
||||||
|
<Link href={`/inbox`}>Pesan Masuk</Link>
|
||||||
|
</div>
|
||||||
|
<div className="bg-[#bb3523] py-1 px-3 rounded-full">Update</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="py-10 px-8 w-[400px] mt-3 border border-black rounded-lg flex flex-col">
|
||||||
|
<h1 className="mb-3 text-lg font-semibold">List Notifikasi</h1>
|
||||||
|
<div className="hover:bg-slate-200 rounded-md">
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="flex flex-row items-center ml-1" href={"/" + list.redirectUrl}>
|
||||||
|
{(() => {
|
||||||
|
switch (Number(list.notificationTypeId)) {
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comment" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:upload" fontSize={25} />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="la:check-double" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:comments" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return (
|
||||||
|
<div className="notif-icon notif-danger">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:tasks" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
return (
|
||||||
|
<div className="notif-icon notif-primary">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:pencil-square" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:times-circle" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{" "}
|
||||||
|
<Icon icon="fa:info-circle" />{" "}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block">{list.message}</span>
|
||||||
|
<span className="text-xs">{getTimestamp(new Date(list.createdAt))}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpdateSection;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||||
|
|
@ -9,8 +9,13 @@ import NewContent from "@/components/landing-page/new-content";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading, successCallback } from "@/config/swal";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
|
||||||
const DetailVideo = () => {
|
const DetailVideo = () => {
|
||||||
const [selectedSize, setSelectedSize] = useState<string>("L");
|
const [selectedSize, setSelectedSize] = useState<string>("L");
|
||||||
|
|
@ -28,8 +33,17 @@ const DetailVideo = () => {
|
||||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||||
const [main, setMain] = useState<any>();
|
const [main, setMain] = useState<any>();
|
||||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||||
|
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const [emailShareList, setEmailShareList] = useState<any>();
|
||||||
|
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||||
|
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams?.get("id");
|
||||||
|
const [width, setWidth] = useState<any>();
|
||||||
|
const [content, setContent] = useState<any>([]);
|
||||||
|
const userRoleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
let typeString = "video";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
|
@ -40,6 +54,8 @@ const DetailVideo = () => {
|
||||||
const response = await getDetail(String(slug));
|
const response = await getDetail(String(slug));
|
||||||
console.log("detailVideo", response);
|
console.log("detailVideo", response);
|
||||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||||
|
setWidth(window.innerWidth);
|
||||||
|
setContent(response?.data.data);
|
||||||
setMain({
|
setMain({
|
||||||
id: response?.data?.data?.files[0]?.id,
|
id: response?.data?.data?.files[0]?.id,
|
||||||
type: response?.data?.data?.fileType.name,
|
type: response?.data?.data?.fileType.name,
|
||||||
|
|
@ -216,6 +232,59 @@ const DetailVideo = () => {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShare = (type: any, url: any) => {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
sendActivityLog(2);
|
||||||
|
sendActivityLog(4);
|
||||||
|
if (type == "wa" && width <= 768) {
|
||||||
|
window.open(`whatsapp://send?${url}`, "_blank");
|
||||||
|
} else if (type == "wa" && width > 768) {
|
||||||
|
window.open(`https://web.whatsapp.com/send?${url}`, "_blank", "noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function shareToEmail() {
|
||||||
|
if (Number(userRoleId) < 1 || userRoleId == undefined) {
|
||||||
|
router.push("/auth/login");
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
mediaUploadId: id?.split("-")?.[0],
|
||||||
|
email: emailShareList || [emailShareInput],
|
||||||
|
message: emailMessageInput,
|
||||||
|
url: window.location.href,
|
||||||
|
};
|
||||||
|
loading();
|
||||||
|
const res = await sendMediaUploadToEmail(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
successCallback("Konten Telah Dikirim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEmailList = (e: any) => {
|
||||||
|
const arrayEmail: any = [];
|
||||||
|
for (let i = 0; i < emailShareList?.length; i += 1) {
|
||||||
|
arrayEmail.push(emailShareList[i]);
|
||||||
|
}
|
||||||
|
if (e.which == 13) {
|
||||||
|
if (e.target.value) {
|
||||||
|
arrayEmail.push(e.target.value);
|
||||||
|
setEmailShareList(arrayEmail);
|
||||||
|
setEmailShareInput("");
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="px-4 md:px-24 py-4">
|
<div className="px-4 md:px-24 py-4">
|
||||||
|
|
@ -318,6 +387,39 @@ const DetailVideo = () => {
|
||||||
</svg>
|
</svg>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* Tombol Bagikan */}
|
||||||
|
<div className="flex flex-row py-3">
|
||||||
|
<p className="text-base font-semibold">Bagikan</p>
|
||||||
|
<a className="ml-8 cursor-pointer" onClick={() => handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}>
|
||||||
|
<Icon icon="brandico:facebook" height="20" className="px-auto text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("tw", `https://twitter.com/share?url=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}&text=${content?.title}`)}>
|
||||||
|
<Icon icon="mdi:twitter" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<a className="ml-5 cursor-pointer" onClick={() => handleShare("wa", `text=${content?.title}%0D%0A%0D%0Ahttps%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}`)}>
|
||||||
|
<Icon icon="ri:whatsapp-fill" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger className="flex justify-end gap-1 cursor-pointer" asChild>
|
||||||
|
<a className="ml-5 cursor-pointer" data-toggle="dropdown" href="#" aria-expanded="false">
|
||||||
|
<Icon icon="material-symbols-light:mail" width="23" className="text-red-600 text-center" />
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<h1 className="mb-2">Share Ke Email</h1>
|
||||||
|
<div className="flex flex-col mb-2">
|
||||||
|
<p className="text-base font-semibold mb-1">Email Tujuan :</p>
|
||||||
|
<Input value={emailShareInput} onChange={(event) => setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" />
|
||||||
|
</div>
|
||||||
|
<Button className="bg-blue-500 text-white p-2 w-fit rounded-lg" onClick={() => shareToEmail()}>
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
||||||
<div className="flex w-full items-center overflow-hidden min-h-dvh h-dvh basis-full">
|
<div className="flex w-full items-center overflow-hidden min-h-dvh h-dvh basis-full">
|
||||||
<div className="overflow-y-auto flex flex-wrap w-full h-dvh">
|
<div className="overflow-y-auto flex flex-wrap w-full h-dvh">
|
||||||
<div
|
<div
|
||||||
className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600
|
className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600 relative z-[1] bg-default-50"
|
||||||
relative z-[1] bg-default-50"
|
|
||||||
>
|
>
|
||||||
<div className="max-w-[520px] pt-16 ps-20 ">
|
<div className="max-w-[520px] pt-16 ps-20 ">
|
||||||
<Link href="/" className="mb-6 inline-block">
|
<Link href="/" className="mb-6 inline-block">
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -578,3 +578,8 @@ html[dir="rtl"] .react-select .select__loading-indicator {
|
||||||
.ck-editor__editable_inline {
|
.ck-editor__editable_inline {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide FullCalendar grid elements */
|
||||||
|
.fc-view-harness:has(.hide-calendar-grid) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import {
|
||||||
saveTicketInternalReply,
|
saveTicketInternalReply,
|
||||||
} from "@/service/communication/communication";
|
} from "@/service/communication/communication";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
import { Link } from "@/i18n/routing";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -34,33 +36,6 @@ const taskSchema = z.object({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type escalationDetail = {
|
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
createdAt: string;
|
|
||||||
commentFromUserName: string;
|
|
||||||
message: string;
|
|
||||||
createdBy: {
|
|
||||||
id: number;
|
|
||||||
fullname: string;
|
|
||||||
};
|
|
||||||
sendTo: {
|
|
||||||
id: number;
|
|
||||||
fullname: string;
|
|
||||||
};
|
|
||||||
status: {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
priority: {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
description: string;
|
|
||||||
narration: string;
|
|
||||||
is_active: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type replyDetail = {
|
export type replyDetail = {
|
||||||
id: number;
|
id: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
|
@ -79,7 +54,7 @@ export default function FormDetailEscalation() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const { id } = useParams() as { id: string };
|
const { id } = useParams() as { id: string };
|
||||||
|
|
||||||
const [detail, setDetail] = useState<escalationDetail>();
|
const [detail, setDetail] = useState<any>();
|
||||||
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
||||||
const [replyVisible, setReplyVisible] = useState(false);
|
const [replyVisible, setReplyVisible] = useState(false);
|
||||||
const [replyMessage, setReplyMessage] = useState("");
|
const [replyMessage, setReplyMessage] = useState("");
|
||||||
|
|
@ -165,40 +140,54 @@ export default function FormDetailEscalation() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<div>
|
||||||
<div className="px-6 py-6">
|
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex flex-col mt-6 w-full mb-3">
|
<div className="flex flex-col mt-6 w-full mb-3">
|
||||||
{detail !== undefined && (
|
{detail !== undefined && (
|
||||||
<div key={detail?.id} className="bg-slate-300 rounded-md">
|
<div key={detail?.id} className="bg-slate-300 rounded-md">
|
||||||
<p className="px-3 py-3 bg-slate-300 rounded-md text-lg font-semibold">
|
<p className="p-5 bg-slate-300 rounded-md text-lg font-semibold">
|
||||||
Ticket #{detail.id}
|
Ticket #{detail.id}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-row gap-3 mt-3 bg-blue-300 py-2 px-2">
|
<div className="flex flex-row gap-3 bg-sky-100 p-5 items-center">
|
||||||
<Avatar>
|
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||||
<AvatarImage
|
|
||||||
src={"/images/avatar/avatar-3.png"}
|
|
||||||
alt={`mabes`}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<span className="font-bold">
|
<span className="font-bold">
|
||||||
{detail?.commentFromUserName}
|
{detail?.commentFromUserName}
|
||||||
</span>{" "}
|
</span>
|
||||||
mengirimkan pesan untuk{" "}
|
{` `}
|
||||||
<span className="font-bold">{detail?.message}</span>
|
mengirimkan pesan untuk{` `}
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
detail?.feed
|
||||||
|
? detail?.feed?.permalink_url == undefined
|
||||||
|
? detail?.feedUrl
|
||||||
|
: detail?.feed?.permalink_url
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
target="_blank"
|
||||||
|
className="font-bold"
|
||||||
|
>
|
||||||
|
{detail?.message}
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs">
|
||||||
|
{`${new Date(detail?.createdAt).getDate()}-${
|
||||||
|
new Date(detail?.createdAt).getMonth() + 1
|
||||||
|
}-${new Date(detail?.createdAt).getFullYear()} ${new Date(
|
||||||
|
detail?.createdAt
|
||||||
|
).getHours()}:${new Date(detail?.createdAt).getMinutes()}`}
|
||||||
</p>
|
</p>
|
||||||
<p>{detail?.createdAt}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="pl-3 bg-white">{detail.message}</p>
|
<p className="p-5 bg-white">{detail.message}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{detail !== undefined && (
|
{detail !== undefined && (
|
||||||
<div className="gap-5 mb-5 w-full border mt-3 rounded-md">
|
<div className="gap-5 mb-5 w-full border mt-3 rounded-md bg-white">
|
||||||
<div className="space-y-2 px-3 mt-3">
|
<div className="space-y-2 px-3 mt-3">
|
||||||
<Label>Judul</Label>
|
<Label>Judul</Label>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -280,6 +269,5 @@ export default function FormDetailEscalation() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import {
|
||||||
getTicketingInternalDiscussion,
|
getTicketingInternalDiscussion,
|
||||||
saveTicketInternalReply,
|
saveTicketInternalReply,
|
||||||
} from "@/service/communication/communication";
|
} from "@/service/communication/communication";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -36,6 +37,7 @@ export type taskDetail = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
referenceNumber: string | number;
|
||||||
createdBy: {
|
createdBy: {
|
||||||
id: number;
|
id: number;
|
||||||
fullname: string;
|
fullname: string;
|
||||||
|
|
@ -161,8 +163,7 @@ export default function FormDetailInternal() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<div className="py-5">
|
||||||
<div className="px-6 py-6">
|
|
||||||
<div className="mt-4 flex flex-row items-center gap-3">
|
<div className="mt-4 flex flex-row items-center gap-3">
|
||||||
<Button onClick={handleReply} color="default" variant={"outline"}>
|
<Button onClick={handleReply} color="default" variant={"outline"}>
|
||||||
Balas
|
Balas
|
||||||
|
|
@ -173,9 +174,10 @@ export default function FormDetailInternal() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row gap-5 mt-5">
|
||||||
|
<div className="flex flex-col w-[70%]">
|
||||||
{replyVisible && (
|
{replyVisible && (
|
||||||
<div className="mt-4">
|
<div className="">
|
||||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
|
||||||
<textarea
|
<textarea
|
||||||
id="replyMessage"
|
id="replyMessage"
|
||||||
className="w-full h-24 border rounded-md p-2"
|
className="w-full h-24 border rounded-md p-2"
|
||||||
|
|
@ -183,7 +185,7 @@ export default function FormDetailInternal() {
|
||||||
onChange={(e) => setReplyMessage(e.target.value)}
|
onChange={(e) => setReplyMessage(e.target.value)}
|
||||||
placeholder="Tulis pesan di sini..."
|
placeholder="Tulis pesan di sini..."
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end gap-3 mt-2">
|
<div className="flex justify-end gap-3 my-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setReplyVisible(false)}
|
onClick={() => setReplyVisible(false)}
|
||||||
color="default"
|
color="default"
|
||||||
|
|
@ -197,40 +199,41 @@ export default function FormDetailInternal() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-row justify-between ">
|
<div className="border rounded-t-xl">
|
||||||
<div className="flex flex-col mt-6 w-7/12">
|
<p className="p-4 bg-slate-300 rounded-t-xl text-lg font-semibold">
|
||||||
{ticketReply?.map((list) => (
|
Ticket #{detail?.referenceNumber}
|
||||||
<div key={list.id} className="bg-slate-300 rounded-md border">
|
|
||||||
<p className="px-3 pt-3 bg-slate-300 rounded-md text-lg font-semibold">
|
|
||||||
Ticket #{list.id}
|
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-row gap-3 mt-3 bg-blue-300 py-2 px-2">
|
{ticketReply?.map((list) => (
|
||||||
<Avatar>
|
<div key={list.id} className="flex flex-col">
|
||||||
<AvatarImage
|
<div className="flex flex-row gap-3 bg-sky-100 p-4 items-center">
|
||||||
src={"/images/avatar/avatar-3.png"}
|
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||||
alt={`mabes`}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<span className="font-bold">
|
<span className="font-bold text-sm">
|
||||||
{list?.messageFrom?.fullname}
|
{list?.messageFrom?.fullname}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
mengirimkan pesan untuk{" "}
|
mengirimkan pesan untuk{" "}
|
||||||
<span className="font-bold">
|
<span className="font-bold text-sm">
|
||||||
{list?.messageTo?.fullname}
|
{list?.messageTo?.fullname}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>{list?.createdAt}</p>
|
<p className="text-xs">
|
||||||
|
{`${new Date(list?.createdAt).getDate()}-${
|
||||||
|
new Date(list?.createdAt).getMonth() + 1
|
||||||
|
}-${new Date(list?.createdAt).getFullYear()} ${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getHours()}:${new Date(list?.createdAt).getMinutes()}`}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="pl-3 bg-white py-2">{list.message}</p>
|
<p className="p-4 bg-white text-sm">{list.message}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{detail !== undefined && (
|
{detail !== undefined && (
|
||||||
<div className="gap-5 mb-5 w-3/12 border mt-3 rounded-md">
|
<div className="gap-5 mb-5 w-[30%] border bg-white rounded-md">
|
||||||
<Label className="ml-3 mt-3">Properties</Label>
|
<p className="mx-3 mt-3">Properties</p>
|
||||||
<div className="space-y-2 px-3">
|
<div className="space-y-2 px-3">
|
||||||
<Label>Judul</Label>
|
<Label>Judul</Label>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -287,6 +290,5 @@ export default function FormDetailInternal() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import {
|
||||||
saveTicketInternalReply,
|
saveTicketInternalReply,
|
||||||
} from "@/service/communication/communication";
|
} from "@/service/communication/communication";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { htmlToString } from "@/utils/globals";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
// description: z.string().min(2, {
|
// description: z.string().min(2, {
|
||||||
|
|
@ -33,30 +35,6 @@ const taskSchema = z.object({
|
||||||
// }),
|
// }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type taskDetail = {
|
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
createdAt: string;
|
|
||||||
createdBy: {
|
|
||||||
id: number;
|
|
||||||
fullname: string;
|
|
||||||
};
|
|
||||||
sendTo: {
|
|
||||||
id: number;
|
|
||||||
fullname: string;
|
|
||||||
};
|
|
||||||
status: {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
priority: {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
description: string;
|
|
||||||
is_active: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type replyDetail = {
|
export type replyDetail = {
|
||||||
id: number;
|
id: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
|
@ -76,13 +54,14 @@ export default function FormEditInternal() {
|
||||||
const { id } = useParams() as { id: string };
|
const { id } = useParams() as { id: string };
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [detail, setDetail] = useState<taskDetail>();
|
const [detail, setDetail] = useState<any>();
|
||||||
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
||||||
const [replyVisible, setReplyVisible] = useState(false);
|
const [replyVisible, setReplyVisible] = useState(false);
|
||||||
const [replyMessage, setReplyMessage] = useState("");
|
const [replyMessage, setReplyMessage] = useState("");
|
||||||
const [selectedPriority, setSelectedPriority] = useState("");
|
const [selectedPriority, setSelectedPriority] = useState("");
|
||||||
const [selectedStatus, setSelectedStatus] = useState("");
|
const [selectedStatus, setSelectedStatus] = useState("");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
type TaskSchema = z.infer<typeof taskSchema>;
|
type TaskSchema = z.infer<typeof taskSchema>;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -98,6 +77,10 @@ export default function FormEditInternal() {
|
||||||
if (id) {
|
if (id) {
|
||||||
const response = await getTicketingInternalDetail(id);
|
const response = await getTicketingInternalDetail(id);
|
||||||
setDetail(response?.data?.data);
|
setDetail(response?.data?.data);
|
||||||
|
setSelectedPriority(response?.data?.data?.priority?.name);
|
||||||
|
console.log("sadad", response?.data?.data);
|
||||||
|
setSelectedStatus(response?.data?.data?.status?.name);
|
||||||
|
setDescription(htmlToString(response?.data?.data?.message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initState();
|
initState();
|
||||||
|
|
@ -206,8 +189,7 @@ export default function FormEditInternal() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<div className="py-5">
|
||||||
<div className="px-6 py-6">
|
|
||||||
<div className="mt-4 flex flex-row items-center gap-3">
|
<div className="mt-4 flex flex-row items-center gap-3">
|
||||||
<Button onClick={handleReply} color="default" variant={"outline"}>
|
<Button onClick={handleReply} color="default" variant={"outline"}>
|
||||||
Balas
|
Balas
|
||||||
|
|
@ -218,9 +200,10 @@ export default function FormEditInternal() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row gap-5 mt-5">
|
||||||
|
<div className="flex flex-col w-[70%]">
|
||||||
{replyVisible && (
|
{replyVisible && (
|
||||||
<div className="mt-4">
|
<div>
|
||||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
|
||||||
<textarea
|
<textarea
|
||||||
id="replyMessage"
|
id="replyMessage"
|
||||||
className="w-full h-24 border rounded-md p-2"
|
className="w-full h-24 border rounded-md p-2"
|
||||||
|
|
@ -242,20 +225,15 @@ export default function FormEditInternal() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-row justify-between ">
|
<div className="border rounded-t-xl">
|
||||||
<div className="flex flex-col mt-6 w-7/12">
|
<p className="p-4 bg-slate-300 rounded-t-xl text-lg font-semibold">
|
||||||
{ticketReply?.map((list) => (
|
Ticket #{detail?.referenceNumber}
|
||||||
<div key={list.id} className="bg-slate-300 rounded-md border">
|
|
||||||
<p className="px-3 pt-3 bg-slate-300 rounded-md text-lg font-semibold">
|
|
||||||
Ticket #{list.id}
|
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-row gap-3 mt-3 bg-blue-300 py-2 px-2">
|
{ticketReply?.map((list) => (
|
||||||
<Avatar>
|
<div key={list.id} className="flex flex-col">
|
||||||
<AvatarImage
|
<div className="flex flex-row gap-3 bg-sky-100 p-4 items-center">
|
||||||
src={"/images/avatar/avatar-3.png"}
|
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||||
alt={`mabes`}
|
|
||||||
/>
|
|
||||||
</Avatar>
|
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<span className="font-bold">
|
<span className="font-bold">
|
||||||
|
|
@ -266,17 +244,26 @@ export default function FormEditInternal() {
|
||||||
{list?.messageTo?.fullname}
|
{list?.messageTo?.fullname}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>{list?.createdAt}</p>
|
<p className="text-xs">{`${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getDate()}-${
|
||||||
|
new Date(list?.createdAt).getMonth() + 1
|
||||||
|
}-${new Date(list?.createdAt).getFullYear()} ${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="pl-3 bg-white py-2">{list.message}</p>
|
<p className="pl-3 bg-white py-2">{list.message}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{detail !== undefined && (
|
{detail !== undefined && (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)} className="w-[30%]">
|
||||||
<div className="gap-5 mb-5 w-full border mt-3 rounded-md">
|
<div className="gap-5 mb-5 border bg-white rounded-xl">
|
||||||
<Label className="ml-3 mt-3">Properties</Label>
|
<p className="mx-3 mt-3">Properties</p>
|
||||||
<div className="space-y-2 px-3">
|
<div className="space-y-2 px-3">
|
||||||
<Label>Judul</Label>
|
<Label>Judul</Label>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -302,7 +289,7 @@ export default function FormEditInternal() {
|
||||||
<Label>Prioritas</Label>
|
<Label>Prioritas</Label>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={setSelectedPriority}
|
onValueChange={setSelectedPriority}
|
||||||
value={detail?.priority?.name}
|
value={selectedPriority}
|
||||||
>
|
>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
|
|
@ -318,35 +305,25 @@ export default function FormEditInternal() {
|
||||||
<Label>Status</Label>
|
<Label>Status</Label>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={setSelectedStatus}
|
onValueChange={setSelectedStatus}
|
||||||
value={detail?.status?.name}
|
value={selectedStatus}
|
||||||
>
|
>
|
||||||
<SelectTrigger size="md">
|
<SelectTrigger size="md">
|
||||||
<SelectValue placeholder="Pilih" />
|
<SelectValue placeholder="Pilih" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="1">Open</SelectItem>
|
<SelectItem value="Open">Open</SelectItem>
|
||||||
<SelectItem value="2">Close</SelectItem>
|
<SelectItem value="Close">Close</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 px-3">
|
<div className="space-y-2 px-3">
|
||||||
<Label>Description</Label>
|
<Label>Description</Label>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="description"
|
|
||||||
render={({ field }) => (
|
|
||||||
<Textarea
|
<Textarea
|
||||||
value={detail?.description}
|
value={description}
|
||||||
onChange={field.onChange}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
placeholder="Enter description"
|
placeholder="Enter description"
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{/* {errors.title?.message && (
|
|
||||||
<p className="text-red-400 text-sm">
|
|
||||||
{errors.title.message}
|
|
||||||
</p>
|
|
||||||
)} */}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end mt-3 mr-3">
|
<div className="flex justify-end mt-3 mr-3">
|
||||||
<Button type="submit" color="primary">
|
<Button type="submit" color="primary">
|
||||||
|
|
@ -358,6 +335,5 @@ export default function FormEditInternal() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import WavesurferPlayer from "@wavesurfer/react";
|
import WavesurferPlayer from "@wavesurfer/react";
|
||||||
|
import WaveSurfer from "wavesurfer.js";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -141,14 +142,21 @@ export default function FormAudioDetail() {
|
||||||
const [audioPlaying, setAudioPlaying] = useState<any>(null);
|
const [audioPlaying, setAudioPlaying] = useState<any>(null);
|
||||||
|
|
||||||
const waveSurferRef = useRef<any>(null);
|
const waveSurferRef = useRef<any>(null);
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
|
||||||
|
|
||||||
|
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
|
const onReady = (ws: any) => {
|
||||||
|
setWavesurfer(ws)
|
||||||
|
setIsPlaying(false);
|
||||||
|
}
|
||||||
|
|
||||||
const onPlayPause = () => {
|
const onPlayPause = () => {
|
||||||
if (waveSurferRef.current) {
|
wavesurfer && wavesurfer.playPause();
|
||||||
waveSurferRef.current.playPause();
|
|
||||||
setIsPlaying(!isPlaying);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let fileTypeId = "4";
|
let fileTypeId = "4";
|
||||||
|
|
||||||
|
|
@ -452,10 +460,12 @@ export default function FormAudioDetail() {
|
||||||
{detailThumb?.map((url: any, index: number) => (
|
{detailThumb?.map((url: any, index: number) => (
|
||||||
<div key={url.id}>
|
<div key={url.id}>
|
||||||
<WavesurferPlayer
|
<WavesurferPlayer
|
||||||
ref={waveSurferRef}
|
|
||||||
height={500}
|
height={500}
|
||||||
waveColor="red"
|
waveColor="red"
|
||||||
url={url}
|
url={url}
|
||||||
|
onReady={onReady}
|
||||||
|
onPlay={() => setIsPlaying(true)}
|
||||||
|
onPause={() => setIsPlaying(false)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,13 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { listRole } from "@/service/landing/landing";
|
import { getUserNotifications, listRole } from "@/service/landing/landing";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
|
||||||
type Detail = {
|
type Detail = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -59,6 +65,7 @@ type Detail = {
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const [inboxOpen, setInboxOpen] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const locale = params?.locale;
|
const locale = params?.locale;
|
||||||
|
|
@ -77,6 +84,10 @@ const Navbar = () => {
|
||||||
const [role, setRole] = useState<any>();
|
const [role, setRole] = useState<any>();
|
||||||
const [menuActive, setMenuActive] = useState<string>();
|
const [menuActive, setMenuActive] = useState<string>();
|
||||||
const [category, setCategory] = useState<any>();
|
const [category, setCategory] = useState<any>();
|
||||||
|
const [notifications, setNotifications] = useState([]);
|
||||||
|
const [isMessageActive, setIsMessageActive] = useState(true);
|
||||||
|
const [notificationsUpdate, setNotificationsUpdate] = useState([]);
|
||||||
|
const [selectedTab, setSelectedTab] = useState("image");
|
||||||
|
|
||||||
let prefixPath = poldaName
|
let prefixPath = poldaName
|
||||||
? `/polda/${poldaName}`
|
? `/polda/${poldaName}`
|
||||||
|
|
@ -104,12 +115,44 @@ const Navbar = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initState() {
|
||||||
|
setMenuActive(menu);
|
||||||
|
const res = await listRole();
|
||||||
|
setRole(res?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotif() {
|
||||||
|
if (roleId != undefined) {
|
||||||
|
const response = await getUserNotifications(0, 2);
|
||||||
|
setNotifications(response?.data?.data?.content);
|
||||||
|
console.log("respon:", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNotifUpdate() {
|
||||||
|
if (roleId != undefined) {
|
||||||
|
const response = await getUserNotifications(0, 3);
|
||||||
|
setNotificationsUpdate(response?.data?.data?.content);
|
||||||
|
console.log("Notiffff:", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
getNotif();
|
||||||
|
getNotifUpdate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const test = () => {
|
||||||
|
console.log(notifications);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
const response = await getInfoProfile();
|
const response = await getInfoProfile();
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
setInboxOpen(details);
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
console.log("data", details);
|
console.log("data", details);
|
||||||
}
|
}
|
||||||
|
|
@ -121,39 +164,6 @@ const Navbar = () => {
|
||||||
console.log("Satkkeeer : ", satkerName);
|
console.log("Satkkeeer : ", satkerName);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initState() {
|
|
||||||
setMenuActive(menu);
|
|
||||||
const res = await listRole();
|
|
||||||
setRole(res?.data?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// async function getNotif() {
|
|
||||||
// if (roleId != undefined) {
|
|
||||||
// const response = await getUserNotifications(0, 2);
|
|
||||||
// setNotifications(response?.data?.data?.content);
|
|
||||||
// console.log("respon:", response);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async function getNotifUpdate() {
|
|
||||||
// if (roleId != undefined) {
|
|
||||||
// const response = await getUserNotifications(0, 3);
|
|
||||||
// setNotificationsUpdate(response?.data?.data?.content); // console.log("respon:", response);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async function getPolritvStatus() {
|
|
||||||
// const response = await getYtPolritvStatus();
|
|
||||||
// console.log("Polritv status :", response?.data?.message);
|
|
||||||
// setIsPolriTvOnline(response.data?.message == "Online");
|
|
||||||
// }
|
|
||||||
|
|
||||||
initState();
|
|
||||||
// getNotif();
|
|
||||||
// getNotifUpdate();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChange = (e: any) => {
|
||||||
setSearch(e.target.value);
|
setSearch(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
@ -214,7 +224,7 @@ const Navbar = () => {
|
||||||
<NavigationMenu>
|
<NavigationMenu>
|
||||||
<NavigationMenuList>
|
<NavigationMenuList>
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<NavigationMenuTrigger className="">
|
<NavigationMenuTrigger>
|
||||||
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer">
|
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer">
|
||||||
<svg
|
<svg
|
||||||
className="mx-2 dark:"
|
className="mx-2 dark:"
|
||||||
|
|
@ -232,12 +242,12 @@ const Navbar = () => {
|
||||||
{t("content")}
|
{t("content")}
|
||||||
</a>
|
</a>
|
||||||
</NavigationMenuTrigger>
|
</NavigationMenuTrigger>
|
||||||
<NavigationMenuContent className=" rounded-md overflow-hidden w-">
|
<NavigationMenuContent className="flex flex-col place-content-start rounded-md overflow-hidden ">
|
||||||
<NavigationMenuLink
|
<NavigationMenuLink
|
||||||
onClick={() => router.push(prefixPath + "/image/filter")}
|
onClick={() => router.push(prefixPath + "/image/filter")}
|
||||||
className="flex place-items-start gap-1.5 p-2"
|
className="flex place-items-start gap-1.5 p-2 w-36"
|
||||||
>
|
>
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row items-center py-2 cursor-pointer">
|
||||||
<FiImage className="mr-2" />
|
<FiImage className="mr-2" />
|
||||||
{t("image")}
|
{t("image")}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -248,29 +258,25 @@ const Navbar = () => {
|
||||||
>
|
>
|
||||||
{pathname?.split("/")[1] == "in" ? (
|
{pathname?.split("/")[1] == "in" ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-slate-600 text-sm dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-0 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiYoutube className="mr-2" />
|
<FiYoutube className="mr-2" />
|
||||||
{t("video")}
|
{t("video")}
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiYoutube className="mr-2" />
|
<FiYoutube className="mr-2" />
|
||||||
{t("video")}
|
{t("video")}
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* <p className="text-slate-600 text-sm dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-0 py-2 cursor-pointer">
|
|
||||||
<FiYoutube className="mr-2" />
|
|
||||||
{t("video")}
|
|
||||||
</p> */}
|
|
||||||
</NavigationMenuLink>
|
</NavigationMenuLink>
|
||||||
<NavigationMenuLink
|
<NavigationMenuLink
|
||||||
onClick={() => router.push(prefixPath + "/document/filter")}
|
onClick={() => router.push(prefixPath + "/document/filter")}
|
||||||
className="flex place-items-start gap-1.5 p-2"
|
className="flex place-items-start gap-1.5 p-2"
|
||||||
>
|
>
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiFile className="mr-2" />
|
<FiFile className="mr-2" />
|
||||||
{t("text")}
|
{t("text")}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -279,7 +285,7 @@ const Navbar = () => {
|
||||||
onClick={() => router.push(prefixPath + "/audio/filter")}
|
onClick={() => router.push(prefixPath + "/audio/filter")}
|
||||||
className="flex place-items-start gap-1.5 p-2 "
|
className="flex place-items-start gap-1.5 p-2 "
|
||||||
>
|
>
|
||||||
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
|
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-2 cursor-pointer">
|
||||||
<FiMusic className="mr-2" />
|
<FiMusic className="mr-2" />
|
||||||
{t("audio")}{" "}
|
{t("audio")}{" "}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -335,10 +341,10 @@ const Navbar = () => {
|
||||||
</NavigationMenuList>
|
</NavigationMenuList>
|
||||||
</NavigationMenu>
|
</NavigationMenu>
|
||||||
|
|
||||||
<Link href="#" className="flex items-center space-x-1 text-red-600">
|
{/* <Link href="#" className="flex items-center space-x-1 text-red-600">
|
||||||
<span className="w-2 h-2 bg-red-500 rounded-full"></span>
|
<span className="w-2 h-2 bg-red-500 rounded-full"></span>
|
||||||
<span className="font-medium">{t("live")}</span>
|
<span className="font-medium">{t("live")}</span>
|
||||||
</Link>
|
</Link> */}
|
||||||
<div className="flex items-center space-x-1 ">
|
<div className="flex items-center space-x-1 ">
|
||||||
<a href="https://tvradio.polri.go.id/">
|
<a href="https://tvradio.polri.go.id/">
|
||||||
<img
|
<img
|
||||||
|
|
@ -431,6 +437,155 @@ const Navbar = () => {
|
||||||
roleId === "6" ||
|
roleId === "6" ||
|
||||||
roleId === "7" ||
|
roleId === "7" ||
|
||||||
roleId === "8" ? (
|
roleId === "8" ? (
|
||||||
|
<>
|
||||||
|
{/* Inbox */}
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<a className="cursor-pointer" onClick={() => test()}>
|
||||||
|
{" "}
|
||||||
|
<Icon
|
||||||
|
icon="basil:envelope-outline"
|
||||||
|
color="black"
|
||||||
|
width="30"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className=" p-0 flex flex-col mt-2" align="end">
|
||||||
|
<Tabs
|
||||||
|
value={selectedTab}
|
||||||
|
onValueChange={setSelectedTab}
|
||||||
|
className="flex flex-col"
|
||||||
|
>
|
||||||
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row">
|
||||||
|
<TabsTrigger value="notif-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center justify-center cursor-pointer gap-4 rounded-lg p-3 text-sm mr-4 ${
|
||||||
|
selectedTab === "notif-tab"
|
||||||
|
? "bg-[#bb3523] text-white"
|
||||||
|
: "bg-gray-200 text-black"
|
||||||
|
}`}
|
||||||
|
id="notif-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notif"
|
||||||
|
aria-selected="true"
|
||||||
|
onClick={() => setIsMessageActive(true)}
|
||||||
|
>
|
||||||
|
Pesan Masuk
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="notifupdate-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center cursor-pointer gap-4 rounded-lg p-3 text-sm mr-4 ${
|
||||||
|
selectedTab === "notifupdate-tab"
|
||||||
|
? "bg-[#bb3523] text-white"
|
||||||
|
: "bg-gray-200 text-black"
|
||||||
|
}`}
|
||||||
|
id="notifupdate-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notifupdate"
|
||||||
|
aria-selected="false"
|
||||||
|
onClick={() => setIsMessageActive(false)}
|
||||||
|
>
|
||||||
|
Update(
|
||||||
|
{notificationsUpdate?.length})
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="notif-tab">
|
||||||
|
<div className="flex flex-col justify-center my-3">
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a
|
||||||
|
className="flex flex-row"
|
||||||
|
href={list.redirectUrl}
|
||||||
|
key={list.id}
|
||||||
|
>
|
||||||
|
<div className="ml-4">
|
||||||
|
<img
|
||||||
|
src="/assets/avatar-profile.png"
|
||||||
|
alt="..."
|
||||||
|
className="w-8 items-center mr-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">
|
||||||
|
{list?.message}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${
|
||||||
|
new Date(list?.createdAt).getMonth() + 1
|
||||||
|
}/${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox" legacyBehavior>
|
||||||
|
<p
|
||||||
|
className="text-[15px] text-center py-2"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="notifupdate-tab">
|
||||||
|
<div className="">
|
||||||
|
{notificationsUpdate?.map((list: any) => (
|
||||||
|
<a
|
||||||
|
className="flex flex-row"
|
||||||
|
href={list.redirectUrl}
|
||||||
|
key={list.id}
|
||||||
|
>
|
||||||
|
<div className="ml-4">
|
||||||
|
<img
|
||||||
|
src="/assets/avatar-profile.png"
|
||||||
|
alt="..."
|
||||||
|
className="w-8 items-center mr-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">
|
||||||
|
{list?.message}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${
|
||||||
|
new Date(list?.createdAt).getMonth() + 1
|
||||||
|
}/${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox/update" legacyBehavior>
|
||||||
|
<p
|
||||||
|
className="text-[15px] text-center py-2"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -501,6 +656,7 @@ const Navbar = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
</>
|
||||||
) : roleId === "2" ||
|
) : roleId === "2" ||
|
||||||
roleId === "3" ||
|
roleId === "3" ||
|
||||||
roleId === "4" ||
|
roleId === "4" ||
|
||||||
|
|
@ -509,7 +665,150 @@ const Navbar = () => {
|
||||||
roleId === "11" ||
|
roleId === "11" ||
|
||||||
roleId === "12" ||
|
roleId === "12" ||
|
||||||
roleId === "13" ? (
|
roleId === "13" ? (
|
||||||
// Dropdown menu for roleId === 3
|
<>
|
||||||
|
{/* Inbox */}
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<a className="cursor-pointer" onClick={() => test()}>
|
||||||
|
{" "}
|
||||||
|
<Icon
|
||||||
|
icon="basil:envelope-outline"
|
||||||
|
color="black"
|
||||||
|
width="30"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
className=" p-0 h-32 flex flex-col mt-2"
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
value={selectedTab}
|
||||||
|
onValueChange={setSelectedTab}
|
||||||
|
className="flex flex-row"
|
||||||
|
>
|
||||||
|
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||||
|
<TabsTrigger value="notif-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center justify-center cursor-pointer bg-[#bb3523] text-white gap-4 rounded-lg p-3 text-sm mr-4 ${
|
||||||
|
isMessageActive ? "active" : ""
|
||||||
|
}`}
|
||||||
|
id="notif-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notif"
|
||||||
|
aria-selected="true"
|
||||||
|
onClick={() => setIsMessageActive(true)}
|
||||||
|
>
|
||||||
|
Pesan Masuk
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="notifupdate-tab">
|
||||||
|
<a
|
||||||
|
className={`flex items-center cursor-pointer bg-[#bb3523] text-white text-sm gap-4 rounded-lg p-3 mr-4 ${
|
||||||
|
isMessageActive ? "" : "active"
|
||||||
|
}`}
|
||||||
|
id="notifupdate-tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
role="tab"
|
||||||
|
aria-controls="notifupdate"
|
||||||
|
aria-selected="false"
|
||||||
|
onClick={() => setIsMessageActive(false)}
|
||||||
|
>
|
||||||
|
Update(
|
||||||
|
{notificationsUpdate?.length})
|
||||||
|
</a>
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
<div
|
||||||
|
className={`flex justify-center my-3 ${
|
||||||
|
isMessageActive ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{notifications?.map((list: any) => (
|
||||||
|
<a className="" href={list.redirectUrl} key={list.id}>
|
||||||
|
<div className="">
|
||||||
|
<img
|
||||||
|
src="/assets/avatar-profile.png"
|
||||||
|
alt="..."
|
||||||
|
className=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">
|
||||||
|
{list?.message}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${
|
||||||
|
new Date(list?.createdAt).getMonth() + 1
|
||||||
|
}/${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox" legacyBehavior>
|
||||||
|
<p className="text-[15px] py-2" role="button">
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col justify-center my-3 ${
|
||||||
|
isMessageActive ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{notificationsUpdate?.map((list: any) => (
|
||||||
|
<a
|
||||||
|
className="flex flex-row"
|
||||||
|
href={list.redirectUrl}
|
||||||
|
key={list.id}
|
||||||
|
>
|
||||||
|
<div className="ml-4">
|
||||||
|
<img
|
||||||
|
src="/assets/avatar-profile.png"
|
||||||
|
alt="..."
|
||||||
|
className="w-8 items-center mr-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<div className="text-wrap text-left">
|
||||||
|
{list?.message}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
{`${new Date(list?.createdAt).getDate()}/${
|
||||||
|
new Date(list?.createdAt).getMonth() + 1
|
||||||
|
}/${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getFullYear()} ${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getHours()}:${new Date(
|
||||||
|
list?.createdAt
|
||||||
|
).getMinutes()}`}{" "}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<Link href="/inbox" legacyBehavior>
|
||||||
|
<p className="text-[15px] text-center py-2" role="button">
|
||||||
|
Lihat semua
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
{/* // Dropdown menu for roleId === 3 */}
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||||
{detail !== undefined ? (
|
{detail !== undefined ? (
|
||||||
|
|
@ -580,6 +879,7 @@ const Navbar = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
// Masuk and Daftar buttons for roleId === null
|
// Masuk and Daftar buttons for roleId === null
|
||||||
<div className="flex justify-center items-center mx-3 gap-5">
|
<div className="flex justify-center items-center mx-3 gap-5">
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ const SidebarManagement = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-4">
|
<div className="p-4 gap-1">
|
||||||
<Link href="/content-management/galery" className="mb-3">
|
<Link href="/content-management/galery" className="mb-3">
|
||||||
<div className={`${pathname?.includes("/content-management/galery") ? "bg-slate-500 text-white" : ""} hover:bg-slate-500 hover:text-white cursor-pointer p-4 rounded-lg flex justify-between`}>
|
<div className={`${pathname?.includes("/content-management/galery") ? "bg-slate-500 text-white" : ""} hover:bg-slate-500 hover:text-white cursor-pointer p-4 rounded-lg flex justify-between`}>
|
||||||
<div className="flex items-center gap-2 text-lg">
|
<div className="flex items-center gap-2 text-lg">
|
||||||
|
|
|
||||||
|
|
@ -2176,7 +2176,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
||||||
icon: "icon-park-outline:communication",
|
icon: "icon-park-outline:communication",
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
href: "/supervisor/communications/questions",
|
href: "/supervisor/communications/questions/all",
|
||||||
label: t("questions"),
|
label: t("questions"),
|
||||||
active: pathname.includes("/communications/questions"),
|
active: pathname.includes("/communications/questions"),
|
||||||
icon: "solar:inbox-line-outline",
|
icon: "solar:inbox-line-outline",
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,11 @@
|
||||||
"ticketing": "Ticketing",
|
"ticketing": "Ticketing",
|
||||||
"knowledge-base": "Knowledge Base",
|
"knowledge-base": "Knowledge Base",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
"questions": "Questions",
|
"questions": "Pertanyaan Masuk",
|
||||||
"internal": "Internal Questions",
|
"internal": "Pertanyaan Internal",
|
||||||
"forward": "Forward",
|
"forward": "Eskalasi",
|
||||||
"collaboration": "Collaboration",
|
"collaboration": "Kolaborasi",
|
||||||
"account-report": "Account Report",
|
"account-report": "Pelaporan Akun",
|
||||||
"settings": "Pengaturan",
|
"settings": "Pengaturan",
|
||||||
"feedback": "Feedback",
|
"feedback": "Feedback",
|
||||||
"content-production": "Produksi Konten",
|
"content-production": "Produksi Konten",
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@
|
||||||
"tus-js-client": "^4.2.3",
|
"tus-js-client": "^4.2.3",
|
||||||
"use-places-autocomplete": "^4.0.1",
|
"use-places-autocomplete": "^4.0.1",
|
||||||
"vaul": "^0.9.1",
|
"vaul": "^0.9.1",
|
||||||
|
"wavesurfer.js": "^7.8.15",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
|
@ -158,7 +159,6 @@
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.2.3",
|
"eslint-config-next": "14.2.3",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"react-wavesurfer.js": "0.0.5",
|
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
|
|
@ -844,6 +844,11 @@
|
||||||
"node": ">=4.2.0"
|
"node": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@dschoon/react-waves/node_modules/wavesurfer.js": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-+nn6VD86pTtRu9leVNXoIGOCMJyaTNsKNy9v+SfUsYo+SxLCQvEzrZZ/eKMImqspsk+BX1V1xlY4FRkHswu3fA=="
|
||||||
|
},
|
||||||
"node_modules/@emoji-mart/data": {
|
"node_modules/@emoji-mart/data": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
||||||
|
|
@ -14271,17 +14276,6 @@
|
||||||
"react": "^16.0.0"
|
"react": "^16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-wavesurfer.js": {
|
|
||||||
"version": "0.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-wavesurfer.js/-/react-wavesurfer.js-0.0.5.tgz",
|
|
||||||
"integrity": "sha512-KLjNTKS25xfsTWrJd+knXF5wFH8CRu7repXSeDSjh4zhj27QbNL5jtcK8pNl82/kcoTeB0fZvtCcxPreEE8dQw==",
|
|
||||||
"dev": true,
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-dom": "^17.0.2",
|
|
||||||
"wavesurfer.js": "^5.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|
@ -17165,9 +17159,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wavesurfer.js": {
|
"node_modules/wavesurfer.js": {
|
||||||
"version": "4.6.0",
|
"version": "7.8.15",
|
||||||
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.8.15.tgz",
|
||||||
"integrity": "sha512-+nn6VD86pTtRu9leVNXoIGOCMJyaTNsKNy9v+SfUsYo+SxLCQvEzrZZ/eKMImqspsk+BX1V1xlY4FRkHswu3fA=="
|
"integrity": "sha512-fWNnQt5BEGzuoJ7HRxfvpT1rOEI1AmCGPZ/+7QDkDVN/m2vIBeLVQ+5vENRMz1YwvZ/u1No0UV492/o8G++KXQ=="
|
||||||
},
|
},
|
||||||
"node_modules/web-namespaces": {
|
"node_modules/web-namespaces": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@
|
||||||
"tus-js-client": "^4.2.3",
|
"tus-js-client": "^4.2.3",
|
||||||
"use-places-autocomplete": "^4.0.1",
|
"use-places-autocomplete": "^4.0.1",
|
||||||
"vaul": "^0.9.1",
|
"vaul": "^0.9.1",
|
||||||
|
"wavesurfer.js": "^7.8.15",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
|
|
@ -159,7 +160,6 @@
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.2.3",
|
"eslint-config-next": "14.2.3",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"react-wavesurfer.js": "0.0.5",
|
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
583
pnpm-lock.yaml
583
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -81,4 +81,51 @@ export async function saveUser(data: any) {
|
||||||
const url = "users/save";
|
const url = "users/save";
|
||||||
return httpPostInterceptor(url, data);
|
return httpPostInterceptor(url, data);
|
||||||
}
|
}
|
||||||
|
export async function saveInstitutes(data: any) {
|
||||||
|
const url = "public/users/save-institutes";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
export async function postRegistration(data: any) {
|
||||||
|
const url = "public/users/save";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataByNIK(reqid: any, nik: any) {
|
||||||
|
const url = `http://spitpolri.com/api/back_end/get_ktp?reqid=${reqid}&nik=${nik}`;
|
||||||
|
return getAPIDummy(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listInstitusi(roleId: any) {
|
||||||
|
const url = `public/users/institutes?categoryRoleId=${roleId}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listProvince() {
|
||||||
|
const url = "public/users/provinces";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listCity(id: any) {
|
||||||
|
const url = `public/users/cities?provId=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listDistricts(id: any) {
|
||||||
|
const url = `public/users/districts?cityId=${id}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataByNRP(reqid: any, nrp: any) {
|
||||||
|
const url = `http://spitpolri.com/api/back_end/get_nrp?reqid=${reqid}&nrp=${nrp}`;
|
||||||
|
return getAPIDummy(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataJournalist(cert: any) {
|
||||||
|
const url = `public/users/search-journalist?cert=${cert}`;
|
||||||
|
return httpGetInterceptor({ url });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDataPersonil(nrp: any) {
|
||||||
|
const url = `public/users/search-personil?nrp=${nrp}`;
|
||||||
|
return httpGetInterceptor({ url });
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,3 +100,18 @@ export async function deleteCollabDiscussion(id: string | number) {
|
||||||
const url = `ticketing/collaboration/discussion?id=${id}`;
|
const url = `ticketing/collaboration/discussion?id=${id}`;
|
||||||
return httpDeleteInterceptor(url);
|
return httpDeleteInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getQuestionPagination(
|
||||||
|
title = "",
|
||||||
|
page: number,
|
||||||
|
typeId: string,
|
||||||
|
size: string
|
||||||
|
) {
|
||||||
|
const url = `question/pagination?enablePage=1&size=${size}&title=${title}&page=${page}&typeId=${typeId}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteQuestion(id: string | number) {
|
||||||
|
const url = `/question?id=${id}`;
|
||||||
|
return httpDeleteInterceptor(url);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { httpPost } from "../http-config/http-base-service";
|
||||||
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function getHeroData() {
|
export async function getHeroData() {
|
||||||
|
|
@ -113,3 +114,17 @@ export async function getPublicSuggestionList(id: any) {
|
||||||
const url = `media/public/suggestion?mediaId=${id}`;
|
const url = `media/public/suggestion?mediaId=${id}`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
export async function verifyOTP(email: any, otp: any) {
|
||||||
|
const url = `public/users/verify-otp?email=${email}&otp=${otp}`;
|
||||||
|
return httpPostInterceptor(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function requestOTP(data: any) {
|
||||||
|
const url = "public/users/otp-request";
|
||||||
|
return httpPost(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserNotifications(page = 0, typeId: any) {
|
||||||
|
const url = `notification/public/list?enablePage=1&page=${page}&typeId=${typeId}`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import {
|
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
|
||||||
httpGetInterceptor,
|
|
||||||
} from "../http-config/http-interceptor-service";
|
|
||||||
|
|
||||||
export async function getFaqList() {
|
export async function getFaqList() {
|
||||||
const url = `master/faq/list`;
|
const url = `master/faq/list`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUserFeedbacks() {
|
||||||
|
const url = "feedback/list-all";
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
|
import { httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function getMediaTrackingMonitoring(page: number, size: number) {
|
export async function getMediaTrackingMonitoring(page: number, size: number) {
|
||||||
const url = `cekmedsos/monitoring/pagination?page=${page}&size=${size}`;
|
const url = `cekmedsos/monitoring/pagination?page=${page}&size=${size}`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function sendMediaUploadToEmail(data: any) {
|
||||||
|
const url = "media/public/share-to-email";
|
||||||
|
return httpPostInterceptor(url, data);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,3 +100,9 @@ export function getLocaleTime(d: Date) {
|
||||||
const pad = (n: number, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
const pad = (n: number, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
}
|
}
|
||||||
|
export function getTimestamp(d: Date) {
|
||||||
|
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
|
||||||
|
return `${pad(d.getFullYear(), 4)}-${pad(d.getMonth() + 1)}-${pad(
|
||||||
|
d.getDate()
|
||||||
|
)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1 @@
|
||||||
#!/bin/sh
|
../typescript/bin/tsc
|
||||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
||||||
|
|
||||||
case `uname` in
|
|
||||||
*CYGWIN*|*MINGW*|*MSYS*)
|
|
||||||
if command -v cygpath > /dev/null 2>&1; then
|
|
||||||
basedir=`cygpath -w "$basedir"`
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -x "$basedir/node" ]; then
|
|
||||||
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
|
||||||
else
|
|
||||||
exec node "$basedir/../typescript/bin/tsc" "$@"
|
|
||||||
fi
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
@ECHO off
|
|
||||||
GOTO start
|
|
||||||
:find_dp0
|
|
||||||
SET dp0=%~dp0
|
|
||||||
EXIT /b
|
|
||||||
:start
|
|
||||||
SETLOCAL
|
|
||||||
CALL :find_dp0
|
|
||||||
|
|
||||||
IF EXIST "%dp0%\node.exe" (
|
|
||||||
SET "_prog=%dp0%\node.exe"
|
|
||||||
) ELSE (
|
|
||||||
SET "_prog=node"
|
|
||||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
||||||
)
|
|
||||||
|
|
||||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/env pwsh
|
|
||||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
||||||
|
|
||||||
$exe=""
|
|
||||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
|
||||||
# Fix case when both the Windows and Linux builds of Node
|
|
||||||
# are installed in the same directory
|
|
||||||
$exe=".exe"
|
|
||||||
}
|
|
||||||
$ret=0
|
|
||||||
if (Test-Path "$basedir/node$exe") {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
} else {
|
|
||||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
} else {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
} else {
|
|
||||||
& "node$exe" "$basedir/../typescript/bin/tsc" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
}
|
|
||||||
exit $ret
|
|
||||||
|
|
@ -1,16 +1 @@
|
||||||
#!/bin/sh
|
../typescript/bin/tsserver
|
||||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
||||||
|
|
||||||
case `uname` in
|
|
||||||
*CYGWIN*|*MINGW*|*MSYS*)
|
|
||||||
if command -v cygpath > /dev/null 2>&1; then
|
|
||||||
basedir=`cygpath -w "$basedir"`
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -x "$basedir/node" ]; then
|
|
||||||
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
|
||||||
else
|
|
||||||
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
|
||||||
fi
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
@ECHO off
|
|
||||||
GOTO start
|
|
||||||
:find_dp0
|
|
||||||
SET dp0=%~dp0
|
|
||||||
EXIT /b
|
|
||||||
:start
|
|
||||||
SETLOCAL
|
|
||||||
CALL :find_dp0
|
|
||||||
|
|
||||||
IF EXIST "%dp0%\node.exe" (
|
|
||||||
SET "_prog=%dp0%\node.exe"
|
|
||||||
) ELSE (
|
|
||||||
SET "_prog=node"
|
|
||||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
||||||
)
|
|
||||||
|
|
||||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsserver" %*
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#!/usr/bin/env pwsh
|
|
||||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
|
||||||
|
|
||||||
$exe=""
|
|
||||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
|
||||||
# Fix case when both the Windows and Linux builds of Node
|
|
||||||
# are installed in the same directory
|
|
||||||
$exe=".exe"
|
|
||||||
}
|
|
||||||
$ret=0
|
|
||||||
if (Test-Path "$basedir/node$exe") {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
} else {
|
|
||||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
} else {
|
|
||||||
# Support pipeline input
|
|
||||||
if ($MyInvocation.ExpectingInput) {
|
|
||||||
$input | & "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
} else {
|
|
||||||
& "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
|
||||||
}
|
|
||||||
$ret=$LASTEXITCODE
|
|
||||||
}
|
|
||||||
exit $ret
|
|
||||||
Loading…
Reference in New Issue