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 ExternalDraggingevent from "./dragging-events";
|
||||
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 { Checkbox } from "@/components/ui/checkbox";
|
||||
import { EventContentArg } from "@fullcalendar/core";
|
||||
|
|
@ -19,6 +19,12 @@ import { getAgendaSettingsList } from "@/service/agenda-setting/agenda-setting";
|
|||
import dayjs from "dayjs";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
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));
|
||||
interface CalendarViewProps {
|
||||
|
|
@ -26,6 +32,21 @@ interface CalendarViewProps {
|
|||
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_MONTH = dayjs().format("M");
|
||||
|
||||
|
|
@ -42,6 +63,33 @@ export interface AgendaSettingsAPIResponse {
|
|||
updatedAt: string;
|
||||
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 {
|
||||
error: boolean;
|
||||
|
|
@ -63,6 +111,8 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
// event canvas state
|
||||
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
||||
const [date, setDate] = React.useState<Date>(new Date());
|
||||
const [activeView, setActiveView] = useState("dayGridMonth");
|
||||
const [yearlyData, setYearlyData] = useState();
|
||||
|
||||
const [dragEvents] = useState([
|
||||
{ title: "New Event Planning", id: "101", tag: "business" },
|
||||
|
|
@ -75,6 +125,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
|
||||
useEffect(() => {
|
||||
getCalendarEvents();
|
||||
getYearlyEvents();
|
||||
}, []);
|
||||
|
||||
const getCalendarEvents = async () => {
|
||||
|
|
@ -82,6 +133,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
console.log("API Response:", res);
|
||||
|
||||
if (res?.error) {
|
||||
error(res?.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +157,15 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
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(() => {
|
||||
setSelectedCategory(categories?.map((c) => c.value));
|
||||
}, [categories]);
|
||||
|
|
@ -212,11 +273,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
|
||||
// event click
|
||||
const handleEventClick = (arg: any) => {
|
||||
console.log("Event Click ", arg);
|
||||
setSelectedEventDate(null);
|
||||
setSheetOpen(true);
|
||||
setApiEvents(arg);
|
||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||
};
|
||||
|
||||
// handle close modal
|
||||
const handleCloseModal = () => {
|
||||
setSheetOpen(false);
|
||||
|
|
@ -225,6 +288,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
};
|
||||
const handleDateClick = (arg: any) => {
|
||||
setSheetOpen(true);
|
||||
console.log("Date :", arg);
|
||||
setSelectedEventDate(arg);
|
||||
setApiEvents([]);
|
||||
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 (
|
||||
<>
|
||||
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
||||
|
|
@ -344,7 +544,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
headerToolbar={{
|
||||
left: "prev,next today",
|
||||
center: "title",
|
||||
right: "dayGridMonth,listWeek,",
|
||||
right: "listYear,dayGridMonth,listMonth",
|
||||
}}
|
||||
views={{
|
||||
listYear: {
|
||||
type: "dayGridMonth",
|
||||
buttonText: "Year",
|
||||
},
|
||||
}}
|
||||
events={displayedEvents}
|
||||
editable={true}
|
||||
|
|
@ -358,9 +564,47 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
eventClassNames={handleClassName}
|
||||
dateClick={handleDateClick}
|
||||
eventClick={handleEventClick}
|
||||
initialView="dayGridMonth"
|
||||
initialView="listYear"
|
||||
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>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
@ -376,17 +620,3 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
};
|
||||
|
||||
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();
|
||||
console.log("Event", event);
|
||||
|
||||
console.log("Event click modal : ", event);
|
||||
}, [agendaType]);
|
||||
|
||||
const handleCheckboxChange = (levelId: number) => {
|
||||
|
|
@ -299,6 +301,7 @@ const EventModal = ({
|
|||
const file = new File([blob], "voiceNote.webm", { type: "audio/webm" });
|
||||
setAudioFile(file);
|
||||
};
|
||||
|
||||
const handleDeleteAudio = () => {
|
||||
// Remove the audio file by setting state to 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,
|
||||
} from "@/service/communication/communication";
|
||||
|
||||
const EscalationTable = () => {
|
||||
const CollaborationTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
|
@ -254,4 +254,4 @@ const EscalationTable = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default EscalationTable;
|
||||
export default CollaborationTable;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,10 @@ export default function CollaborationPage() {
|
|||
loading();
|
||||
const response = await deleteCollabDiscussion(dataId);
|
||||
console.log(response);
|
||||
|
||||
toast({
|
||||
title: "Sukses hapus",
|
||||
});
|
||||
setReplyValue("");
|
||||
close();
|
||||
initState();
|
||||
}
|
||||
|
|
@ -79,7 +82,7 @@ export default function CollaborationPage() {
|
|||
loading();
|
||||
const data = {
|
||||
ticketId: id,
|
||||
replyValue,
|
||||
message: replyValue,
|
||||
parentId: null,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -100,14 +100,6 @@ const columns: ColumnDef<any>[] = [
|
|||
View
|
||||
</DropdownMenuItem>
|
||||
</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>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ import {
|
|||
import { listTicketingInternal } from "@/service/communication/communication";
|
||||
import { Link } from "@/components/navigation";
|
||||
|
||||
const TableAudio = () => {
|
||||
const InternalTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
|
@ -260,4 +260,4 @@ const TableAudio = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default TableAudio;
|
||||
export default InternalTable;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"use client";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import InternalTable from "./internal/components/internal-table";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
|
||||
import CollaborationTable from "./collaboration/components/collabroation-table";
|
||||
|
|
@ -10,6 +9,7 @@ import { useState } from "react";
|
|||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import InternalTable from "./internal/components/internal-table";
|
||||
|
||||
const CommunicationPage = () => {
|
||||
const [tab, setTab] = useState("Komunikasi");
|
||||
|
|
|
|||
|
|
@ -36,22 +36,35 @@ import {
|
|||
TrendingDown,
|
||||
TrendingUp,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
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 { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import { getFaqList } from "@/service/master/faq";
|
||||
import columns from "./column";
|
||||
import columns from "./columns";
|
||||
import {
|
||||
listDataAudio,
|
||||
listDataImage,
|
||||
listDataVideo,
|
||||
} from "@/service/content/content";
|
||||
import {
|
||||
getTicketingCollaborationPagination,
|
||||
getTicketingEscalationPagination,
|
||||
listTicketingInternal,
|
||||
} from "@/service/communication/communication";
|
||||
|
||||
const FaqTable = () => {
|
||||
const CollaborationSpvTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
|
@ -64,13 +77,17 @@ const FaqTable = () => {
|
|||
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: 10,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
const [page, setPage] = 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({
|
||||
data: dataTable,
|
||||
|
|
@ -94,62 +111,99 @@ const FaqTable = () => {
|
|||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get('page');
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
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() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res?.data?.data;
|
||||
const res = await getTicketingCollaborationPagination(
|
||||
page - 1,
|
||||
showData,
|
||||
search
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
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);
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(contentData?.totalElements || contentData?.length);
|
||||
setTotalPage(contentData?.totalPages || 1);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<div className="flex justify-between items-center px-6">
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<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..."
|
||||
placeholder="Search pertanyaan..."
|
||||
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>
|
||||
<div className="flex-none">
|
||||
<Input
|
||||
placeholder="Filter Status..."
|
||||
value={
|
||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||
}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm "
|
||||
/>
|
||||
</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) => (
|
||||
|
|
@ -191,9 +245,13 @@ const FaqTable = () => {
|
|||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import FaqTable from "./components/table";
|
||||
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 () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||
<CardTitle>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
FAQ Data
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
<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 className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between py-3">
|
||||
<p className="text-lg">Kolaborasi</p>
|
||||
|
||||
<Link href="/supervisor/communications/collaboration/create">
|
||||
<Button color="primary" size="md">
|
||||
<PlusIcon />
|
||||
Kolaborasi baru
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<CollaborationSpvTable />
|
||||
</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,
|
||||
TrendingUp,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
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 { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import { getFaqList } from "@/service/master/faq";
|
||||
import columns from "./column";
|
||||
import columns from "./columns";
|
||||
import {
|
||||
listDataAudio,
|
||||
listDataImage,
|
||||
listDataVideo,
|
||||
} from "@/service/content/content";
|
||||
import {
|
||||
getTicketingEscalationPagination,
|
||||
listTicketingInternal,
|
||||
} from "@/service/communication/communication";
|
||||
|
||||
const FaqTable = () => {
|
||||
const EscalationTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
|
@ -64,13 +76,17 @@ const FaqTable = () => {
|
|||
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: 10,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
const [page, setPage] = 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({
|
||||
data: dataTable,
|
||||
|
|
@ -94,38 +110,66 @@ const FaqTable = () => {
|
|||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get('page');
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
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() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res?.data?.data;
|
||||
const res = await getTicketingEscalationPagination(
|
||||
page - 1,
|
||||
Number(showData),
|
||||
search
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
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);
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(contentData?.totalElements || contentData?.length);
|
||||
setTotalPage(contentData?.totalPages || 1);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
} catch (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 (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<div className="flex justify-between items-center px-6">
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<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" />
|
||||
|
|
@ -134,21 +178,39 @@ const FaqTable = () => {
|
|||
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>
|
||||
<div className="flex-none">
|
||||
<Input
|
||||
placeholder="Filter Status..."
|
||||
value={
|
||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||
}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm "
|
||||
/>
|
||||
</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>
|
||||
|
|
@ -191,9 +253,13 @@ const FaqTable = () => {
|
|||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import FaqTable from "./components/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Plus } from "lucide-react";
|
||||
import EscalationTable from "./components/escalation-table";
|
||||
|
||||
const FaqPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||
<CardTitle>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
FAQ Data
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
<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 className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between py-3">
|
||||
<p className="text-lg">Eskalasi</p>
|
||||
</div>
|
||||
<EscalationTable />
|
||||
</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,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
UploadIcon,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
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 { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import { getFaqList } from "@/service/master/faq";
|
||||
import columns from "./column";
|
||||
import columns from "./columns";
|
||||
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 searchParams = useSearchParams();
|
||||
|
||||
|
|
@ -64,13 +75,19 @@ const FaqTable = () => {
|
|||
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: 10,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
const [page, setPage] = 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({
|
||||
data: dataTable,
|
||||
|
|
@ -94,38 +111,63 @@ const FaqTable = () => {
|
|||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get('page');
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
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() {
|
||||
try {
|
||||
const res = await getFaqList();
|
||||
const contentData = res?.data?.data;
|
||||
const res = await listTicketingInternal(
|
||||
page - 1,
|
||||
Number(showData),
|
||||
search
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
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);
|
||||
|
||||
setDataTable(contentData);
|
||||
setTotalData(contentData?.totalElements || contentData?.length);
|
||||
setTotalPage(contentData?.totalPages || 1);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<div className="flex justify-between items-center px-6">
|
||||
<div>
|
||||
<div className="w-full overflow-x-auto ">
|
||||
<div className="flex justify-between items-center">
|
||||
<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" />
|
||||
|
|
@ -134,21 +176,39 @@ const FaqTable = () => {
|
|||
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>
|
||||
<div className="flex-none">
|
||||
<Input
|
||||
placeholder="Filter Status..."
|
||||
value={
|
||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||
}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm "
|
||||
/>
|
||||
</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>
|
||||
|
|
@ -191,9 +251,13 @@ const FaqTable = () => {
|
|||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
totalPage={totalPage}
|
||||
/>
|
||||
</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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import FaqTable from "./components/table";
|
||||
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 () => {
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||
<CardTitle>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
FAQ Data
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
<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 className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between py-3">
|
||||
<p className="text-lg">Pertanyaan Internal</p>
|
||||
<Link href="/supervisor/communications/internal/create">
|
||||
<Button color="primary" size="md">
|
||||
<PlusIcon />
|
||||
Pertanyaan baru
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<InternalSpvTable />
|
||||
</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";
|
||||
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
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 { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
|
||||
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 [selectedSize, setSelectedSize] = useState<string>("L");
|
||||
|
|
@ -29,8 +33,17 @@ const DetailAudio = () => {
|
|||
const [main, setMain] = useState<any>();
|
||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||
|
||||
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(() => {
|
||||
initFetch();
|
||||
|
|
@ -41,6 +54,8 @@ const DetailAudio = () => {
|
|||
const response = await getDetail(String(slug));
|
||||
console.log("detailAudio", response);
|
||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||
setWidth(window.innerWidth);
|
||||
setContent(response?.data.data);
|
||||
setMain({
|
||||
id: response?.data?.data?.files[0]?.id,
|
||||
type: response?.data?.data?.fileType.name,
|
||||
|
|
@ -216,6 +231,59 @@ const DetailAudio = () => {
|
|||
{ 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 (
|
||||
<>
|
||||
<div className="min-h-screen px-4 md:px-24 py-4">
|
||||
|
|
@ -310,6 +378,39 @@ const DetailAudio = () => {
|
|||
</svg>
|
||||
Download
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ import SidebarManagement from "@/components/landing-page/sidebar-management";
|
|||
import withReactContent from "sweetalert2-react-content";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
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 [profile, setProfile] = useState<any>();
|
||||
|
|
@ -20,7 +24,6 @@ const Galery = (props: any) => {
|
|||
const router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const searchParams = useSearchParams();
|
||||
// const { id } = router.query;
|
||||
const page: any = searchParams?.get("page");
|
||||
const title = searchParams?.get("title");
|
||||
const category = searchParams?.get("category");
|
||||
|
|
@ -28,7 +31,6 @@ const Galery = (props: any) => {
|
|||
const { isInstitute, instituteId } = props;
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
|
||||
const [totalContent, setTotalContent] = useState();
|
||||
const [categoryFilter] = useState([]);
|
||||
const [formatFilter] = useState([]);
|
||||
|
|
@ -42,6 +44,10 @@ const Galery = (props: any) => {
|
|||
const [refresh, setRefresh] = useState(false);
|
||||
const [, setCopySuccess] = useState("");
|
||||
const [, setWishlistId] = useState();
|
||||
const [emailShareList, setEmailShareList] = useState<any>();
|
||||
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||
const id = searchParams?.get("id");
|
||||
|
||||
useEffect(() => {
|
||||
getDataVideo();
|
||||
|
|
@ -219,8 +225,44 @@ const Galery = (props: any) => {
|
|||
// 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
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
|
|
@ -236,10 +278,10 @@ const Galery = (props: any) => {
|
|||
<SidebarManagement />
|
||||
<div className="w-2/3 p-12">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Galeri Saya</h1>
|
||||
<h1 className="text-2xl font-semibold mb-3">Galeri Saya</h1>
|
||||
</div>
|
||||
<div className="flex flex-col p-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="flex flex-col">
|
||||
<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}>
|
||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||
<TabsTrigger
|
||||
|
|
@ -272,17 +314,54 @@ const Galery = (props: any) => {
|
|||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
<div className="">
|
||||
{selectedTab == "video" ? (
|
||||
contentVideo?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentVideo?.map((video: any) => (
|
||||
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<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" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||
</Link>
|
||||
<Card key={video?.id}>
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<div>
|
||||
<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 " />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</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(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>
|
||||
</Card>
|
||||
))}
|
||||
|
|
@ -296,11 +375,7 @@ const Galery = (props: any) => {
|
|||
contentAudio?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 ">
|
||||
{contentAudio?.map((audio: any) => (
|
||||
<Link
|
||||
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 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">
|
||||
<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">
|
||||
<path
|
||||
|
|
@ -309,8 +384,10 @@ const Galery = (props: any) => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
||||
<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>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="mt-2">
|
||||
|
|
@ -327,7 +404,42 @@ const Galery = (props: any) => {
|
|||
</svg>
|
||||
</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>
|
||||
) : (
|
||||
|
|
@ -339,12 +451,47 @@ const Galery = (props: any) => {
|
|||
contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentImage?.map((image: any) => (
|
||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Card key={image?.id}>
|
||||
<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}`}>
|
||||
<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>
|
||||
</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>
|
||||
</Card>
|
||||
))}
|
||||
|
|
@ -357,7 +504,7 @@ const Galery = (props: any) => {
|
|||
) : contentDocument.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{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">
|
||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
|
@ -368,7 +515,9 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
|
||||
<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">
|
||||
<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" />
|
||||
|
|
@ -376,7 +525,42 @@ const Galery = (props: any) => {
|
|||
Download Dokumen
|
||||
</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>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@ import withReactContent from "sweetalert2-react-content";
|
|||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import Swal from "sweetalert2";
|
||||
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 { 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 Galery = (props: any) => {
|
||||
const [profile, setProfile] = useState<any>();
|
||||
|
|
@ -23,7 +26,6 @@ const Galery = (props: any) => {
|
|||
const router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const searchParams = useSearchParams();
|
||||
// const { id } = router.query;
|
||||
const page: any = searchParams?.get("page");
|
||||
const title = searchParams?.get("title");
|
||||
const category = searchParams?.get("category");
|
||||
|
|
@ -45,6 +47,10 @@ const Galery = (props: any) => {
|
|||
const [refresh, setRefresh] = useState(false);
|
||||
const [, setCopySuccess] = useState("");
|
||||
const [, setWishlistId] = useState();
|
||||
const [emailShareList, setEmailShareList] = useState<any>();
|
||||
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||
const id = searchParams?.get("id");
|
||||
|
||||
useEffect(() => {
|
||||
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
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
|
|
@ -244,8 +286,8 @@ const Galery = (props: any) => {
|
|||
<div>
|
||||
<h1 className="text-2xl font-semibold">Galeri {profile?.institute?.name}</h1>
|
||||
</div>
|
||||
<div className="flex flex-col p-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="flex flex-col mt-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}>
|
||||
<TabsList className="grid grid-cols-2 lg:flex lg:flex-row ">
|
||||
<TabsTrigger
|
||||
|
|
@ -278,7 +320,7 @@ const Galery = (props: any) => {
|
|||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
<div className="px-2">
|
||||
{selectedTab == "video" ? (
|
||||
contentVideo?.length > 0 ? (
|
||||
<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}`}>
|
||||
<img src={video?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
</Link>
|
||||
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<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>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(video?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</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} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</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 items-center 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-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>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -361,35 +408,41 @@ const Galery = (props: any) => {
|
|||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<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>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(audio?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</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} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</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>
|
||||
|
|
@ -402,41 +455,47 @@ const Galery = (props: any) => {
|
|||
contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{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">
|
||||
<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" />
|
||||
<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>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<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>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(image?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</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} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</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>
|
||||
</Card>
|
||||
))}
|
||||
|
|
@ -470,35 +529,41 @@ const Galery = (props: any) => {
|
|||
Download Dokumen
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<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>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(document?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</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} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const imageSchema = z.object({
|
|||
// tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
const page = (props: { states?: string }) => {
|
||||
const page = (props: any) => {
|
||||
const { states } = props;
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -255,7 +255,7 @@ const page = (props: { states?: string }) => {
|
|||
const maxRetries = 20;
|
||||
|
||||
try {
|
||||
const waitForStatusUpdate = async () => {
|
||||
const waitForStatusUpdate: any = async () => {
|
||||
while (retryCount < maxRetries) {
|
||||
const res = await getDetailArticle(id);
|
||||
const articleData = res?.data?.data;
|
||||
|
|
@ -276,7 +276,7 @@ const page = (props: { states?: string }) => {
|
|||
const articleImagesData = articleData?.imagesUrl?.split(",");
|
||||
setValue("description", cleanArticleBody || "");
|
||||
setArticleBody(cleanArticleBody || "");
|
||||
setDetailData(articleData);
|
||||
setDetailArticle(articleData);
|
||||
setSelectedArticleId(id);
|
||||
setArticleImages(articleImagesData || []);
|
||||
} catch (error) {
|
||||
|
|
@ -404,7 +404,11 @@ const page = (props: { states?: string }) => {
|
|||
{articleIds.map((id: any, index: any) => (
|
||||
<p
|
||||
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)}
|
||||
>
|
||||
{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 className="flex flex-row gap-3">
|
||||
<Button
|
||||
|
|
@ -441,7 +445,7 @@ const page = (props: { states?: string }) => {
|
|||
>
|
||||
Kembali
|
||||
</Button>
|
||||
<Button type="submit" className="border border-blue-500 bg-blue-500 text-white">
|
||||
<Button type="submit" className="border border-blue-500 bg-blue-500 text-white">
|
||||
Simpan
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { close, error, loading } from "@/config/swal";
|
|||
import { useRouter } from "@/i18n/routing";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
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 Swal from "sweetalert2";
|
||||
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 SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import { saveContentRewrite } from "@/service/content/content";
|
||||
import CustomEditor from "@/components/editor/custom-editor";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
const page = (props: any) => {
|
||||
const { states } = props;
|
||||
const page = () => {
|
||||
const [profile, setProfile] = useState();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const [, setLoadingState] = useState(false);
|
||||
const id: any = searchParams?.get("title");
|
||||
const getParams = useParams();
|
||||
const id: any = getParams?.id;
|
||||
const [content, setContent] = useState<any>([]);
|
||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||
const [listSuggestion, setListSuggestion] = useState();
|
||||
|
|
@ -199,96 +201,95 @@ const page = (props: any) => {
|
|||
<div className="text-xl font-bold mb-5">Detail Content Rewrite</div>
|
||||
<div className="p-8 border border-black rounded-lg">
|
||||
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* {content && ( */}
|
||||
<>
|
||||
<div className="w-full">
|
||||
<div className="mb-3">
|
||||
<p className="font-semibold">Judul</p>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className={`w-full p-2 border rounded-md mb-3 border-black ${errors.title ? "is-invalid" : ""}`}
|
||||
{...register("title", {
|
||||
value: content?.title,
|
||||
})}
|
||||
id="title"
|
||||
defaultValue={content?.title}
|
||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between lg:flex-row">
|
||||
<div className="w-50%">
|
||||
<div className="mb-3">
|
||||
<p className="font-semibold">Main Keyword</p>
|
||||
{content && (
|
||||
<>
|
||||
<div className="w-full">
|
||||
<div className="mb-1">
|
||||
<p className="font-bold text-base">Judul</p>
|
||||
</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} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-50%">
|
||||
<div className="mb-3">
|
||||
<label htmlFor="title" className="font-semibold">
|
||||
Additional Keyword{" "}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="border mb-3 rounded-md p-2 border-black"
|
||||
<Input
|
||||
type="text"
|
||||
id="additionalKeyword"
|
||||
name="additionalKeyword"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Additional Keyword disini!"
|
||||
defaultValue={content?.additionalKeyword}
|
||||
className={`w-full mb-3 ${errors.title ? "is-invalid" : ""}`}
|
||||
{...register("title", {
|
||||
value: content?.title,
|
||||
})}
|
||||
id="title"
|
||||
defaultValue={content?.title}
|
||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between gap-3 lg:flex-row">
|
||||
<div className="w-1/2">
|
||||
<div className="mb-1">
|
||||
<p className="font-semibold">Main Keyword</p>
|
||||
</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} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<div className="mb-1">
|
||||
<label htmlFor="title" className="font-semibold">
|
||||
Additional Keyword{" "}
|
||||
</label>
|
||||
</div>
|
||||
<Input
|
||||
className="border mb-3 rounded-md p-2 border-black"
|
||||
type="text"
|
||||
id="additionalKeyword"
|
||||
name="additionalKeyword"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Additional Keyword disini!"
|
||||
defaultValue={content?.additionalKeyword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-between lg:flex-row">
|
||||
<div className="w-50%">
|
||||
<div className="flex gap-3 justify-between lg:flex-row">
|
||||
<div className="w-1/2">
|
||||
<div className="font-semibold mb-1">
|
||||
<label htmlFor="metaTitle">Meta Title</label>
|
||||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
className="border rounded-md mb-3 p-2 border-black"
|
||||
id="metaTitle"
|
||||
name="metaTitle"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Meta Title disini!"
|
||||
defaultValue={content?.metaTitle}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<div className="font-semibold mb-1">
|
||||
<label htmlFor="metaDescription">Meta Description</label>
|
||||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
className="border rounded-md mb-3 p-2 border-black"
|
||||
id="metaDescription"
|
||||
name="metaDescription"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Meta Description disini!"
|
||||
defaultValue={content?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="metaTitle">Meta Title</label>
|
||||
<label htmlFor="description">Deskripsi Artikel</label>
|
||||
<CustomEditor onChange={(e: any) => {}} initialData={articleBody || ""} />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="border rounded-md mb-3 p-2 border-black"
|
||||
id="metaTitle"
|
||||
name="metaTitle"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Meta Title disini!"
|
||||
defaultValue={content?.metaTitle}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-50%">
|
||||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="metaDescription">Meta Description</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="border rounded-md mb-3 p-2 border-black"
|
||||
id="metaDescription"
|
||||
name="metaDescription"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Meta Description disini!"
|
||||
defaultValue={content?.metaDescription}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="description">Deskripsi Artikel</label>
|
||||
</div>
|
||||
{articleBody === null || articleBody === "" ? <div className="w-full px-0 text-sm">Deskripsi tidak boleh kosong</div> : ""}
|
||||
</div>
|
||||
</>
|
||||
{/* )} */}
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,13 @@ import Swal from "sweetalert2";
|
|||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
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 router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -21,23 +26,21 @@ const page = (props: any) => {
|
|||
const title = searchParams?.get("title");
|
||||
const category = searchParams?.get("category");
|
||||
const pages = page ? page - 1 : 0;
|
||||
|
||||
const { isInstitute, instituteId } = props;
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
|
||||
const [, setGetTotalPage] = useState();
|
||||
const [totalContent, setTotalContent] = useState<any>();
|
||||
const [contentImage, setContentImage] = useState([]);
|
||||
|
||||
const [categoryFilter] = useState([]);
|
||||
const [formatFilter] = useState([]);
|
||||
const [sortBy] = useState();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [, setCopySuccess] = useState("");
|
||||
|
||||
const [, setWishlistId] = useState();
|
||||
const [emailShareList, setEmailShareList] = useState<any>();
|
||||
const [emailShareInput, setEmailShareInput] = useState<any>();
|
||||
const [emailMessageInput, setEmailMessageInput] = useState();
|
||||
const id = searchParams?.get("id");
|
||||
|
||||
useEffect(() => {
|
||||
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 (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
|
|
@ -162,16 +202,53 @@ const page = (props: any) => {
|
|||
<div>
|
||||
<h1 className="text-2xl font-semibold mb-4">Galeri Content Rewrite</h1>
|
||||
</div>
|
||||
<div className="px-0 lg:px-10">
|
||||
<div className="">
|
||||
{contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{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">
|
||||
<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" />
|
||||
<div className="">
|
||||
<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" />
|
||||
</Link>
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.title}</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(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>
|
||||
</Card>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
"use client";
|
||||
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import NewContent from "@/components/landing-page/new-content";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
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 { 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 [selectedSize, setSelectedSize] = useState<string>("L");
|
||||
|
|
@ -29,8 +33,17 @@ const DetailDocument = () => {
|
|||
const [main, setMain] = useState<any>();
|
||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||
|
||||
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(() => {
|
||||
initFetch();
|
||||
|
|
@ -41,6 +54,8 @@ const DetailDocument = () => {
|
|||
const response = await getDetail(String(slug));
|
||||
console.log("detailDocument", response);
|
||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||
setWidth(window.innerWidth);
|
||||
setContent(response?.data.data);
|
||||
setMain({
|
||||
id: response?.data?.data?.files[0]?.id,
|
||||
type: response?.data?.data?.fileType.name,
|
||||
|
|
@ -216,6 +231,59 @@ const DetailDocument = () => {
|
|||
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 (
|
||||
<>
|
||||
<div className="px-4 md:px-24 py-4">
|
||||
|
|
@ -307,6 +375,39 @@ const DetailDocument = () => {
|
|||
</svg>
|
||||
Download
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
"use client";
|
||||
|
||||
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 { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import NewContent from "@/components/landing-page/new-content";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
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 { 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 [selectedSize, setSelectedSize] = useState<string>("L");
|
||||
|
|
@ -29,8 +33,17 @@ const DetailInfo = () => {
|
|||
const [main, setMain] = useState<any>();
|
||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||
const [imageSizeSelected, setImageSizeSelected] = useState("l");
|
||||
|
||||
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(() => {
|
||||
initFetch();
|
||||
|
|
@ -41,6 +54,8 @@ const DetailInfo = () => {
|
|||
const response = await getDetail(String(slug));
|
||||
console.log("detailImage", response);
|
||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||
setWidth(window.innerWidth);
|
||||
setContent(response?.data.data);
|
||||
setMain({
|
||||
id: response?.data?.data?.files[0]?.id,
|
||||
type: response?.data?.data?.fileType.name,
|
||||
|
|
@ -219,6 +234,59 @@ const DetailInfo = () => {
|
|||
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 (
|
||||
<>
|
||||
<div className="min-h-screen px-4 md:px-24 py-4">
|
||||
|
|
@ -296,7 +364,7 @@ const DetailInfo = () => {
|
|||
{sizes.map((size: any) => (
|
||||
<div className="flex flex-row justify-between">
|
||||
<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>
|
||||
<div className="">
|
||||
|
|
@ -321,6 +389,39 @@ const DetailInfo = () => {
|
|||
</svg>
|
||||
Download
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -486,7 +486,7 @@ const FilterPage = () => {
|
|||
<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">
|
||||
<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">
|
||||
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
|
||||
{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";
|
||||
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
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 { Textarea } from "@/components/ui/textarea";
|
||||
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 { 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 [selectedSize, setSelectedSize] = useState<string>("L");
|
||||
|
|
@ -28,8 +33,17 @@ const DetailVideo = () => {
|
|||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||
const [main, setMain] = useState<any>();
|
||||
const [resolutionSelected, setResolutionSelected] = useState("720");
|
||||
|
||||
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(() => {
|
||||
initFetch();
|
||||
|
|
@ -40,6 +54,8 @@ const DetailVideo = () => {
|
|||
const response = await getDetail(String(slug));
|
||||
console.log("detailVideo", response);
|
||||
setIsFromSPIT(response?.data?.data?.isFromSPIT);
|
||||
setWidth(window.innerWidth);
|
||||
setContent(response?.data.data);
|
||||
setMain({
|
||||
id: response?.data?.data?.files[0]?.id,
|
||||
type: response?.data?.data?.fileType.name,
|
||||
|
|
@ -216,6 +232,59 @@ const DetailVideo = () => {
|
|||
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 (
|
||||
<>
|
||||
<div className="px-4 md:px-24 py-4">
|
||||
|
|
@ -318,6 +387,39 @@ const DetailVideo = () => {
|
|||
</svg>
|
||||
Download
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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="overflow-y-auto flex flex-wrap w-full h-dvh">
|
||||
<div
|
||||
className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600
|
||||
relative z-[1] bg-default-50"
|
||||
className="lg:block hidden flex-1 overflow-hidden text-[40px] leading-[48px] text-default-600 relative z-[1] bg-default-50"
|
||||
>
|
||||
<div className="max-w-[520px] pt-16 ps-20 ">
|
||||
<Link href="/" className="mb-6 inline-block">
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -577,4 +577,9 @@ html[dir="rtl"] .react-select .select__loading-indicator {
|
|||
|
||||
.ck-editor__editable_inline {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide FullCalendar grid elements */
|
||||
.fc-view-harness:has(.hide-calendar-grid) {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import {
|
|||
saveTicketInternalReply,
|
||||
} from "@/service/communication/communication";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
const taskSchema = z.object({
|
||||
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 = {
|
||||
id: number;
|
||||
message: string;
|
||||
|
|
@ -79,7 +54,7 @@ export default function FormDetailEscalation() {
|
|||
const MySwal = withReactContent(Swal);
|
||||
const { id } = useParams() as { id: string };
|
||||
|
||||
const [detail, setDetail] = useState<escalationDetail>();
|
||||
const [detail, setDetail] = useState<any>();
|
||||
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
||||
const [replyVisible, setReplyVisible] = useState(false);
|
||||
const [replyMessage, setReplyMessage] = useState("");
|
||||
|
|
@ -165,121 +140,134 @@ export default function FormDetailEscalation() {
|
|||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="px-6 py-6">
|
||||
<div className="flex">
|
||||
<div className="flex flex-col mt-6 w-full mb-3">
|
||||
{detail !== undefined && (
|
||||
<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">
|
||||
Ticket #{detail.id}
|
||||
</p>
|
||||
<div className="flex flex-row gap-3 mt-3 bg-blue-300 py-2 px-2">
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src={"/images/avatar/avatar-3.png"}
|
||||
alt={`mabes`}
|
||||
/>
|
||||
</Avatar>
|
||||
<div>
|
||||
<p>
|
||||
<span className="font-bold">
|
||||
{detail?.commentFromUserName}
|
||||
</span>{" "}
|
||||
mengirimkan pesan untuk{" "}
|
||||
<span className="font-bold">{detail?.message}</span>
|
||||
</p>
|
||||
<p>{detail?.createdAt}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col mt-6 w-full mb-3">
|
||||
{detail !== undefined && (
|
||||
<div key={detail?.id} className="bg-slate-300 rounded-md">
|
||||
<p className="p-5 bg-slate-300 rounded-md text-lg font-semibold">
|
||||
Ticket #{detail.id}
|
||||
</p>
|
||||
<div className="flex flex-row gap-3 bg-sky-100 p-5 items-center">
|
||||
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<span className="font-bold">
|
||||
{detail?.commentFromUserName}
|
||||
</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>
|
||||
</div>
|
||||
<p className="pl-3 bg-white">{detail.message}</p>
|
||||
</div>
|
||||
)}
|
||||
<p className="p-5 bg-white">{detail.message}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{detail !== undefined && (
|
||||
<div className="gap-5 mb-5 w-full border mt-3 rounded-md bg-white">
|
||||
<div className="space-y-2 px-3 mt-3">
|
||||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-5 px-3">
|
||||
<Label>Prioritas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedPriority}
|
||||
value={detail?.priority?.name}
|
||||
>
|
||||
<SelectTrigger size="md" className="w-3/12">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Low">Low</SelectItem>
|
||||
<SelectItem value="Medium">Medium</SelectItem>
|
||||
<SelectItem value="High">High</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2 px-3 mt-3">
|
||||
<Label>Description</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Textarea
|
||||
value={detail?.description}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-4 px-3 mb-3">
|
||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
||||
<textarea
|
||||
id="replyMessage"
|
||||
className="w-full h-24 border rounded-md p-2"
|
||||
value={replyMessage}
|
||||
onChange={(e) => setReplyMessage(e.target.value)}
|
||||
placeholder="Tulis pesan di sini..."
|
||||
/>
|
||||
<div className="flex justify-end gap-3 mt-2">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{detail !== undefined && (
|
||||
<div className="gap-5 mb-5 w-full border mt-3 rounded-md">
|
||||
<div className="space-y-2 px-3 mt-3">
|
||||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-5 px-3">
|
||||
<Label>Prioritas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedPriority}
|
||||
value={detail?.priority?.name}
|
||||
>
|
||||
<SelectTrigger size="md" className="w-3/12">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Low">Low</SelectItem>
|
||||
<SelectItem value="Medium">Medium</SelectItem>
|
||||
<SelectItem value="High">High</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2 px-3 mt-3">
|
||||
<Label>Description</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Textarea
|
||||
value={detail?.description}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-4 px-3 mb-3">
|
||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
||||
<textarea
|
||||
id="replyMessage"
|
||||
className="w-full h-24 border rounded-md p-2"
|
||||
value={replyMessage}
|
||||
onChange={(e) => setReplyMessage(e.target.value)}
|
||||
placeholder="Tulis pesan di sini..."
|
||||
/>
|
||||
<div className="flex justify-end gap-3 mt-2">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
getTicketingInternalDiscussion,
|
||||
saveTicketInternalReply,
|
||||
} from "@/service/communication/communication";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -36,6 +37,7 @@ export type taskDetail = {
|
|||
id: number;
|
||||
title: string;
|
||||
createdAt: string;
|
||||
referenceNumber: string | number;
|
||||
createdBy: {
|
||||
id: number;
|
||||
fullname: string;
|
||||
|
|
@ -161,132 +163,132 @@ export default function FormDetailInternal() {
|
|||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="px-6 py-6">
|
||||
<div className="mt-4 flex flex-row items-center gap-3">
|
||||
<Button onClick={handleReply} color="default" variant={"outline"}>
|
||||
Balas
|
||||
</Button>
|
||||
<div className="py-5">
|
||||
<div className="mt-4 flex flex-row items-center gap-3">
|
||||
<Button onClick={handleReply} color="default" variant={"outline"}>
|
||||
Balas
|
||||
</Button>
|
||||
|
||||
<Button color="default" variant={"outline"}>
|
||||
Hapus
|
||||
</Button>
|
||||
</div>
|
||||
<Button color="default" variant={"outline"}>
|
||||
Hapus
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{replyVisible && (
|
||||
<div className="mt-4">
|
||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
||||
<textarea
|
||||
id="replyMessage"
|
||||
className="w-full h-24 border rounded-md p-2"
|
||||
value={replyMessage}
|
||||
onChange={(e) => setReplyMessage(e.target.value)}
|
||||
placeholder="Tulis pesan di sini..."
|
||||
/>
|
||||
<div className="flex justify-end gap-3 mt-2">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
<div className="flex flex-row gap-5 mt-5">
|
||||
<div className="flex flex-col w-[70%]">
|
||||
{replyVisible && (
|
||||
<div className="">
|
||||
<textarea
|
||||
id="replyMessage"
|
||||
className="w-full h-24 border rounded-md p-2"
|
||||
value={replyMessage}
|
||||
onChange={(e) => setReplyMessage(e.target.value)}
|
||||
placeholder="Tulis pesan di sini..."
|
||||
/>
|
||||
<div className="flex justify-end gap-3 my-2">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row justify-between ">
|
||||
<div className="flex flex-col mt-6 w-7/12">
|
||||
)}
|
||||
<div className="border rounded-t-xl">
|
||||
<p className="p-4 bg-slate-300 rounded-t-xl text-lg font-semibold">
|
||||
Ticket #{detail?.referenceNumber}
|
||||
</p>
|
||||
{ticketReply?.map((list) => (
|
||||
<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>
|
||||
<div className="flex flex-row gap-3 mt-3 bg-blue-300 py-2 px-2">
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src={"/images/avatar/avatar-3.png"}
|
||||
alt={`mabes`}
|
||||
/>
|
||||
</Avatar>
|
||||
<div key={list.id} className="flex flex-col">
|
||||
<div className="flex flex-row gap-3 bg-sky-100 p-4 items-center">
|
||||
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||
<div>
|
||||
<p>
|
||||
<span className="font-bold">
|
||||
<span className="font-bold text-sm">
|
||||
{list?.messageFrom?.fullname}
|
||||
</span>{" "}
|
||||
mengirimkan pesan untuk{" "}
|
||||
<span className="font-bold">
|
||||
<span className="font-bold text-sm">
|
||||
{list?.messageTo?.fullname}
|
||||
</span>
|
||||
</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>
|
||||
<p className="pl-3 bg-white py-2">{list.message}</p>
|
||||
<p className="p-4 bg-white text-sm">{list.message}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{detail !== undefined && (
|
||||
<div className="gap-5 mb-5 w-3/12 border mt-3 rounded-md">
|
||||
<Label className="ml-3 mt-3">Properties</Label>
|
||||
<div className="space-y-2 px-3">
|
||||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
</div>
|
||||
{detail !== undefined && (
|
||||
<div className="gap-5 mb-5 w-[30%] border bg-white rounded-md">
|
||||
<p className="mx-3 mt-3">Properties</p>
|
||||
<div className="space-y-2 px-3">
|
||||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-5 px-3">
|
||||
<Label>Prioritas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedPriority}
|
||||
value={detail?.priority?.name}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Low">Low</SelectItem>
|
||||
<SelectItem value="Medium">Medium</SelectItem>
|
||||
<SelectItem value="High">High</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="mt-5 px-3 mb-3">
|
||||
<Label>Status</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedStatus}
|
||||
value={detail?.status?.name}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Open">Open</SelectItem>
|
||||
<SelectItem value="Close">Close</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-5 px-3">
|
||||
<Label>Prioritas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedPriority}
|
||||
value={detail?.priority?.name}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Low">Low</SelectItem>
|
||||
<SelectItem value="Medium">Medium</SelectItem>
|
||||
<SelectItem value="High">High</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="mt-5 px-3 mb-3">
|
||||
<Label>Status</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedStatus}
|
||||
value={detail?.status?.name}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Open">Open</SelectItem>
|
||||
<SelectItem value="Close">Close</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import {
|
|||
saveTicketInternalReply,
|
||||
} from "@/service/communication/communication";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { htmlToString } from "@/utils/globals";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
|
||||
const taskSchema = z.object({
|
||||
// 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 = {
|
||||
id: number;
|
||||
message: string;
|
||||
|
|
@ -76,13 +54,14 @@ export default function FormEditInternal() {
|
|||
const { id } = useParams() as { id: string };
|
||||
const router = useRouter();
|
||||
|
||||
const [detail, setDetail] = useState<taskDetail>();
|
||||
const [detail, setDetail] = useState<any>();
|
||||
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
||||
const [replyVisible, setReplyVisible] = useState(false);
|
||||
const [replyMessage, setReplyMessage] = useState("");
|
||||
const [selectedPriority, setSelectedPriority] = useState("");
|
||||
const [selectedStatus, setSelectedStatus] = useState("");
|
||||
const [selectedTarget, setSelectedTarget] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
type TaskSchema = z.infer<typeof taskSchema>;
|
||||
|
||||
const {
|
||||
|
|
@ -98,6 +77,10 @@ export default function FormEditInternal() {
|
|||
if (id) {
|
||||
const response = await getTicketingInternalDetail(id);
|
||||
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();
|
||||
|
|
@ -206,56 +189,51 @@ export default function FormEditInternal() {
|
|||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="px-6 py-6">
|
||||
<div className="mt-4 flex flex-row items-center gap-3">
|
||||
<Button onClick={handleReply} color="default" variant={"outline"}>
|
||||
Balas
|
||||
</Button>
|
||||
<div className="py-5">
|
||||
<div className="mt-4 flex flex-row items-center gap-3">
|
||||
<Button onClick={handleReply} color="default" variant={"outline"}>
|
||||
Balas
|
||||
</Button>
|
||||
|
||||
<Button color="default" variant={"outline"}>
|
||||
Hapus
|
||||
</Button>
|
||||
</div>
|
||||
<Button color="default" variant={"outline"}>
|
||||
Hapus
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{replyVisible && (
|
||||
<div className="mt-4">
|
||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
||||
<textarea
|
||||
id="replyMessage"
|
||||
className="w-full h-24 border rounded-md p-2"
|
||||
value={replyMessage}
|
||||
onChange={(e) => setReplyMessage(e.target.value)}
|
||||
placeholder="Tulis pesan di sini..."
|
||||
/>
|
||||
<div className="flex justify-end gap-3 mt-2">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
<div className="flex flex-row gap-5 mt-5">
|
||||
<div className="flex flex-col w-[70%]">
|
||||
{replyVisible && (
|
||||
<div>
|
||||
<textarea
|
||||
id="replyMessage"
|
||||
className="w-full h-24 border rounded-md p-2"
|
||||
value={replyMessage}
|
||||
onChange={(e) => setReplyMessage(e.target.value)}
|
||||
placeholder="Tulis pesan di sini..."
|
||||
/>
|
||||
<div className="flex justify-end gap-3 mt-2">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row justify-between ">
|
||||
<div className="flex flex-col mt-6 w-7/12">
|
||||
)}
|
||||
<div className="border rounded-t-xl">
|
||||
<p className="p-4 bg-slate-300 rounded-t-xl text-lg font-semibold">
|
||||
Ticket #{detail?.referenceNumber}
|
||||
</p>
|
||||
{ticketReply?.map((list) => (
|
||||
<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>
|
||||
<div className="flex flex-row gap-3 mt-3 bg-blue-300 py-2 px-2">
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src={"/images/avatar/avatar-3.png"}
|
||||
alt={`mabes`}
|
||||
/>
|
||||
</Avatar>
|
||||
<div key={list.id} className="flex flex-col">
|
||||
<div className="flex flex-row gap-3 bg-sky-100 p-4 items-center">
|
||||
<Icon icon="qlementine-icons:user-16" width={36} />
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<span className="font-bold">
|
||||
|
|
@ -266,98 +244,96 @@ export default function FormEditInternal() {
|
|||
{list?.messageTo?.fullname}
|
||||
</span>
|
||||
</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>
|
||||
<p className="pl-3 bg-white py-2">{list.message}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{detail !== undefined && (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="gap-5 mb-5 w-full border mt-3 rounded-md">
|
||||
<Label className="ml-3 mt-3">Properties</Label>
|
||||
<div className="space-y-2 px-3">
|
||||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-5 px-3">
|
||||
<Label>Prioritas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedPriority}
|
||||
value={detail?.priority?.name}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Low">Low</SelectItem>
|
||||
<SelectItem value="Medium">Medium</SelectItem>
|
||||
<SelectItem value="High">High</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="mt-5 px-3 mb-3">
|
||||
<Label>Status</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedStatus}
|
||||
value={detail?.status?.name}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">Open</SelectItem>
|
||||
<SelectItem value="2">Close</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2 px-3">
|
||||
<Label>Description</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<Textarea
|
||||
value={detail?.description}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter description"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="flex justify-end mt-3 mr-3">
|
||||
<Button type="submit" color="primary">
|
||||
Update
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
{detail !== undefined && (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="w-[30%]">
|
||||
<div className="gap-5 mb-5 border bg-white rounded-xl">
|
||||
<p className="mx-3 mt-3">Properties</p>
|
||||
<div className="space-y-2 px-3">
|
||||
<Label>Judul</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={detail?.title}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {errors.title?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title.message}
|
||||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-5 px-3">
|
||||
<Label>Prioritas</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedPriority}
|
||||
value={selectedPriority}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Low">Low</SelectItem>
|
||||
<SelectItem value="Medium">Medium</SelectItem>
|
||||
<SelectItem value="High">High</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="mt-5 px-3 mb-3">
|
||||
<Label>Status</Label>
|
||||
<Select
|
||||
onValueChange={setSelectedStatus}
|
||||
value={selectedStatus}
|
||||
>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Open">Open</SelectItem>
|
||||
<SelectItem value="Close">Close</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2 px-3">
|
||||
<Label>Description</Label>
|
||||
|
||||
<Textarea
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="Enter description"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end mt-3 mr-3">
|
||||
<Button type="submit" color="primary">
|
||||
Update
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import { Icon } from "@iconify/react/dist/iconify.js";
|
|||
import { error } from "@/lib/swal";
|
||||
import dynamic from "next/dynamic";
|
||||
import WavesurferPlayer from "@wavesurfer/react";
|
||||
import WaveSurfer from "wavesurfer.js";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -141,14 +142,21 @@ export default function FormAudioDetail() {
|
|||
const [audioPlaying, setAudioPlaying] = useState<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 = () => {
|
||||
if (waveSurferRef.current) {
|
||||
waveSurferRef.current.playPause();
|
||||
setIsPlaying(!isPlaying);
|
||||
}
|
||||
};
|
||||
wavesurfer && wavesurfer.playPause();
|
||||
}
|
||||
|
||||
|
||||
let fileTypeId = "4";
|
||||
|
||||
|
|
@ -452,10 +460,12 @@ export default function FormAudioDetail() {
|
|||
{detailThumb?.map((url: any, index: number) => (
|
||||
<div key={url.id}>
|
||||
<WavesurferPlayer
|
||||
ref={waveSurferRef}
|
||||
height={500}
|
||||
waveColor="red"
|
||||
url={url}
|
||||
onReady={onReady}
|
||||
onPlay={() => setIsPlaying(true)}
|
||||
onPause={() => setIsPlaying(false)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,13 @@ import {
|
|||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} 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 = {
|
||||
id: number;
|
||||
|
|
@ -59,6 +65,7 @@ type Detail = {
|
|||
|
||||
const Navbar = () => {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [inboxOpen, setInboxOpen] = useState(false);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
|
@ -77,6 +84,10 @@ const Navbar = () => {
|
|||
const [role, setRole] = useState<any>();
|
||||
const [menuActive, setMenuActive] = useState<string>();
|
||||
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
|
||||
? `/polda/${poldaName}`
|
||||
|
|
@ -104,12 +115,44 @@ const Navbar = () => {
|
|||
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(() => {
|
||||
async function initState() {
|
||||
const response = await getInfoProfile();
|
||||
if (!response?.error) {
|
||||
const details = response?.data?.data;
|
||||
|
||||
setInboxOpen(details);
|
||||
setDetail(details);
|
||||
console.log("data", details);
|
||||
}
|
||||
|
|
@ -121,39 +164,6 @@ const Navbar = () => {
|
|||
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) => {
|
||||
setSearch(e.target.value);
|
||||
};
|
||||
|
|
@ -214,7 +224,7 @@ const Navbar = () => {
|
|||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger className="">
|
||||
<NavigationMenuTrigger>
|
||||
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer">
|
||||
<svg
|
||||
className="mx-2 dark:"
|
||||
|
|
@ -232,12 +242,12 @@ const Navbar = () => {
|
|||
{t("content")}
|
||||
</a>
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent className=" rounded-md overflow-hidden w-">
|
||||
<NavigationMenuContent className="flex flex-col place-content-start rounded-md overflow-hidden ">
|
||||
<NavigationMenuLink
|
||||
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" />
|
||||
{t("image")}
|
||||
</p>
|
||||
|
|
@ -248,29 +258,25 @@ const Navbar = () => {
|
|||
>
|
||||
{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" />
|
||||
{t("video")}
|
||||
</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" />
|
||||
{t("video")}
|
||||
</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
|
||||
onClick={() => router.push(prefixPath + "/document/filter")}
|
||||
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" />
|
||||
{t("text")}
|
||||
</p>
|
||||
|
|
@ -279,7 +285,7 @@ const Navbar = () => {
|
|||
onClick={() => router.push(prefixPath + "/audio/filter")}
|
||||
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" />
|
||||
{t("audio")}{" "}
|
||||
</p>
|
||||
|
|
@ -335,10 +341,10 @@ const Navbar = () => {
|
|||
</NavigationMenuList>
|
||||
</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="font-medium">{t("live")}</span>
|
||||
</Link>
|
||||
</Link> */}
|
||||
<div className="flex items-center space-x-1 ">
|
||||
<a href="https://tvradio.polri.go.id/">
|
||||
<img
|
||||
|
|
@ -431,76 +437,226 @@ const Navbar = () => {
|
|||
roleId === "6" ||
|
||||
roleId === "7" ||
|
||||
roleId === "8" ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||
{detail !== undefined ? (
|
||||
<div className="flex items-center gap-3 text-default-800">
|
||||
<Image
|
||||
src={"/assets/avatar-profile.png"}
|
||||
alt={"Image"}
|
||||
width={36}
|
||||
height={36}
|
||||
className="rounded-full"
|
||||
<>
|
||||
{/* Inbox */}
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<a className="cursor-pointer" onClick={() => test()}>
|
||||
{" "}
|
||||
<Icon
|
||||
icon="basil:envelope-outline"
|
||||
color="black"
|
||||
width="30"
|
||||
/>
|
||||
<div>
|
||||
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">
|
||||
{detail?.fullname}
|
||||
</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>
|
||||
<p className="text-xs whitespace-nowrap overflow-hidden truncate">
|
||||
({detail?.fullname})
|
||||
</p>
|
||||
</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>
|
||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||
{detail !== undefined ? (
|
||||
<div className="flex items-center gap-3 text-default-800">
|
||||
<Image
|
||||
src={"/assets/avatar-profile.png"}
|
||||
alt={"Image"}
|
||||
width={36}
|
||||
height={36}
|
||||
className="rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">
|
||||
{detail?.fullname}
|
||||
</div>
|
||||
<p className="text-xs whitespace-nowrap overflow-hidden truncate">
|
||||
({detail?.fullname})
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-base me-2.5 lg:inline-block hidden">
|
||||
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-base me-2.5 lg:inline-block hidden">
|
||||
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56 p-0" align="end">
|
||||
<DropdownMenuGroup>
|
||||
{[
|
||||
{
|
||||
name: "Profile & Settings",
|
||||
icon: "heroicons:user",
|
||||
href: "/profile",
|
||||
},
|
||||
{
|
||||
name: "Kelola Konten",
|
||||
icon: "stash:save-ribbon-duotone",
|
||||
href: "/content-management/galery",
|
||||
},
|
||||
].map((item, index) => (
|
||||
<Link
|
||||
href={item.href}
|
||||
key={`info-menu-${index}`}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
||||
<Icon icon={item.icon} className="w-4 h-4" />
|
||||
{item.name}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
||||
<div>
|
||||
<Link href={"/"}>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full flex items-center gap-2"
|
||||
onClick={onLogout}
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56 p-0" align="end">
|
||||
<DropdownMenuGroup>
|
||||
{[
|
||||
{
|
||||
name: "Profile & Settings",
|
||||
icon: "heroicons:user",
|
||||
href: "/profile",
|
||||
},
|
||||
{
|
||||
name: "Kelola Konten",
|
||||
icon: "stash:save-ribbon-duotone",
|
||||
href: "/content-management/galery",
|
||||
},
|
||||
].map((item, index) => (
|
||||
<Link
|
||||
href={item.href}
|
||||
key={`info-menu-${index}`}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Icon icon="heroicons:power" className="w-4 h-4" />
|
||||
{t("logOut")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
||||
<Icon icon={item.icon} className="w-4 h-4" />
|
||||
{item.name}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
||||
<div>
|
||||
<Link href={"/"}>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full flex items-center gap-2"
|
||||
onClick={onLogout}
|
||||
>
|
||||
<Icon icon="heroicons:power" className="w-4 h-4" />
|
||||
{t("logOut")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
) : roleId === "2" ||
|
||||
roleId === "3" ||
|
||||
roleId === "4" ||
|
||||
|
|
@ -509,77 +665,221 @@ const Navbar = () => {
|
|||
roleId === "11" ||
|
||||
roleId === "12" ||
|
||||
roleId === "13" ? (
|
||||
// Dropdown menu for roleId === 3
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||
{detail !== undefined ? (
|
||||
<div className="flex items-center gap-3 text-default-800">
|
||||
<Image
|
||||
src={"/assets/avatar-profile.png"}
|
||||
alt={"Image"}
|
||||
width={36}
|
||||
height={36}
|
||||
className="rounded-full"
|
||||
<>
|
||||
{/* Inbox */}
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<a className="cursor-pointer" onClick={() => test()}>
|
||||
{" "}
|
||||
<Icon
|
||||
icon="basil:envelope-outline"
|
||||
color="black"
|
||||
width="30"
|
||||
/>
|
||||
<div>
|
||||
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">
|
||||
{detail?.fullname}
|
||||
</div>
|
||||
<p className="text-xs whitespace-nowrap overflow-hidden truncate">
|
||||
({detail?.fullname})
|
||||
</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>
|
||||
</div>
|
||||
<span className="text-base me-2.5 lg:inline-block hidden">
|
||||
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56 p-0" align="end">
|
||||
<DropdownMenuGroup>
|
||||
{[
|
||||
{
|
||||
name: "Profile & Settings",
|
||||
icon: "heroicons:user",
|
||||
href: "/profile",
|
||||
},
|
||||
{
|
||||
name: "Dashboard",
|
||||
icon: "material-symbols:dashboard",
|
||||
href: "/dashboard",
|
||||
},
|
||||
].map((item, index) => (
|
||||
<Link
|
||||
href={item.href}
|
||||
key={`info-menu-${index}`}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
||||
<Icon icon={item.icon} className="w-4 h-4" />
|
||||
{item.name}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
||||
<div>
|
||||
<Link href={"/"}>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full flex items-center gap-2"
|
||||
onClick={onLogout}
|
||||
</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}
|
||||
>
|
||||
<Icon icon="heroicons:power" className="w-4 h-4" />
|
||||
{t("logOut")}
|
||||
</button>
|
||||
<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>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
{/* // Dropdown menu for roleId === 3 */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="cursor-pointer">
|
||||
{detail !== undefined ? (
|
||||
<div className="flex items-center gap-3 text-default-800">
|
||||
<Image
|
||||
src={"/assets/avatar-profile.png"}
|
||||
alt={"Image"}
|
||||
width={36}
|
||||
height={36}
|
||||
className="rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">
|
||||
{detail?.fullname}
|
||||
</div>
|
||||
<p className="text-xs whitespace-nowrap overflow-hidden truncate">
|
||||
({detail?.fullname})
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-base me-2.5 lg:inline-block hidden">
|
||||
<Icon icon="heroicons-outline:chevron-down"></Icon>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56 p-0" align="end">
|
||||
<DropdownMenuGroup>
|
||||
{[
|
||||
{
|
||||
name: "Profile & Settings",
|
||||
icon: "heroicons:user",
|
||||
href: "/profile",
|
||||
},
|
||||
{
|
||||
name: "Dashboard",
|
||||
icon: "material-symbols:dashboard",
|
||||
href: "/dashboard",
|
||||
},
|
||||
].map((item, index) => (
|
||||
<Link
|
||||
href={item.href}
|
||||
key={`info-menu-${index}`}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize px-3 py-1.5 cursor-pointer">
|
||||
<Icon icon={item.icon} className="w-4 h-4" />
|
||||
{item.name}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="flex items-center gap-2 text-sm font-medium text-default-600 capitalize my-1 px-3 cursor-pointer">
|
||||
<div>
|
||||
<Link href={"/"}>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full flex items-center gap-2"
|
||||
onClick={onLogout}
|
||||
>
|
||||
<Icon icon="heroicons:power" className="w-4 h-4" />
|
||||
{t("logOut")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
) : (
|
||||
// Masuk and Daftar buttons for roleId === null
|
||||
<div className="flex justify-center items-center mx-3 gap-5">
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ const SidebarManagement = () => {
|
|||
// Render
|
||||
if (!hasMounted) return null;
|
||||
return (
|
||||
<div className="p-12 w-1/3">
|
||||
<div className="p-12 w-1/3 ">
|
||||
<div className="border rounded-2xl border-black m-4">
|
||||
<h1 className="text-xl p-5">Tentang Saya</h1>
|
||||
<div>
|
||||
|
|
@ -94,7 +94,7 @@ const SidebarManagement = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4">
|
||||
<div className="p-4 gap-1">
|
||||
<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="flex items-center gap-2 text-lg">
|
||||
|
|
|
|||
|
|
@ -2176,7 +2176,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
|||
icon: "icon-park-outline:communication",
|
||||
submenus: [
|
||||
{
|
||||
href: "/supervisor/communications/questions",
|
||||
href: "/supervisor/communications/questions/all",
|
||||
label: t("questions"),
|
||||
active: pathname.includes("/communications/questions"),
|
||||
icon: "solar:inbox-line-outline",
|
||||
|
|
|
|||
|
|
@ -141,11 +141,11 @@
|
|||
"ticketing": "Ticketing",
|
||||
"knowledge-base": "Knowledge Base",
|
||||
"faq": "FAQ",
|
||||
"questions": "Questions",
|
||||
"internal": "Internal Questions",
|
||||
"forward": "Forward",
|
||||
"collaboration": "Collaboration",
|
||||
"account-report": "Account Report",
|
||||
"questions": "Pertanyaan Masuk",
|
||||
"internal": "Pertanyaan Internal",
|
||||
"forward": "Eskalasi",
|
||||
"collaboration": "Kolaborasi",
|
||||
"account-report": "Pelaporan Akun",
|
||||
"settings": "Pengaturan",
|
||||
"feedback": "Feedback",
|
||||
"content-production": "Produksi Konten",
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@
|
|||
"tus-js-client": "^4.2.3",
|
||||
"use-places-autocomplete": "^4.0.1",
|
||||
"vaul": "^0.9.1",
|
||||
"wavesurfer.js": "^7.8.15",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
@ -158,7 +159,6 @@
|
|||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"postcss": "^8",
|
||||
"react-wavesurfer.js": "0.0.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
|
@ -844,6 +844,11 @@
|
|||
"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": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emoji-mart/data/-/data-1.2.1.tgz",
|
||||
|
|
@ -14271,17 +14276,6 @@
|
|||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
|
@ -17165,9 +17159,9 @@
|
|||
}
|
||||
},
|
||||
"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=="
|
||||
"version": "7.8.15",
|
||||
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.8.15.tgz",
|
||||
"integrity": "sha512-fWNnQt5BEGzuoJ7HRxfvpT1rOEI1AmCGPZ/+7QDkDVN/m2vIBeLVQ+5vENRMz1YwvZ/u1No0UV492/o8G++KXQ=="
|
||||
},
|
||||
"node_modules/web-namespaces": {
|
||||
"version": "2.0.1",
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@
|
|||
"tus-js-client": "^4.2.3",
|
||||
"use-places-autocomplete": "^4.0.1",
|
||||
"vaul": "^0.9.1",
|
||||
"wavesurfer.js": "^7.8.15",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
@ -159,7 +160,6 @@
|
|||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"postcss": "^8",
|
||||
"react-wavesurfer.js": "0.0.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"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";
|
||||
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}`;
|
||||
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";
|
||||
|
||||
export async function getHeroData() {
|
||||
|
|
@ -113,3 +114,17 @@ export async function getPublicSuggestionList(id: any) {
|
|||
const url = `media/public/suggestion?mediaId=${id}`;
|
||||
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 {
|
||||
httpGetInterceptor,
|
||||
} from "../http-config/http-interceptor-service";
|
||||
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
|
||||
|
||||
export async function getFaqList() {
|
||||
const url = `master/faq/list`;
|
||||
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) {
|
||||
const url = `cekmedsos/monitoring/pagination?page=${page}&size=${size}`;
|
||||
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);
|
||||
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
|
||||
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
|
||||
../typescript/bin/tsc
|
||||
|
|
@ -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
|
||||
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
|
||||
../typescript/bin/tsserver
|
||||
|
|
@ -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