feat:update konten, update task,fix logo header admin

This commit is contained in:
Anang Yusman 2025-01-12 23:21:22 +08:00
commit b9fc0a63e4
69 changed files with 5796 additions and 1607 deletions

View File

@ -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;
};
};

View File

@ -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);

View File

@ -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>
);
}

View File

@ -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;

View File

@ -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,
};

View File

@ -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>
);

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>
);
}

View File

@ -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>
);
}

View File

@ -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>
);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>
);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>
);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>
);
}

View File

@ -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}&quote=${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>

View File

@ -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>
) : (

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
))}

View File

@ -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}&quote=${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>

View File

@ -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}&quote=${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>

View File

@ -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}{" "}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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}&quote=${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>

View File

@ -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

View File

@ -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;
}

View File

@ -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>
);
}

View File

@ -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>
);
}

View File

@ -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>
);
}

View File

@ -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>
))}

View File

@ -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">

View File

@ -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">

View File

@ -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",

View File

@ -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",

24
package-lock.json generated
View File

@ -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",

View File

@ -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"
}

File diff suppressed because it is too large Load Diff

View File

@ -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 });
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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())}`;
}

View File

@ -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

View File

@ -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" %*

View File

@ -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

View File

@ -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

View File

@ -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" %*

View File

@ -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