Merge branches 'dev-main' and 'dev-restructure' of https://gitlab.com/hanifsalafi/web-humas-polri into dev-main
This commit is contained in:
commit
6747a1011d
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:21-alpine
|
FROM node:23.5.0-alpine
|
||||||
|
|
||||||
ENV PORT 4000
|
ENV PORT 4000
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ WORKDIR /usr/src/app
|
||||||
COPY package*.json /usr/src/app/
|
COPY package*.json /usr/src/app/
|
||||||
|
|
||||||
# RUN npm install --force
|
# RUN npm install --force
|
||||||
# RUN npm install -g npm@latest
|
RUN npm install -g npm@latest
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
# Copying source files
|
# Copying source files
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Card } from "@nextui-org/react";
|
||||||
|
|
||||||
export default function CreateArticle() {
|
export default function CreateArticle() {
|
||||||
return (
|
return (
|
||||||
<div className="bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
<div className="bg-transparent p-3 lg:p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||||
{/* <FormArticle /> */}
|
{/* <FormArticle /> */}
|
||||||
<CreateArticleForm />
|
<CreateArticleForm />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import EditArticleForm from "@/components/form/article/edit-article-form";
|
||||||
|
|
||||||
export default function DetailArticlePage() {
|
export default function DetailArticlePage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
<div className="h-[96vh] bg-transparent p-3 lg:p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||||
{/* <FormDetailArticle /> */}
|
{/* <FormDetailArticle /> */}
|
||||||
<EditArticleForm isDetail={true} />
|
<EditArticleForm isDetail={true} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import EditArticleForm from "@/components/form/article/edit-article-form";
|
||||||
|
|
||||||
export default function UpdateArticlePage() {
|
export default function UpdateArticlePage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
<div className="h-[96vh] bg-transparent p-3 lg:p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||||
<EditArticleForm isDetail={false} />
|
<EditArticleForm isDetail={false} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,12 @@ export default function BasicPage() {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-x-hidden overflow-y-scroll">
|
<div className="overflow-x-hidden overflow-y-scroll">
|
||||||
<div className="px-2 md:px-4 md:py-4 w-full">
|
<div className="px-2 md:px-4 md:py-4 w-full">
|
||||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-3">
|
||||||
<Link href="/admin/article/create" className="mx-3">
|
<Link href="/admin/article/create">
|
||||||
<Button size="md" className="bg-[#F07C00] text-white">
|
<Button
|
||||||
|
size="md"
|
||||||
|
className="bg-[#F07C00] text-white w-full lg:w-fit"
|
||||||
|
>
|
||||||
Tambah Artikel
|
Tambah Artikel
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import DashboardContainer from "@/components/main/dashboard/dashboard-container"
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] overflow-x-hidden overflow-y-scroll gap-0 grid">
|
<div className="h-[96vh] overflow-x-hidden overflow-y-scroll gap-0 grid">
|
||||||
<div className="px-4">
|
<div className="lg:px-4 !w-screen lg:!w-auto">
|
||||||
<DashboardContainer />
|
<DashboardContainer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import React from "react";
|
||||||
|
|
||||||
const AdminMagazineCreate = () => {
|
const AdminMagazineCreate = () => {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
<div className="h-[96vh] bg-transparent p-3 lg:p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||||
<NewCreateMagazineForm />
|
<NewCreateMagazineForm />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import EditMagazineForm from "@/components/form/magazine/edit-magazine-form";
|
||||||
|
|
||||||
export default function DetailArticlePage() {
|
export default function DetailArticlePage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
<div className="h-[96vh] bg-transparent p-3 lg:p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||||
<EditMagazineForm isDetail={true} />
|
<EditMagazineForm isDetail={true} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import EditMagazineForm from "@/components/form/magazine/edit-magazine-form";
|
||||||
|
|
||||||
export default function EditArticlePage() {
|
export default function EditArticlePage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
<div className="h-[96vh] bg-transparent p-3 lg:p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||||
<EditMagazineForm isDetail={false} />
|
<EditMagazineForm isDetail={false} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@ export default function MagazineTablePage() {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-x-hidden overflow-y-scroll">
|
<div className="overflow-x-hidden overflow-y-scroll">
|
||||||
<div className="px-2 md:px-4 md:py-4 w-full">
|
<div className="px-2 md:px-4 md:py-4 w-full">
|
||||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-3">
|
||||||
<Link href="/admin/magazine/create" className="mx-3">
|
<Link href="/admin/magazine/create">
|
||||||
<Button size="md" className="bg-[#F07C00] text-white">
|
<Button
|
||||||
|
size="md"
|
||||||
|
className="bg-[#F07C00] text-white w-full lg:w-fit"
|
||||||
|
>
|
||||||
Tambah Majalah
|
Tambah Majalah
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -164,10 +164,10 @@ export default function MasterCategoryTable() {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-x-hidden overflow-y-scroll">
|
<div className="overflow-x-hidden overflow-y-scroll">
|
||||||
<div className="px-2 md:px-4 md:py-4 w-full">
|
<div className="px-2 md:px-4 md:py-4 w-full">
|
||||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-3">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
className="bg-[#F07C00] text-white mx-3"
|
className="bg-[#F07C00] text-white w-full lg:w-fit"
|
||||||
onPress={onOpen}
|
onPress={onOpen}
|
||||||
>
|
>
|
||||||
Tambah Kategori
|
Tambah Kategori
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import MagazineTable from '@/components/table/magazine/magazine-table'
|
import MappingUserLevel from "@/components/table/master/master-user-level/mapping-user-level";
|
||||||
import MenuDataTable from '@/components/table/master/master-menu/menu-data/menu-data-table'
|
import MasterUserLevelTable from "@/components/table/master/master-user-level/master-user-level-table";
|
||||||
import MasterModuleTable from '@/components/table/master/master-module/master-module-table'
|
import React from "react";
|
||||||
import MasterUserLevelTable from '@/components/table/master/master-user-level/master-user-level-table'
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
const AdminMasterUserLevel = () => {
|
const AdminMasterUserLevel = () => {
|
||||||
return (
|
return (
|
||||||
<div><MasterUserLevelTable /></div>
|
// <div><MasterUserLevelTable /></div>
|
||||||
)
|
<div>
|
||||||
}
|
<MappingUserLevel />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default AdminMasterUserLevel
|
export default AdminMasterUserLevel;
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ export default function StaticPageGeneratorList() {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-x-hidden overflow-y-scroll rounded-lg">
|
<div className="overflow-x-hidden overflow-y-scroll rounded-lg">
|
||||||
<div className="px-2 md:px-4 md:py-4 w-full">
|
<div className="px-2 md:px-4 md:py-4 w-full">
|
||||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-3">
|
||||||
<Link href="/admin/static-page/create" className="mx-3">
|
<Link href="/admin/static-page/create">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
color="primary"
|
color="primary"
|
||||||
className="bg-[#F07C00] text-white"
|
className="bg-[#F07C00] text-white w-full lg:w-fit"
|
||||||
>
|
>
|
||||||
Tambah Halaman
|
Tambah Halaman
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
import PasswordForm from "@/components/form/settings/password";
|
import PasswordForm from "@/components/form/settings/password";
|
||||||
import ProfileForm from "@/components/form/settings/profile";
|
import ProfileForm from "@/components/form/settings/profile";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
import { getProfile } from "@/service/master-user";
|
import { getProfile } from "@/service/master-user";
|
||||||
import { Tab, Tabs } from "@nextui-org/react";
|
import { Tab, Tabs } from "@nextui-org/react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
@ -11,11 +12,13 @@ export default function Settings() {
|
||||||
initFetch();
|
initFetch();
|
||||||
}, []);
|
}, []);
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
|
loading();
|
||||||
const profile = await getProfile();
|
const profile = await getProfile();
|
||||||
setProfile(profile?.data?.data);
|
setProfile(profile?.data?.data);
|
||||||
|
close();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="w-full lg:w-[60%] p-5">
|
<div className="w-full lg:w-[60%] p-3">
|
||||||
<div className="flex flex-col bg-gray-50 shadow-md dark:bg-stone-900 text-black dark:text-white rounded-md p-5">
|
<div className="flex flex-col bg-gray-50 shadow-md dark:bg-stone-900 text-black dark:text-white rounded-md p-5">
|
||||||
<Tabs aria-label="Tabs radius" radius="sm">
|
<Tabs aria-label="Tabs radius" radius="sm">
|
||||||
<Tab key="profile" title="Profile">
|
<Tab key="profile" title="Profile">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import Login from "@/components/form/login";
|
import Login from "@/components/form/login";
|
||||||
import QudoLogin from "@/components/form/qudo-login";
|
import QudoLogin from "@/components/form/qudo-login";
|
||||||
|
|
@ -6,23 +6,23 @@ import Cookies from "js-cookie";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function AuthPage() {
|
export default function AuthPage() {
|
||||||
|
// const isAuthenticated = Cookies.get("is_authenticated") || "false";
|
||||||
|
|
||||||
const isAuthenticated = Cookies.get("is_authenticated") || "false";
|
// console.log("isAuthenticated : ", isAuthenticated);
|
||||||
|
|
||||||
console.log("isAuthenticated : ", isAuthenticated);
|
// const [hasMounted, setHasMounted] = useState(false);
|
||||||
|
|
||||||
const [hasMounted, setHasMounted] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
setHasMounted(true);
|
// setHasMounted(true);
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
// Render
|
// // Render
|
||||||
if (!hasMounted) return null;
|
// if (!hasMounted) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isAuthenticated == "true" ?
|
// isAuthenticated == "true" ?
|
||||||
<Login /> :
|
<Login />
|
||||||
<QudoLogin />
|
// :
|
||||||
|
// <QudoLogin />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { fontSans } from "@/config/fonts";
|
import { fontSans } from "@/config/fonts";
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from "@/config/site";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
|
|
||||||
import "@/styles/globals.css";
|
import "@/styles/globals.css";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
|
@ -10,6 +12,7 @@ import LoadScript from "@/utils/global";
|
||||||
import { NextIntlClientProvider } from "next-intl";
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
import { useEffect, useState, type ReactNode } from "react";
|
import { useEffect, useState, type ReactNode } from "react";
|
||||||
import storedLanguage from "@/store/language-store";
|
import storedLanguage from "@/store/language-store";
|
||||||
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
// export const metadata: Metadata = {
|
// export const metadata: Metadata = {
|
||||||
// title: {
|
// title: {
|
||||||
|
|
@ -63,10 +66,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
||||||
<LoadScript />
|
<LoadScript />
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
className={clsx(
|
className={clsx("bg-background font-sans antialiased", inter.className)}
|
||||||
"bg-background font-sans antialiased",
|
|
||||||
fontSans.variable
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<NextIntlClientProvider locale={localeNow} messages={messages}>
|
<NextIntlClientProvider locale={localeNow} messages={messages}>
|
||||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Button } from "@nextui-org/button";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading } from "@/config/swal";
|
||||||
import { getProfile, postSignIn, resetPassword } from "@/service/master-user";
|
import { postSignIn, resetPassword } from "@/service/master-user";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,13 @@ import {
|
||||||
} from "@/service/article";
|
} from "@/service/article";
|
||||||
import ReactSelect from "react-select";
|
import ReactSelect from "react-select";
|
||||||
import makeAnimated from "react-select/animated";
|
import makeAnimated from "react-select/animated";
|
||||||
import { Checkbox, Chip } from "@nextui-org/react";
|
import {
|
||||||
|
Checkbox,
|
||||||
|
Chip,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
SelectSection,
|
||||||
|
} from "@nextui-org/react";
|
||||||
import GenerateSingleArticleForm from "./generate-ai-single-form";
|
import GenerateSingleArticleForm from "./generate-ai-single-form";
|
||||||
import { htmlToString } from "@/utils/global";
|
import { htmlToString } from "@/utils/global";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
|
@ -32,6 +38,7 @@ import {
|
||||||
saveManualContext,
|
saveManualContext,
|
||||||
updateManualArticle,
|
updateManualArticle,
|
||||||
} from "@/service/generate-article";
|
} from "@/service/generate-article";
|
||||||
|
import GenerateContentRewriteForm from "./generate-ai-content-rewrite-form";
|
||||||
|
|
||||||
const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -98,7 +105,9 @@ export default function CreateArticleForm() {
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [thumbnailValidation, setThumbnailValidation] = useState("");
|
const [thumbnailValidation, setThumbnailValidation] = useState("");
|
||||||
|
const [filesValidation, setFileValidation] = useState("");
|
||||||
const [diseData, setDiseData] = useState<DiseData>();
|
const [diseData, setDiseData] = useState<DiseData>();
|
||||||
|
const [selectedWritingType, setSelectedWritingType] = useState("single");
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
onDrop: (acceptedFiles) => {
|
onDrop: (acceptedFiles) => {
|
||||||
|
|
@ -108,6 +117,9 @@ export default function CreateArticleForm() {
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
multiple: true,
|
multiple: true,
|
||||||
|
accept: {
|
||||||
|
"image/*": [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const formOptions = {
|
const formOptions = {
|
||||||
|
|
@ -151,10 +163,21 @@ export default function CreateArticleForm() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (values: z.infer<typeof createArticleSchema>) => {
|
const onSubmit = async (values: z.infer<typeof createArticleSchema>) => {
|
||||||
if (thumbnailImg.length < 1 && !selectedMainImage) {
|
if ((thumbnailImg.length < 1 && !selectedMainImage) || files.length < 1) {
|
||||||
setThumbnailValidation("Required");
|
if (files.length < 1) {
|
||||||
|
setFileValidation("Required");
|
||||||
|
} else {
|
||||||
|
setFileValidation("");
|
||||||
|
}
|
||||||
|
if (thumbnailImg.length < 1 && !selectedMainImage) {
|
||||||
|
setThumbnailValidation("Required");
|
||||||
|
} else {
|
||||||
|
setThumbnailValidation("");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setThumbnailValidation("");
|
setThumbnailValidation("");
|
||||||
|
setFileValidation("Required");
|
||||||
|
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Simpan Data",
|
title: "Simpan Data",
|
||||||
text: "",
|
text: "",
|
||||||
|
|
@ -410,10 +433,10 @@ export default function CreateArticleForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="flex flex-row gap-8 text-black"
|
className="flex flex-col lg:flex-row gap-8 text-black"
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
>
|
>
|
||||||
<div className="w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1">
|
<div className="w-full lg:w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1">
|
||||||
<p className="text-sm">Judul</p>
|
<p className="text-sm">Judul</p>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -452,7 +475,6 @@ export default function CreateArticleForm() {
|
||||||
placeholder=""
|
placeholder=""
|
||||||
label=""
|
label=""
|
||||||
value={value}
|
value={value}
|
||||||
isReadOnly
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
labelPlacement="outside"
|
labelPlacement="outside"
|
||||||
className="w-full "
|
className="w-full "
|
||||||
|
|
@ -475,15 +497,55 @@ export default function CreateArticleForm() {
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
||||||
{useAi && (
|
{useAi && (
|
||||||
<GenerateSingleArticleForm
|
<div className="flex flex-col gap-2">
|
||||||
content={(data) => {
|
<Select
|
||||||
setDiseData(data);
|
label="Writing Style"
|
||||||
setValue(
|
variant="bordered"
|
||||||
"description",
|
labelPlacement="outside"
|
||||||
data?.articleBody ? data?.articleBody : ""
|
placeholder=""
|
||||||
);
|
selectedKeys={[selectedWritingType]}
|
||||||
}}
|
onChange={(e) =>
|
||||||
/>
|
e.target.value !== ""
|
||||||
|
? setSelectedWritingType(e.target.value)
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
classNames={{
|
||||||
|
label: "!text-black",
|
||||||
|
value: "!text-black",
|
||||||
|
trigger: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectSection>
|
||||||
|
<SelectItem key="single">Single Article</SelectItem>
|
||||||
|
<SelectItem key="rewrite">Content Rewrite</SelectItem>
|
||||||
|
</SelectSection>
|
||||||
|
</Select>
|
||||||
|
{selectedWritingType === "single" ? (
|
||||||
|
<GenerateSingleArticleForm
|
||||||
|
content={(data) => {
|
||||||
|
setDiseData(data);
|
||||||
|
setValue(
|
||||||
|
"description",
|
||||||
|
data?.articleBody ? data?.articleBody : ""
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<GenerateContentRewriteForm
|
||||||
|
content={(data) => {
|
||||||
|
setDiseData(data);
|
||||||
|
setValue(
|
||||||
|
"description",
|
||||||
|
data?.articleBody ? data?.articleBody : ""
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="text-sm mt-3">Deskripsi</p>
|
<p className="text-sm mt-3">Deskripsi</p>
|
||||||
|
|
@ -526,8 +588,11 @@ export default function CreateArticleForm() {
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
{filesValidation !== "" && files.length < 1 && (
|
||||||
|
<p className="text-red-400 text-sm mb-3">Upload File Media</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[35%] flex flex-col gap-8">
|
<div className="w-full lg:w-[35%] flex flex-col gap-8">
|
||||||
<div className="h-fit bg-white rounded-lg p-8 flex flex-col gap-1">
|
<div className="h-fit bg-white rounded-lg p-8 flex flex-col gap-1">
|
||||||
<p className="text-sm">Thubmnail</p>
|
<p className="text-sm">Thubmnail</p>
|
||||||
|
|
||||||
|
|
@ -575,10 +640,13 @@ export default function CreateArticleForm() {
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
multiple
|
||||||
className="w-fit h-fit"
|
className="w-fit h-fit"
|
||||||
|
accept="image/*"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
{thumbnailValidation !== "" && (
|
{thumbnailValidation !== "" && (
|
||||||
<p className="text-red-400 text-sm mb-3">Thumbnail harus ada</p>
|
<p className="text-red-400 text-sm mb-3">
|
||||||
|
Upload thumbnail atau pilih dari File Media
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,9 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
multiple: true,
|
multiple: true,
|
||||||
|
accept: {
|
||||||
|
"image/*": [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const formOptions = {
|
const formOptions = {
|
||||||
|
|
@ -372,10 +375,10 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="flex flex-row gap-8 text-black"
|
className="flex flex-col lg:flex-row gap-8 text-black"
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
>
|
>
|
||||||
<div className="w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1">
|
<div className="w-full lg:w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1">
|
||||||
{isDetail && <GetSeoScore id={String(diseId)} />}
|
{isDetail && <GetSeoScore id={String(diseId)} />}
|
||||||
<p className="text-sm">Judul</p>
|
<p className="text-sm">Judul</p>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -416,7 +419,6 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
placeholder=""
|
placeholder=""
|
||||||
label=""
|
label=""
|
||||||
value={value}
|
value={value}
|
||||||
isReadOnly
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
labelPlacement="outside"
|
labelPlacement="outside"
|
||||||
className="w-full "
|
className="w-full "
|
||||||
|
|
@ -506,8 +508,11 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
detailfiles ? (
|
detailfiles ? (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<img
|
<Image
|
||||||
src={`http://38.47.180.165:8802${detailfiles[mainImage]?.file_url}`}
|
alt="main"
|
||||||
|
width={720}
|
||||||
|
height={480}
|
||||||
|
src={detailfiles[mainImage]?.file_url}
|
||||||
className="w-[75%] mx-auto"
|
className="w-[75%] mx-auto"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -518,8 +523,11 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
onClick={() => setMainImage(index)}
|
onClick={() => setMainImage(index)}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
<img
|
<Image
|
||||||
src={`http://38.47.180.165:8802${file.file_url}`}
|
width={480}
|
||||||
|
height={360}
|
||||||
|
alt={`image-${index}`}
|
||||||
|
src={file.file_url}
|
||||||
className="h-[100px] object-cover w-[150px]"
|
className="h-[100px] object-cover w-[150px]"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -531,63 +539,65 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{detailfiles?.map(
|
{detailfiles?.map((file: any, index: number) => (
|
||||||
(file: any, index: number) =>
|
<div
|
||||||
index > 0 && (
|
key={file?.file_name + index}
|
||||||
<div
|
className=" flex justify-between border px-3.5 py-3 rounded-md"
|
||||||
key={file?.file_name + index}
|
>
|
||||||
className=" flex justify-between border px-3.5 py-3 rounded-md"
|
<div className="flex gap-3 items-center">
|
||||||
>
|
<div className="file-preview">
|
||||||
<div className="flex gap-3 items-center">
|
<Image
|
||||||
<div className="file-preview">
|
width={480}
|
||||||
<img
|
height={360}
|
||||||
src={`http://38.47.180.165:8802${file?.file_url}`}
|
alt={`image-${index}`}
|
||||||
className="h-[50px] object-cover"
|
src={file?.file_url}
|
||||||
/>
|
className="h-[100px] object-cover w-[150px]"
|
||||||
</div>
|
/>
|
||||||
<div>
|
|
||||||
<div className=" text-sm text-card-foreground">
|
|
||||||
{file?.file_name}
|
|
||||||
</div>
|
|
||||||
<div className=" text-xs font-light text-muted-foreground">
|
|
||||||
{Math.round(file?.size / 100) / 10 > 1000 ? (
|
|
||||||
<>
|
|
||||||
{(Math.round(file?.size / 100) / 10000).toFixed(
|
|
||||||
1
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{(Math.round(file?.size / 100) / 10).toFixed(1)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{" kb"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className=" border-none rounded-full"
|
|
||||||
variant="bordered"
|
|
||||||
color="danger"
|
|
||||||
onClick={() => handleDeleteFile(file?.id)}
|
|
||||||
>
|
|
||||||
<TimesIcon />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
<div>
|
||||||
)}
|
<div className=" text-sm text-card-foreground">
|
||||||
|
{file?.file_name}
|
||||||
|
</div>
|
||||||
|
<div className=" text-xs font-light text-muted-foreground">
|
||||||
|
{Math.round(file?.size / 100) / 10 > 1000 ? (
|
||||||
|
<>{(Math.round(file?.size / 100) / 10000).toFixed(1)}</>
|
||||||
|
) : (
|
||||||
|
<>{(Math.round(file?.size / 100) / 10).toFixed(1)}</>
|
||||||
|
)}
|
||||||
|
{" kb"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className=" border-none rounded-full"
|
||||||
|
variant="bordered"
|
||||||
|
color="danger"
|
||||||
|
onClick={() => handleDeleteFile(file?.id)}
|
||||||
|
>
|
||||||
|
<TimesIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[35%] flex flex-col gap-8">
|
<div className="w-full lg:w-[35%] flex flex-col gap-8">
|
||||||
<div className="h-fit bg-white rounded-lg p-8 flex flex-col gap-1">
|
<div className="h-fit bg-white rounded-lg p-8 flex flex-col gap-1">
|
||||||
<p className="text-sm">Thubmnail</p>
|
<p className="text-sm">Thubmnail</p>
|
||||||
{isDetail ? (
|
{isDetail ? (
|
||||||
<img src={thumbnail} className="w-[30%]" alt="thumbnail" />
|
<Image
|
||||||
|
width={480}
|
||||||
|
height={360}
|
||||||
|
src={thumbnail}
|
||||||
|
className="w-[30%]"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
) : selectedMainImage && files.length >= selectedMainImage ? (
|
) : selectedMainImage && files.length >= selectedMainImage ? (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<img
|
<Image
|
||||||
|
width={480}
|
||||||
|
height={360}
|
||||||
src={URL.createObjectURL(files[selectedMainImage - 1])}
|
src={URL.createObjectURL(files[selectedMainImage - 1])}
|
||||||
className="w-[30%]"
|
className="w-[30%]"
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
|
|
@ -604,7 +614,13 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
) : thumbnail !== "" ? (
|
) : thumbnail !== "" ? (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<img src={thumbnail} className="w-[30%]" alt="thumbnail" />
|
<Image
|
||||||
|
width={480}
|
||||||
|
height={360}
|
||||||
|
src={thumbnail}
|
||||||
|
className="w-[30%]"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className=" border-none rounded-full"
|
className=" border-none rounded-full"
|
||||||
|
|
@ -618,7 +634,9 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
) : thumbnailImg.length > 0 ? (
|
) : thumbnailImg.length > 0 ? (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<img
|
<Image
|
||||||
|
width={480}
|
||||||
|
height={360}
|
||||||
src={URL.createObjectURL(thumbnailImg[0])}
|
src={URL.createObjectURL(thumbnailImg[0])}
|
||||||
className="w-[30%]"
|
className="w-[30%]"
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
|
|
@ -640,6 +658,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
multiple
|
||||||
className="w-fit h-fit"
|
className="w-fit h-fit"
|
||||||
|
accept="image/*"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
{thumbnailValidation !== "" && (
|
{thumbnailValidation !== "" && (
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,280 @@
|
||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
SelectSection,
|
||||||
|
} from "@nextui-org/react";
|
||||||
|
import { FormEvent, useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import * as z from "zod";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { close, error, loading } from "@/config/swal";
|
||||||
|
import {
|
||||||
|
generateDataArticle,
|
||||||
|
getDetailArticle,
|
||||||
|
getGenerateKeywords,
|
||||||
|
getGenerateRewriter,
|
||||||
|
getGenerateTitle,
|
||||||
|
} from "@/service/generate-article";
|
||||||
|
import { delay } from "@/utils/global";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const CustomEditor = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("@/components/editor/custom-editor");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const writingStyle = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "Friendly",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "Professional",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "Informational",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "Neutral",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "Witty",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const articleSize = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "News (300 - 900 words)",
|
||||||
|
value: "News",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Info (900 - 2000 words)",
|
||||||
|
value: "Info",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "Detail (2000 - 5000 words)",
|
||||||
|
value: "Detail",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface DiseData {
|
||||||
|
id: number;
|
||||||
|
articleBody: string;
|
||||||
|
title: string;
|
||||||
|
metaTitle: string;
|
||||||
|
description: string;
|
||||||
|
metaDescription: string;
|
||||||
|
mainKeyword: string;
|
||||||
|
additionalKeywords: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GenerateContentRewriteForm(props: {
|
||||||
|
content: (data: DiseData) => void;
|
||||||
|
}) {
|
||||||
|
const [selectedWritingSyle, setSelectedWritingStyle] =
|
||||||
|
useState("Informational");
|
||||||
|
const [selectedArticleSize, setSelectedArticleSize] = useState("News");
|
||||||
|
const [selectedLanguage, setSelectedLanguage] = useState("id");
|
||||||
|
const [mainKeyword, setMainKeyword] = useState("");
|
||||||
|
const [articleIds, setArticleIds] = useState<number[]>([]);
|
||||||
|
const [selectedId, setSelectedId] = useState<number>();
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
loading();
|
||||||
|
const request = {
|
||||||
|
advConfig: "",
|
||||||
|
context: mainKeyword,
|
||||||
|
style: selectedWritingSyle,
|
||||||
|
sentiment: "Informational",
|
||||||
|
urlContext: null,
|
||||||
|
contextType: "article",
|
||||||
|
lang: selectedLanguage,
|
||||||
|
createdBy: "123123",
|
||||||
|
clientId: "humasClientIdtest",
|
||||||
|
};
|
||||||
|
const res = await getGenerateRewriter(request);
|
||||||
|
close();
|
||||||
|
if (res?.error) {
|
||||||
|
error("Error");
|
||||||
|
}
|
||||||
|
setArticleIds([...articleIds, res?.data?.data?.id]);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getArticleDetail();
|
||||||
|
}, [selectedId]);
|
||||||
|
|
||||||
|
const checkArticleStatus = async (data: string | null) => {
|
||||||
|
if (data === null) {
|
||||||
|
delay(7000).then(() => {
|
||||||
|
getArticleDetail();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getArticleDetail = async () => {
|
||||||
|
if (selectedId) {
|
||||||
|
const res = await getDetailArticle(selectedId);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
checkArticleStatus(data?.articleBody);
|
||||||
|
if (data?.articleBody !== null) {
|
||||||
|
setIsLoading(false);
|
||||||
|
props.content(data);
|
||||||
|
} else {
|
||||||
|
setIsLoading(true);
|
||||||
|
props.content({
|
||||||
|
id: data?.id,
|
||||||
|
articleBody: "",
|
||||||
|
title: "",
|
||||||
|
metaTitle: "",
|
||||||
|
description: "",
|
||||||
|
metaDescription: "",
|
||||||
|
additionalKeywords: "",
|
||||||
|
mainKeyword: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<form className="flex flex-col w-full mt-3">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 w-full">
|
||||||
|
<Select
|
||||||
|
label="Writing Style"
|
||||||
|
variant="bordered"
|
||||||
|
labelPlacement="outside"
|
||||||
|
placeholder=""
|
||||||
|
selectedKeys={[selectedWritingSyle]}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.value !== ""
|
||||||
|
? setSelectedWritingStyle(e.target.value)
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
classNames={{
|
||||||
|
label: "!text-black",
|
||||||
|
value: "!text-black",
|
||||||
|
trigger: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectSection>
|
||||||
|
{writingStyle.map((style) => (
|
||||||
|
<SelectItem key={style.name}>{style.name}</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectSection>
|
||||||
|
</Select>
|
||||||
|
<Select
|
||||||
|
label="Article Size"
|
||||||
|
variant="bordered"
|
||||||
|
labelPlacement="outside"
|
||||||
|
placeholder=""
|
||||||
|
selectedKeys={[selectedArticleSize]}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.value !== ""
|
||||||
|
? setSelectedArticleSize(e.target.value)
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
classNames={{
|
||||||
|
label: "!text-black",
|
||||||
|
value: "!text-black",
|
||||||
|
trigger: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectSection>
|
||||||
|
{articleSize.map((size) => (
|
||||||
|
<SelectItem key={size.value}>{size.name}</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectSection>
|
||||||
|
</Select>
|
||||||
|
<Select
|
||||||
|
label="Bahasa"
|
||||||
|
variant="bordered"
|
||||||
|
labelPlacement="outside"
|
||||||
|
placeholder=""
|
||||||
|
selectedKeys={[selectedLanguage]}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.value !== "" ? setSelectedLanguage(e.target.value) : ""
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
classNames={{
|
||||||
|
label: "!text-black",
|
||||||
|
value: "!text-black",
|
||||||
|
trigger: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectSection>
|
||||||
|
<SelectItem key="id">Indonesia</SelectItem>
|
||||||
|
<SelectItem key="en">English</SelectItem>
|
||||||
|
</SelectSection>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col mt-3">
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<p className="text-sm">Text</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-[78vw] lg:w-full">
|
||||||
|
<CustomEditor onChange={setMainKeyword} initialData={mainKeyword} />
|
||||||
|
</div>
|
||||||
|
{mainKeyword == "" && (
|
||||||
|
<p className="text-red-400 text-sm">Required</p>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
className="my-5 w-full py-5 text-xs md:text-base"
|
||||||
|
type="button"
|
||||||
|
onPress={onSubmit}
|
||||||
|
isDisabled={mainKeyword == ""}
|
||||||
|
>
|
||||||
|
Generate
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{articleIds.length > 0 && (
|
||||||
|
<div className="flex flex-row gap-1">
|
||||||
|
{articleIds?.map((id) => (
|
||||||
|
<Button
|
||||||
|
onPress={() => setSelectedId(id)}
|
||||||
|
key={id}
|
||||||
|
isLoading={isLoading && selectedId == id}
|
||||||
|
color={
|
||||||
|
selectedId == id && isLoading
|
||||||
|
? "warning"
|
||||||
|
: selectedId == id
|
||||||
|
? "success"
|
||||||
|
: "default"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p className={selectedId == id ? "text-white" : "text-black"}>
|
||||||
|
{id}
|
||||||
|
</p>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -388,7 +388,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
</div>
|
</div>
|
||||||
{articleIds.length > 0 && (
|
{articleIds.length > 0 && (
|
||||||
<div className="flex flex-row gap-1">
|
<div className="flex flex-row gap-1">
|
||||||
{articleIds?.map((id) => (
|
{articleIds?.map((id, index) => (
|
||||||
<Button
|
<Button
|
||||||
onPress={() => setSelectedId(id)}
|
onPress={() => setSelectedId(id)}
|
||||||
key={id}
|
key={id}
|
||||||
|
|
@ -402,7 +402,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<p className={selectedId == id ? "text-white" : "text-black"}>
|
<p className={selectedId == id ? "text-white" : "text-black"}>
|
||||||
{id}
|
Article {index}
|
||||||
</p>
|
</p>
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { checkUsernames, getProfile, postSignIn } from "@/service/master-user";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { saveActivity } from "@/service/activity-log";
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -53,6 +54,14 @@ export default function Login() {
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
});
|
});
|
||||||
const profile = await getProfile(access_token);
|
const profile = await getProfile(access_token);
|
||||||
|
const resActivity = await saveActivity(
|
||||||
|
{
|
||||||
|
activityTypeId: 1,
|
||||||
|
url: "https://kontenhumas.com/auth",
|
||||||
|
userId: profile?.data?.data?.id,
|
||||||
|
},
|
||||||
|
response?.data?.data?.id_token
|
||||||
|
);
|
||||||
console.log("PROFILE : ", profile?.data);
|
console.log("PROFILE : ", profile?.data);
|
||||||
Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
|
Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
|
|
@ -85,8 +94,8 @@ export default function Login() {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
close();
|
|
||||||
router.push("/admin/dashboard");
|
router.push("/admin/dashboard");
|
||||||
|
close();
|
||||||
Cookies.set("status", "login", {
|
Cookies.set("status", "login", {
|
||||||
expires: 1,
|
expires: 1,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ export default function NewCreateMagazineForm() {
|
||||||
<div className="flex gap-3 grow">
|
<div className="flex gap-3 grow">
|
||||||
<div className="file-preview">{renderPreview(file)}</div>
|
<div className="file-preview">{renderPreview(file)}</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1 grow">
|
<div className="flex flex-col gap-1 grow w-[45vw] md:w-auto">
|
||||||
<p className="text-sm font-semibold">Nama File</p>
|
<p className="text-sm font-semibold">Nama File</p>
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<p className=" text-sm text-card-foreground">{file.name}</p>
|
<p className=" text-sm text-card-foreground">{file.name}</p>
|
||||||
|
|
@ -389,7 +389,9 @@ export default function NewCreateMagazineForm() {
|
||||||
|
|
||||||
{thumbnailImg.length > 0 ? (
|
{thumbnailImg.length > 0 ? (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<img
|
<Image
|
||||||
|
width={720}
|
||||||
|
height={480}
|
||||||
src={URL.createObjectURL(thumbnailImg[0])}
|
src={URL.createObjectURL(thumbnailImg[0])}
|
||||||
className="w-[30%]"
|
className="w-[30%]"
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
|
|
@ -409,7 +411,7 @@ export default function NewCreateMagazineForm() {
|
||||||
<input
|
<input
|
||||||
id="file-upload"
|
id="file-upload"
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
accept="image/*"
|
||||||
className="w-fit h-fit"
|
className="w-fit h-fit"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
|
|
@ -446,7 +448,9 @@ export default function NewCreateMagazineForm() {
|
||||||
</div>
|
</div>
|
||||||
{files.length ? (
|
{files.length ? (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="grid grid-cols-2 gap-2">{fileList}</div>
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||||
|
{fileList}
|
||||||
|
</div>
|
||||||
{files.length > 1 && (
|
{files.length > 1 && (
|
||||||
<div className=" flex justify-between gap-2">
|
<div className=" flex justify-between gap-2">
|
||||||
<Button onPress={() => setFiles([])} size="sm">
|
<Button onPress={() => setFiles([])} size="sm">
|
||||||
|
|
|
||||||
|
|
@ -156,12 +156,8 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
setValue("title", data?.title);
|
setValue("title", data?.title);
|
||||||
setValue("description", data?.description);
|
setValue("description", data?.description);
|
||||||
if (res?.data?.data?.thumbnailPath) {
|
if (res?.data?.data?.thumbnailUrl) {
|
||||||
setPrevThumbnail(
|
setPrevThumbnail(res?.data?.data?.thumbnailUrl);
|
||||||
res?.data?.data?.thumbnailPath?.split("/")[
|
|
||||||
res?.data?.data?.thumbnailPath?.split("/").length - 1
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDetailFiles(data?.files);
|
setDetailFiles(data?.files);
|
||||||
|
|
@ -303,7 +299,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
<div className="flex gap-3 grow">
|
<div className="flex gap-3 grow">
|
||||||
<div className="file-preview">{renderPreview(file)}</div>
|
<div className="file-preview">{renderPreview(file)}</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1 grow">
|
<div className="flex flex-col gap-1 grow w-[45vw] md:w-auto">
|
||||||
<p className="text-sm font-semibold">Nama File</p>
|
<p className="text-sm font-semibold">Nama File</p>
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<p className=" text-sm text-card-foreground">{file.name}</p>
|
<p className=" text-sm text-card-foreground">{file.name}</p>
|
||||||
|
|
@ -477,8 +473,10 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
|
|
||||||
{prevThumbnail !== "" ? (
|
{prevThumbnail !== "" ? (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<img
|
<Image
|
||||||
src={`http://38.47.180.165:8802/magazines/thumbnail/viewer/${prevThumbnail}`}
|
width={720}
|
||||||
|
height={480}
|
||||||
|
src={prevThumbnail}
|
||||||
className="w-[30%]"
|
className="w-[30%]"
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
/>
|
/>
|
||||||
|
|
@ -496,7 +494,9 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
) : thumbnailImg.length > 0 ? (
|
) : thumbnailImg.length > 0 ? (
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<img
|
<Image
|
||||||
|
width={720}
|
||||||
|
height={480}
|
||||||
src={URL.createObjectURL(thumbnailImg[0])}
|
src={URL.createObjectURL(thumbnailImg[0])}
|
||||||
className="w-[30%]"
|
className="w-[30%]"
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
|
|
@ -516,7 +516,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
<input
|
<input
|
||||||
id="file-upload"
|
id="file-upload"
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
accept="image/*"
|
||||||
className="w-fit h-fit"
|
className="w-fit h-fit"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
|
|
@ -557,7 +557,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||||
{files.length ? (
|
{files.length ? (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{fileList}
|
{fileList}
|
||||||
|
|
@ -574,14 +574,14 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
{detailfiles?.map((file: any, index: number) => (
|
{detailfiles?.map((file: any, index: number) => (
|
||||||
<div
|
<div
|
||||||
key={file.fileName + index}
|
key={file.fileName + index}
|
||||||
className=" flex justify-between border p-3 rounded-md"
|
className=" flex flex-row lg:justify-between border p-3 rounded-md grow"
|
||||||
>
|
>
|
||||||
<div className="flex gap-3 grow">
|
<div className="flex gap-3 grow">
|
||||||
<div className="file-preview">
|
<div className="file-preview">
|
||||||
{renderPreview(file, file.fileName)}
|
{renderPreview(file, file.fileName)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1 grow">
|
<div className="flex flex-col grow gap-1 w-[45vw] md:w-auto">
|
||||||
<p className="text-sm font-semibold">Nama File</p>
|
<p className="text-sm font-semibold">Nama File</p>
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<p className=" text-sm text-card-foreground">
|
<p className=" text-sm text-card-foreground">
|
||||||
|
|
@ -644,7 +644,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className=" border-none rounded-full"
|
className=" border-none rounded-full m-0 p-0"
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
color="danger"
|
color="danger"
|
||||||
onClick={() => handleDeleteFile(file?.id)}
|
onClick={() => handleDeleteFile(file?.id)}
|
||||||
|
|
@ -657,7 +657,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
{isDetail && (
|
{isDetail && (
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||||
{detailfiles?.map((file: any, index: number) => (
|
{detailfiles?.map((file: any, index: number) => (
|
||||||
<div
|
<div
|
||||||
key={file.fileName + index}
|
key={file.fileName + index}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { error, loading, close } from "@/config/swal";
|
||||||
import { Button, Input } from "@nextui-org/react";
|
import { Button, Input } from "@nextui-org/react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { getProfile, postSignIn } from "@/service/master-user";
|
import { postSignIn } from "@/service/master-user";
|
||||||
|
|
||||||
const images = ["bg1.jpg", "bg2.jpg"];
|
const images = ["bg1.jpg", "bg2.jpg"];
|
||||||
|
|
||||||
|
|
@ -66,20 +66,6 @@ export default function QudoLogin() {
|
||||||
secure: true,
|
secure: true,
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
});
|
});
|
||||||
const profile = await getProfile();
|
|
||||||
console.log("PROFILE : ", profile?.data);
|
|
||||||
Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
|
|
||||||
expires: 1,
|
|
||||||
});
|
|
||||||
Cookies.set("uie", profile?.data?.data?.id, {
|
|
||||||
expires: 1,
|
|
||||||
});
|
|
||||||
Cookies.set("ufne", profile?.data?.data?.fullname, {
|
|
||||||
expires: 1,
|
|
||||||
});
|
|
||||||
Cookies.set("username", profile?.data?.data?.username, {
|
|
||||||
expires: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
close();
|
close();
|
||||||
router.push("/");
|
router.push("/");
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,38 @@ export default function ProfileForm(props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="flex flex-col gap-3 " onSubmit={handleSubmit(onSubmit)}>
|
<form className="flex flex-col gap-3 " onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<p className="text-sm">Username</p>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="username"
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
placeholder=""
|
||||||
|
label=""
|
||||||
|
isReadOnly
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
labelPlacement="outside"
|
||||||
|
className="w-full "
|
||||||
|
classNames={{
|
||||||
|
inputWrapper: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
variant="bordered"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{errors?.username && (
|
||||||
|
<p className="text-red-400 text-sm mb-3">
|
||||||
|
{errors.username?.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<p className="text-sm">Nama Lengkap</p>
|
<p className="text-sm">Nama Lengkap</p>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -131,37 +163,6 @@ export default function ProfileForm(props: {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<p className="text-sm">Username</p>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="username"
|
|
||||||
render={({ field: { onChange, value } }) => (
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
placeholder=""
|
|
||||||
label=""
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
labelPlacement="outside"
|
|
||||||
className="w-full "
|
|
||||||
classNames={{
|
|
||||||
inputWrapper: [
|
|
||||||
"border-1 rounded-lg",
|
|
||||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
variant="bordered"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{errors?.username && (
|
|
||||||
<p className="text-red-400 text-sm mb-3">
|
|
||||||
{errors.username?.message}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<p className="text-sm">Email</p>
|
<p className="text-sm">Email</p>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,21 +1,76 @@
|
||||||
|
"use client";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import React from "react";
|
import Image from "next/image";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { ChevronLeftIcon, ChevronRightIcon } from "../icons";
|
||||||
|
|
||||||
|
const images = [
|
||||||
|
"/landing-1.jpg",
|
||||||
|
"/landing-2.jpg",
|
||||||
|
"/landing-3.jpg",
|
||||||
|
"/landing-4.jpg",
|
||||||
|
];
|
||||||
|
|
||||||
export default function BannerHumas() {
|
export default function BannerHumas() {
|
||||||
const t = useTranslations("Banner");
|
const t = useTranslations("Banner");
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const [resetTimer, setResetTimer] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
|
||||||
|
}, 25000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [resetTimer]);
|
||||||
|
const nextImage = () => {
|
||||||
|
setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
|
||||||
|
setResetTimer((prev) => prev + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevImage = () => {
|
||||||
|
setCurrentIndex(
|
||||||
|
(prevIndex) => (prevIndex - 1 + images.length) % images.length
|
||||||
|
);
|
||||||
|
setResetTimer((prev) => prev + 1);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-fit relative text-white lg:mt-[-130px]">
|
<div className="h-fit relative text-gray-300 dark:text-white lg:mt-[-130px] overflow-hidden">
|
||||||
<img
|
<div
|
||||||
src="headerbanner1.png"
|
className="flex w-full h-[78vh] lg:h-[95vh] transition-transform duration-700 ease-in-out"
|
||||||
alt="humasbanner"
|
style={{ transform: `translateX(-${currentIndex * 100}%)` }}
|
||||||
className="w-full relative h-[78vh] lg:h-[95vh] object-cover"
|
>
|
||||||
/>
|
{images.map((img, index) => (
|
||||||
|
<div key={index} className="w-full shrink-0">
|
||||||
|
<Image
|
||||||
|
src={img}
|
||||||
|
width={1440}
|
||||||
|
height={1080}
|
||||||
|
alt={`humasbanner-${index}`}
|
||||||
|
className="w-full h-full object-cover opacity-[100] dark:opacity-70"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<div className="absolute lg:mt-[50px] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center w-full lg:w-[75%]">
|
<div className="absolute lg:mt-[50px] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center w-full lg:w-[75%]">
|
||||||
<p className="text-[30px] lg:text-[42px] lg:leading-10 font-bold pb-1 md:pb-5">
|
<p className="text-[30px] lg:text-[42px] lg:leading-10 font-bold pb-1 md:pb-5">
|
||||||
{t("jumbotron")}
|
{t("jumbotron")}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs md:text-medium">{`"${t("phrase")}"`}</p>
|
<p className="text-xs md:text-medium">{`"${t("phrase")}"`}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={prevImage}
|
||||||
|
className="absolute left-4 top-1/2 transform -translate-y-1/2 bg-black/50 text-white px-2 py-2 rounded-full hover:bg-black/70 transition"
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={nextImage}
|
||||||
|
className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-black/50 text-white px-2 py-2 rounded-full hover:bg-black/70 transition"
|
||||||
|
>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ export default function BodyLayout() {
|
||||||
<>
|
<>
|
||||||
<div className="lg:flex bg-white text-black p-2 lg:p-8 gap-9">
|
<div className="lg:flex bg-white text-black p-2 lg:p-8 gap-9">
|
||||||
<div className="lg:w-[75%] space-y-7">
|
<div className="lg:w-[75%] space-y-7">
|
||||||
{/* <MedolUpdate /> */}
|
<CategorySatker />
|
||||||
{/* <CategorySatker /> */}
|
|
||||||
<RegionalNews />
|
<RegionalNews />
|
||||||
|
<MedolUpdate />
|
||||||
<MediaSocial />
|
<MediaSocial />
|
||||||
<ENewsPolri />
|
<ENewsPolri />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,12 @@ export default function CategorySatker() {
|
||||||
title: "Itwasum",
|
title: "Itwasum",
|
||||||
path: "/news/itwasum",
|
path: "/news/itwasum",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
img: "/assets/satker2/stik-ptik.svg",
|
||||||
|
title: "STIK-PTIK",
|
||||||
|
path: "/news/stik-ptik",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const SatkerAll = [
|
const SatkerAll = [
|
||||||
|
|
@ -298,18 +304,26 @@ export default function CategorySatker() {
|
||||||
// };
|
// };
|
||||||
// }, [list]);
|
// }, [list]);
|
||||||
|
|
||||||
|
const changeNameToSlug = (name: string) => {
|
||||||
|
const cleaned = name.trim().toLowerCase();
|
||||||
|
return cleaned.replace(/\s+/g, "-");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-center bg-[#DD8306] rounded-none md:rounded-lg h-auto lg:h-[338px] space-y-0 py-4 md:space-y-7 flex flex-col justify-center">
|
<div className="text-center bg-[#DD8306] rounded-none md:rounded-lg h-auto lg:h-[338px] space-y-0 py-4 md:space-y-7 flex flex-col justify-center">
|
||||||
<div className="text-white font-bold text-2xl underline underline-offset-4 decoration-red-600">
|
<div className="text-xl text-white w-full justify-center flex">
|
||||||
{t("kategoriSatker")}
|
<p className="border-b-3 border-[#C3170F] py-2 w-fit">
|
||||||
|
{" "}
|
||||||
|
{t("kategoriSatker")}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-around">
|
<div className="flex items-center justify-around">
|
||||||
<div>
|
<div className="gap-2 md:gap-4 lg:gap-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6">
|
||||||
<ChevronLeftWhite />
|
|
||||||
</div>
|
|
||||||
<div className="gap-2 md:gap-4 lg:gap-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5">
|
|
||||||
{list.map((item: any, index: any) => (
|
{list.map((item: any, index: any) => (
|
||||||
<Link href={item.path} key={item.path}>
|
<Link
|
||||||
|
href={`/news/all?satker=${changeNameToSlug(item.title)}`}
|
||||||
|
key={item.path}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="w-[157px] h-[140px] flex flex-col items-center justify-evenly "
|
className="w-[157px] h-[140px] flex flex-col items-center justify-evenly "
|
||||||
|
|
@ -320,15 +334,13 @@ export default function CategorySatker() {
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<ChevronRightWhite />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
onPress={onOpen}
|
onPress={onOpen}
|
||||||
className="bg-white text-[#DD8306] font-bold"
|
className="border-red-700 text-white w-fit"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
variant="bordered"
|
||||||
>
|
>
|
||||||
{t("lebihBanyak")}
|
{t("lebihBanyak")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -346,8 +358,12 @@ export default function CategorySatker() {
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
{(onClose) => (
|
{(onClose) => (
|
||||||
<>
|
<>
|
||||||
<ModalHeader className="flex flex-col text-[#DD8306] items-center min-h text-3xl font-semibold">
|
<ModalHeader className="flex flex-col text-black items-center min-h text-3xl font-semibold">
|
||||||
{t("kategoriSatker")}
|
<div className="text-xl text-black w-full justify-center flex">
|
||||||
|
<p className="border-b-3 border-[#C3170F] py-2 w-fit">
|
||||||
|
{t("kategoriSatker")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody className="flex flex-row flex-wrap justify-center text-center">
|
<ModalBody className="flex flex-row flex-wrap justify-center text-center">
|
||||||
{SatkerAll.map((item: any, index: any) => (
|
{SatkerAll.map((item: any, index: any) => (
|
||||||
|
|
@ -355,21 +371,31 @@ export default function CategorySatker() {
|
||||||
key={index.id}
|
key={index.id}
|
||||||
className="w-[140px] h-[115px] flex flex-col items-center justify-center rounded-lg shadow-sm"
|
className="w-[140px] h-[115px] flex flex-col items-center justify-center rounded-lg shadow-sm"
|
||||||
>
|
>
|
||||||
<Link href={item.path}>
|
<Link
|
||||||
|
href={`/news/all?satker=${changeNameToSlug(
|
||||||
|
item.title
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
<Image
|
<Image
|
||||||
radius="lg"
|
radius="lg"
|
||||||
className="h-[59px] "
|
className="h-[59px] "
|
||||||
src={item.img}
|
src={item.img}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-xs font-bold text-[#DD8306] pt-2">
|
<p className="text-xs font-bold text-black pt-2">
|
||||||
{item.title}
|
{item.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="danger" variant="light" onPress={onClose}>
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onPress={onClose}
|
||||||
|
className="text-danger"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
{t("tutup")}
|
{t("tutup")}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,14 @@ export default function ENewsPolri() {
|
||||||
<SwiperSlide key={newsItem.id}>
|
<SwiperSlide key={newsItem.id}>
|
||||||
<Card isFooterBlurred radius="lg" className="border-none">
|
<Card isFooterBlurred radius="lg" className="border-none">
|
||||||
<img
|
<img
|
||||||
|
// width={720}
|
||||||
|
// height={480}
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
src={newsItem?.thumbnailUrl ? "" : "/no-image.jpg"}
|
src={
|
||||||
|
newsItem?.thumbnailUrl
|
||||||
|
? newsItem?.thumbnailUrl
|
||||||
|
: "no-image.jpg"
|
||||||
|
}
|
||||||
className="!h-[25vh] object-cover rounded-none"
|
className="!h-[25vh] object-cover rounded-none"
|
||||||
/>
|
/>
|
||||||
<CardFooter className="before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 md:absolute bottom-1 shadow-small z-10">
|
<CardFooter className="before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 md:absolute bottom-1 shadow-small z-10">
|
||||||
|
|
@ -81,7 +87,6 @@ export default function ENewsPolri() {
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex flex-row gap-1">
|
<div className="flex flex-row gap-1">
|
||||||
{" "}
|
|
||||||
<p className="py-[2px] text-left text-xs">
|
<p className="py-[2px] text-left text-xs">
|
||||||
{convertDateFormat(newsItem.createdAt)} WIB
|
{convertDateFormat(newsItem.createdAt)} WIB
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,15 @@ export default function HeaderNews() {
|
||||||
{article?.map((newsItem: any) => (
|
{article?.map((newsItem: any) => (
|
||||||
<SwiperSlide key={newsItem.id}>
|
<SwiperSlide key={newsItem.id}>
|
||||||
<Card isFooterBlurred radius="lg" className="border-none">
|
<Card isFooterBlurred radius="lg" className="border-none">
|
||||||
<img
|
<Image
|
||||||
|
width={1920}
|
||||||
|
height={1080}
|
||||||
alt="headernews"
|
alt="headernews"
|
||||||
src={newsItem.thumbnailUrl}
|
src={
|
||||||
|
newsItem?.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: newsItem?.thumbnailUrl
|
||||||
|
}
|
||||||
className="h-[25vh] object-cover"
|
className="h-[25vh] object-cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -111,10 +117,16 @@ export default function HeaderNews() {
|
||||||
className="text-xs text-left m-2 p-2 dark:bg-[#1E1616] bg-white rounded-md flex flex-row gap-2"
|
className="text-xs text-left m-2 p-2 dark:bg-[#1E1616] bg-white rounded-md flex flex-row gap-2"
|
||||||
key={data.id}
|
key={data.id}
|
||||||
>
|
>
|
||||||
<img
|
<Image
|
||||||
|
height={480}
|
||||||
|
width={480}
|
||||||
alt="headernews"
|
alt="headernews"
|
||||||
src={data.thumbnailUrl}
|
src={
|
||||||
className="object-cover w-[18%] rounded-md"
|
data?.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: data?.thumbnailUrl
|
||||||
|
}
|
||||||
|
className="object-cover w-[60px] h-[60px] rounded-md"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
|
|
@ -129,7 +141,7 @@ export default function HeaderNews() {
|
||||||
key={data?.id}
|
key={data?.id}
|
||||||
className="hidden lg:block"
|
className="hidden lg:block"
|
||||||
>
|
>
|
||||||
{data.title}{" "}
|
{textEllipsis(data.title, 66)}
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex flex-row gap-2 text-[10px]">
|
<div className="flex flex-row gap-2 text-[10px]">
|
||||||
<p className="py-[2px]">
|
<p className="py-[2px]">
|
||||||
|
|
@ -191,10 +203,16 @@ export default function HeaderNews() {
|
||||||
radius="lg"
|
radius="lg"
|
||||||
className="border-none h-[67vh] shadow-none"
|
className="border-none h-[67vh] shadow-none"
|
||||||
>
|
>
|
||||||
<img
|
<Image
|
||||||
alt="headernews"
|
alt="headernews"
|
||||||
src={newsItem.thumbnailUrl}
|
width={1440}
|
||||||
className="w-full h-[67vh] object-cover rounded-lg"
|
height={1080}
|
||||||
|
src={
|
||||||
|
newsItem?.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: newsItem?.thumbnailUrl
|
||||||
|
}
|
||||||
|
className="w-full !h-[67vh] object-cover rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CardFooter className="mb-1 max-h-[20vh] before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 md:absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
<CardFooter className="mb-1 max-h-[20vh] before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 md:absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
||||||
|
|
@ -202,7 +220,7 @@ export default function HeaderNews() {
|
||||||
<Link
|
<Link
|
||||||
href={`news/detail/${newsItem.id}-${newsItem?.slug}`}
|
href={`news/detail/${newsItem.id}-${newsItem?.slug}`}
|
||||||
>
|
>
|
||||||
<p className="text-left font-semibold text-2xl">
|
<p className="text-left font-semibold text-lg lg:text-2xl">
|
||||||
{newsItem.title}
|
{newsItem.title}
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -1,111 +1,140 @@
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
|
FacebookLandingIcon,
|
||||||
FbIcon,
|
FbIcon,
|
||||||
IgIcon,
|
IgIcon,
|
||||||
|
InstagramLandingIcon,
|
||||||
|
TiktokLandingIcon,
|
||||||
TtIcon,
|
TtIcon,
|
||||||
TwitterIcon,
|
TwitterIcon,
|
||||||
|
XLandingIcon,
|
||||||
|
YoutubeLandingIcon,
|
||||||
YtIcon,
|
YtIcon,
|
||||||
} from "../icons";
|
} from "../icons";
|
||||||
import TwitterWidget from "../ui/social-media/twitter";
|
import TwitterWidget from "../ui/social-media/twitter";
|
||||||
import InstagramWidget from "../ui/social-media/instagram";
|
import InstagramWidget from "../ui/social-media/instagram";
|
||||||
import FacebookWidget from "../ui/social-media/facebook";
|
import FacebookWidget from "../ui/social-media/facebook";
|
||||||
import YoutubeWidget from "../ui/social-media/youtube";
|
import YoutubeWidget from "../ui/social-media/youtube";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "@nextui-org/button";
|
||||||
|
|
||||||
export default function MediaSocial() {
|
export default function MediaSocial() {
|
||||||
// const [limitedData, setLimitedData] = useState<any>([]);
|
// const [limitedData, setLimitedData] = useState<any>([]);
|
||||||
|
const [selectedPlatform, setSelectedPlatform] = useState("x");
|
||||||
const dummyData = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
logo: "/logohumas.png",
|
|
||||||
division: "Divisi Humas Polri",
|
|
||||||
type: "/temp/offical.svg",
|
|
||||||
username: "@DivHumas_Polri",
|
|
||||||
followIcon: "/temp/iconX.svg",
|
|
||||||
description:
|
|
||||||
"Pada pembukaan KTT ke-43 ASEAN, Presiden RI, H. Joko Widodo menegaskan bahwa kesatuan ASEAN sampai saat ini masih terjaga dan terpelihara dengan baik.",
|
|
||||||
imageUrl: "/headernews.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
logo: "/logohumas.png",
|
|
||||||
division: "Divisi Humas Polri",
|
|
||||||
username: "@DivHumas_Polri",
|
|
||||||
type: "/temp/offical.svg",
|
|
||||||
followIcon: "/temp/iconX.svg",
|
|
||||||
description:
|
|
||||||
"Pada pembukaan KTT ke-43 ASEAN, Presiden RI, H. Joko Widodo menegaskan bahwa kesatuan ASEAN sampai saat ini masih terjaga dan terpelihara dengan baik.",
|
|
||||||
imageUrl: "/headernews.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
logo: "/logohumas.png",
|
|
||||||
division: "Divisi Humas Polri",
|
|
||||||
type: "/temp/offical.svg",
|
|
||||||
username: "@DivHumas_Polri",
|
|
||||||
followIcon: "/temp/iconX.svg",
|
|
||||||
description:
|
|
||||||
"Pada pembukaan KTT ke-43 ASEAN, Presiden RI, H. Joko Widodo menegaskan bahwa kesatuan ASEAN sampai saat ini masih terjaga dan terpelihara dengan baik.",
|
|
||||||
imageUrl: "/headernews.png",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-5 md:space-y-7 bg-white text-black rounded-lg ">
|
<div className="space-y-5 md:space-y-7 bg-white text-black rounded-lg ">
|
||||||
<p className="text-xl border-b-3 border-red-500 py-2 w-fit font-semibold">
|
<p className="text-xl border-b-3 border-red-500 py-2 w-fit font-semibold">
|
||||||
MediaSocial
|
MediaSocial
|
||||||
</p>
|
</p>
|
||||||
<div>
|
|
||||||
<div className="flex items-center justify-between pb-3">
|
<div className="flex flex-row gap-3">
|
||||||
|
<Button
|
||||||
|
className="min-w-[60px]"
|
||||||
|
variant={selectedPlatform === "x" ? "solid" : "flat"}
|
||||||
|
onClick={() => setSelectedPlatform("x")}
|
||||||
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<TwitterIcon color="#1DA1F2" />
|
<XLandingIcon size={16} />
|
||||||
<p className="pl-2 text-xl">Twitter</p>
|
<p
|
||||||
|
className={`pl-2 text-sm ${
|
||||||
|
selectedPlatform === "x"
|
||||||
|
? "font-semibold"
|
||||||
|
: "text-slate-600 dark:text-slate-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Button>
|
||||||
<div>
|
<Button
|
||||||
<TwitterWidget />
|
className="min-w-[60px]"
|
||||||
</div>
|
variant={selectedPlatform === "instagram" ? "solid" : "flat"}
|
||||||
|
onClick={() => setSelectedPlatform("instagram")}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<InstagramLandingIcon />
|
||||||
|
<p
|
||||||
|
className={`pl-2 text-sm ${
|
||||||
|
selectedPlatform === "instagram"
|
||||||
|
? "font-semibold"
|
||||||
|
: "text-slate-600 dark:text-slate-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Instagram
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="min-w-[60px]"
|
||||||
|
variant={selectedPlatform === "facebook" ? "solid" : "flat"}
|
||||||
|
onClick={() => setSelectedPlatform("facebook")}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FacebookLandingIcon />
|
||||||
|
<p
|
||||||
|
className={`pl-2 text-sm ${
|
||||||
|
selectedPlatform === "facebook"
|
||||||
|
? "font-semibold"
|
||||||
|
: "text-slate-600 dark:text-slate-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Facebook
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="min-w-[60px]"
|
||||||
|
variant={selectedPlatform === "tiktok" ? "solid" : "flat"}
|
||||||
|
onClick={() => setSelectedPlatform("tiktok")}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<TiktokLandingIcon />
|
||||||
|
<p
|
||||||
|
className={`pl-2 text-sm ${
|
||||||
|
selectedPlatform === "tiktok"
|
||||||
|
? "font-semibold"
|
||||||
|
: "text-slate-600 dark:text-slate-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Tiktok
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="min-w-[60px]"
|
||||||
|
variant={selectedPlatform === "youtube" ? "solid" : "flat"}
|
||||||
|
onClick={() => setSelectedPlatform("youtube")}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<YoutubeLandingIcon />
|
||||||
|
<p
|
||||||
|
className={`pl-2 text-sm ${
|
||||||
|
selectedPlatform === "youtube"
|
||||||
|
? "font-semibold"
|
||||||
|
: "text-slate-600 dark:text-slate-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Youtube
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={selectedPlatform === "x" ? "" : "hidden"}>
|
||||||
<div className="flex items-center justify-between pb-3">
|
<TwitterWidget />
|
||||||
<div className="flex items-center">
|
|
||||||
<IgIcon size={40} color="#1DA1F2" />
|
|
||||||
<p className="pl-2 text-xl">Instagram</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="">
|
|
||||||
<InstagramWidget />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={selectedPlatform === "instagram" ? "" : "hidden"}>
|
||||||
<div className="flex items-center justify-between pb-3">
|
<InstagramWidget />
|
||||||
<div className="flex items-center">
|
</div>
|
||||||
<FbIcon color="#1DA1F2" />
|
<div className={selectedPlatform === "facebook" ? "" : "hidden"}>
|
||||||
<p className="pl-2 text-xl">Facebook</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<FacebookWidget />
|
<FacebookWidget />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={selectedPlatform === "tiktok" ? "" : "hidden"}>
|
||||||
<div className="flex items-center justify-between pb-3">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<TtIcon color="#1DA1F2" size={40} />
|
|
||||||
<p className="pl-2 text-xl">Tiktok</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<FacebookWidget />
|
<FacebookWidget />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={selectedPlatform === "youtube" ? "" : "hidden"}>
|
||||||
<div className="flex items-center justify-between pb-3">
|
<YoutubeWidget />
|
||||||
<div className="font-semibold flex items-center">
|
|
||||||
<YtIcon color="#1DA1F2" size={40} />
|
|
||||||
<p className="text-xl pl-2">Youtube</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className=" rounded-md ">
|
|
||||||
<YoutubeWidget />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,31 @@ export default function MedolUpdate() {
|
||||||
navigation={true}
|
navigation={true}
|
||||||
modules={[Navigation, Pagination]}
|
modules={[Navigation, Pagination]}
|
||||||
spaceBetween={40}
|
spaceBetween={40}
|
||||||
slidesPerView={2}
|
slidesPerView={1}
|
||||||
|
breakpoints={{
|
||||||
|
// When the window width is less than 640px
|
||||||
|
720: {
|
||||||
|
slidesPerView: 2, // Set slidesPerView to 1 on mobile
|
||||||
|
},
|
||||||
|
}}
|
||||||
pagination={true}
|
pagination={true}
|
||||||
className="mySwiper"
|
className="mySwiper"
|
||||||
|
onSwiper={(swiper) => {
|
||||||
|
swiper.navigation.nextEl?.classList.add(
|
||||||
|
"bg-white/70",
|
||||||
|
"!text-black",
|
||||||
|
"rounded-full",
|
||||||
|
"!w-[40px]",
|
||||||
|
"!h-[40px]"
|
||||||
|
);
|
||||||
|
swiper.navigation.prevEl?.classList.add(
|
||||||
|
"bg-white/70",
|
||||||
|
"!text-black",
|
||||||
|
"rounded-full",
|
||||||
|
"!w-[40px]",
|
||||||
|
"!h-[40px]"
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{mediahubUpdate?.map((newsItem: any) => (
|
{mediahubUpdate?.map((newsItem: any) => (
|
||||||
<SwiperSlide key={newsItem.title}>
|
<SwiperSlide key={newsItem.title}>
|
||||||
|
|
@ -108,7 +130,7 @@ export default function MedolUpdate() {
|
||||||
radius="lg"
|
radius="lg"
|
||||||
width="300%"
|
width="300%"
|
||||||
alt="tes"
|
alt="tes"
|
||||||
className="object-cover h-[270px]"
|
className="object-cover !h-[30vh]"
|
||||||
src={newsItem.thumbnailLink}
|
src={newsItem.thumbnailLink}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
@ -141,9 +163,31 @@ export default function MedolUpdate() {
|
||||||
navigation={true}
|
navigation={true}
|
||||||
modules={[Navigation, Pagination]}
|
modules={[Navigation, Pagination]}
|
||||||
spaceBetween={40}
|
spaceBetween={40}
|
||||||
slidesPerView={2}
|
slidesPerView={1}
|
||||||
|
breakpoints={{
|
||||||
|
// When the window width is less than 640px
|
||||||
|
720: {
|
||||||
|
slidesPerView: 2, // Set slidesPerView to 1 on mobile
|
||||||
|
},
|
||||||
|
}}
|
||||||
pagination={true}
|
pagination={true}
|
||||||
className="mySwiper"
|
className="mySwiper"
|
||||||
|
onSwiper={(swiper) => {
|
||||||
|
swiper.navigation.nextEl?.classList.add(
|
||||||
|
"bg-white/70",
|
||||||
|
"!text-black",
|
||||||
|
"rounded-full",
|
||||||
|
"!w-[40px]",
|
||||||
|
"!h-[40px]"
|
||||||
|
);
|
||||||
|
swiper.navigation.prevEl?.classList.add(
|
||||||
|
"bg-white/70",
|
||||||
|
"!text-black",
|
||||||
|
"rounded-full",
|
||||||
|
"!w-[40px]",
|
||||||
|
"!h-[40px]"
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{tbnUpdate?.map((newsItem: any) => (
|
{tbnUpdate?.map((newsItem: any) => (
|
||||||
<SwiperSlide key={newsItem.title}>
|
<SwiperSlide key={newsItem.title}>
|
||||||
|
|
@ -158,7 +202,7 @@ export default function MedolUpdate() {
|
||||||
radius="lg"
|
radius="lg"
|
||||||
width="300%"
|
width="300%"
|
||||||
alt="tes"
|
alt="tes"
|
||||||
className="object-cover h-[270px]"
|
className="object-cover !h-[30vh]"
|
||||||
src={newsItem?.image}
|
src={newsItem?.image}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
@ -184,14 +228,36 @@ export default function MedolUpdate() {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
{/* <Tab key="inp" title="Indonesia Nasional Police Update">
|
<Tab key="inp" title="Indonesia Nasional Police Update">
|
||||||
<Swiper
|
<Swiper
|
||||||
navigation={true}
|
navigation={true}
|
||||||
modules={[Navigation, Pagination]}
|
modules={[Navigation, Pagination]}
|
||||||
spaceBetween={40}
|
spaceBetween={40}
|
||||||
slidesPerView={2}
|
slidesPerView={1}
|
||||||
|
breakpoints={{
|
||||||
|
// When the window width is less than 640px
|
||||||
|
720: {
|
||||||
|
slidesPerView: 2, // Set slidesPerView to 1 on mobile
|
||||||
|
},
|
||||||
|
}}
|
||||||
pagination={true}
|
pagination={true}
|
||||||
className="mySwiper"
|
className="mySwiper"
|
||||||
|
onSwiper={(swiper) => {
|
||||||
|
swiper.navigation.nextEl?.classList.add(
|
||||||
|
"bg-white/70",
|
||||||
|
"!text-black",
|
||||||
|
"rounded-full",
|
||||||
|
"!w-[40px]",
|
||||||
|
"!h-[40px]"
|
||||||
|
);
|
||||||
|
swiper.navigation.prevEl?.classList.add(
|
||||||
|
"bg-white/70",
|
||||||
|
"!text-black",
|
||||||
|
"rounded-full",
|
||||||
|
"!w-[40px]",
|
||||||
|
"!h-[40px]"
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{inpUpdate?.map((newsItem: any) => (
|
{inpUpdate?.map((newsItem: any) => (
|
||||||
<SwiperSlide key={newsItem?.id}>
|
<SwiperSlide key={newsItem?.id}>
|
||||||
|
|
@ -206,7 +272,7 @@ export default function MedolUpdate() {
|
||||||
radius="lg"
|
radius="lg"
|
||||||
width="300%"
|
width="300%"
|
||||||
alt="tes"
|
alt="tes"
|
||||||
className="object-cover h-[270px]"
|
className="object-cover !h-[30vh]"
|
||||||
src={newsItem.image}
|
src={newsItem.image}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
@ -231,7 +297,7 @@ export default function MedolUpdate() {
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</Tab> */}
|
</Tab>
|
||||||
<Tab key="polritv" title="Polri TV Update">
|
<Tab key="polritv" title="Polri TV Update">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="w-[40%] mx-auto">
|
<div className="w-[40%] mx-auto">
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ export default function NewsTicker() {
|
||||||
<div className="fixed bottom-0 z-50 flex flex-row h-[60px] gap-3 w-full justify-between dark:bg-stone-800 bg-gray-50">
|
<div className="fixed bottom-0 z-50 flex flex-row h-[60px] gap-3 w-full justify-between dark:bg-stone-800 bg-gray-50">
|
||||||
<div className="relative px-4 py-2 font-semibold text-xs lg:text-sm flex items-center bg-amber-500 text-white w-[30%] lg:w-[10%]">
|
<div className="relative px-4 py-2 font-semibold text-xs lg:text-sm flex items-center bg-amber-500 text-white w-[30%] lg:w-[10%]">
|
||||||
<span className="mr-2"></span> BREAKING NEWS
|
<span className="mr-2"></span> BREAKING NEWS
|
||||||
<div className="absolute right-0 top-0 h-full w-4 bg-amber-500 transform translate-x-full clip-path-triangle"></div>
|
<div className="absolute right-0 top-0 h-full w-4 bg-amber-500 transform translate-x-full clipPath-triangle"></div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`w-full px-5 py-1 flex flex-col gap-1 transition-transform duration-300 ${
|
className={`w-full px-5 py-1 flex flex-col gap-1 transition-transform duration-300 ${
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,12 @@ export default function RegionalNews() {
|
||||||
// };
|
// };
|
||||||
// }, [list]);
|
// }, [list]);
|
||||||
|
|
||||||
|
const changeNameToSlug = (name: string) => {
|
||||||
|
const cleaned = name.replace("Polda ", "").trim().toLowerCase();
|
||||||
|
const slug = cleaned.replace(/\s+/g, "-");
|
||||||
|
return slug;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-center rounded-md lg:rounded-lg h-auto lg:h-[338px] flex flex-col p-6 bg-[#DD8306]">
|
<div className="text-center rounded-md lg:rounded-lg h-auto lg:h-[338px] flex flex-col p-6 bg-[#DD8306]">
|
||||||
<div className="text-xl text-white w-full justify-center flex">
|
<div className="text-xl text-white w-full justify-center flex">
|
||||||
|
|
@ -312,7 +318,10 @@ export default function RegionalNews() {
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="gap-2 md:gap-4 lg:gap-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6">
|
<div className="gap-2 md:gap-4 lg:gap-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6">
|
||||||
{listPolda.map((item: any, index: any) => (
|
{listPolda.map((item: any, index: any) => (
|
||||||
<Link href={item.path} key={item.path}>
|
<Link
|
||||||
|
href={`/news/all?polda=${changeNameToSlug(item.title)}`}
|
||||||
|
key={item.path}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="w-[157px] h-[141px] flex flex-col items-center justify-evenly"
|
className="w-[157px] h-[141px] flex flex-col items-center justify-evenly"
|
||||||
|
|
@ -344,8 +353,8 @@ export default function RegionalNews() {
|
||||||
scrollBehavior={scrollBehavior}
|
scrollBehavior={scrollBehavior}
|
||||||
placement={modalPlacement}
|
placement={modalPlacement}
|
||||||
classNames={{
|
classNames={{
|
||||||
wrapper: "bg-[#DD8306]",
|
wrapper: "bg-white",
|
||||||
base: "bg-[#DD8306] min-h-full",
|
base: "bg-white min-h-full",
|
||||||
// body: "w-full bg-white",
|
// body: "w-full bg-white",
|
||||||
// footer: "bg-white"
|
// footer: "bg-white"
|
||||||
}}
|
}}
|
||||||
|
|
@ -353,8 +362,8 @@ export default function RegionalNews() {
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
{(onClose) => (
|
{(onClose) => (
|
||||||
<>
|
<>
|
||||||
<ModalHeader className="flex flex-col text-white items-center min-h mb- text-3xl font-semibold">
|
<ModalHeader className="flex flex-col text-black items-center min-h mb- text-3xl font-semibold">
|
||||||
<div className="text-xl text-white w-full justify-center flex">
|
<div className="text-xl text-black w-full justify-center flex">
|
||||||
<p className="border-b-3 border-[#C3170F] py-2 w-fit">
|
<p className="border-b-3 border-[#C3170F] py-2 w-fit">
|
||||||
{" "}
|
{" "}
|
||||||
{t("beritaWilayah")}
|
{t("beritaWilayah")}
|
||||||
|
|
@ -367,14 +376,16 @@ export default function RegionalNews() {
|
||||||
key={index.id}
|
key={index.id}
|
||||||
className="w-[140px] h-[115px] flex flex-col items-center justify-center rounded-lg shadow-sm"
|
className="w-[140px] h-[115px] flex flex-col items-center justify-center rounded-lg shadow-sm"
|
||||||
>
|
>
|
||||||
<Link href={item.path}>
|
<Link
|
||||||
|
href={`/news/all?polda=${changeNameToSlug(item.title)}`}
|
||||||
|
>
|
||||||
<div className="flex flex-col items-center ">
|
<div className="flex flex-col items-center ">
|
||||||
<Image
|
<Image
|
||||||
radius="lg"
|
radius="lg"
|
||||||
className="h-[59px]"
|
className="h-[59px]"
|
||||||
src={item.img}
|
src={item.img}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs font-bold text-white">
|
<p className="text-xs font-bold text-black">
|
||||||
{item.title}
|
{item.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -383,7 +394,11 @@ export default function RegionalNews() {
|
||||||
))}
|
))}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button variant="light" onPress={onClose}>
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onPress={onClose}
|
||||||
|
className="text-danger"
|
||||||
|
>
|
||||||
{t("tutup")}
|
{t("tutup")}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export default function SidebarNav() {
|
||||||
onClick={() => setSelectedTab("media")}
|
onClick={() => setSelectedTab("media")}
|
||||||
className={
|
className={
|
||||||
selectedTab === "media"
|
selectedTab === "media"
|
||||||
? "text-black dark:text-white border-b-3 border-red-400 cursor-pointer py-2"
|
? "text-black border-b-3 border-red-400 cursor-pointer py-2"
|
||||||
: "text-slate-300 cursor-pointer py-2"
|
: "text-slate-300 cursor-pointer py-2"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
@ -40,7 +40,7 @@ export default function SidebarNav() {
|
||||||
onClick={() => setSelectedTab("video")}
|
onClick={() => setSelectedTab("video")}
|
||||||
className={
|
className={
|
||||||
selectedTab === "video"
|
selectedTab === "video"
|
||||||
? "text-black dark:text-white border-b-3 border-red-400 cursor-pointer py-2"
|
? "text-black border-b-3 border-red-400 cursor-pointer py-2"
|
||||||
: "text-slate-300 cursor-pointer py-2"
|
: "text-slate-300 cursor-pointer py-2"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
@ -91,10 +91,10 @@ export default function SidebarNav() {
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="text-[15px] border-b-3 border-red-500 w-fit py-2">
|
{/* <p className="text-[15px] border-b-3 border-red-500 w-fit py-2">
|
||||||
Pelayanan Informasi Publik
|
Pelayanan Informasi Publik
|
||||||
</p>
|
</p>
|
||||||
<img src="pelayanan-informasi-publik.png" className="w-full" />
|
<img src="pelayanan-informasi-publik.png" className="w-full" /> */}
|
||||||
{/* <div className="text-xl font-semibold underline underline-offset-4 decoration-red-600 ">
|
{/* <div className="text-xl font-semibold underline underline-offset-4 decoration-red-600 ">
|
||||||
SERTIFIKAT ISO 9001:2015
|
SERTIFIKAT ISO 9001:2015
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -163,7 +163,7 @@ export default function SidebarNav() {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
</div> */}
|
||||||
{/* <div className="text-xl font-semibold underline underline-offset-4 decoration-red-600">
|
<div className="text-xl font-semibold underline underline-offset-4 decoration-red-600">
|
||||||
Channel Humas Polri
|
Channel Humas Polri
|
||||||
<div className="flex flex-col gap-3 m-10">
|
<div className="flex flex-col gap-3 m-10">
|
||||||
<Link
|
<Link
|
||||||
|
|
@ -226,9 +226,9 @@ export default function SidebarNav() {
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[15px] border-b-3 border-red-500 py-2 w-fit mx-1 lg:mx-5">
|
{/* <p className="text-[15px] border-b-3 border-red-500 py-2 w-fit mx-1 lg:mx-5">
|
||||||
{" "}
|
{" "}
|
||||||
Info Eksternal
|
Info Eksternal
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -321,7 +321,7 @@ export default function SidebarNav() {
|
||||||
Info dan Berita terbaru dari Komnas Perempuan Indonesia
|
Info dan Berita terbaru dari Komnas Perempuan Indonesia
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ import { useEffect, useState } from "react";
|
||||||
import Sidebar from "./sidebar/sidebar";
|
import Sidebar from "./sidebar/sidebar";
|
||||||
import { SidebarProvider } from "./sidebar/sidebar-context";
|
import { SidebarProvider } from "./sidebar/sidebar-context";
|
||||||
import { Breadcrumb } from "../ui/breadcrumb";
|
import { Breadcrumb } from "../ui/breadcrumb";
|
||||||
|
import SidebarMobile from "./sidebar/sidebar-mobile";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AdminLayout = ({ children }: Props) => {
|
export const AdminLayout = ({ children }: Props) => {
|
||||||
const [isOpen, setIsOpen] = useState(true);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const updateSidebarData = (newData: boolean) => {
|
const updateSidebarData = (newData: boolean) => {
|
||||||
setIsOpen(newData);
|
setIsOpen(newData);
|
||||||
};
|
};
|
||||||
|
|
@ -29,6 +30,10 @@ export const AdminLayout = ({ children }: Props) => {
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<div className="!h-screen flex items-center flex-row !overflow-y-hidden">
|
<div className="!h-screen flex items-center flex-row !overflow-y-hidden">
|
||||||
<Sidebar sidebarData={isOpen} updateSidebarData={updateSidebarData} />
|
<Sidebar sidebarData={isOpen} updateSidebarData={updateSidebarData} />
|
||||||
|
<SidebarMobile
|
||||||
|
sidebarData={isOpen}
|
||||||
|
updateSidebarData={updateSidebarData}
|
||||||
|
/>
|
||||||
<div className={`w-full h-full flex flex-col overflow-hidden`}>
|
<div className={`w-full h-full flex flex-col overflow-hidden`}>
|
||||||
<Breadcrumb />
|
<Breadcrumb />
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -68,15 +68,15 @@ export default function NavbarHumas(props: { size: string }) {
|
||||||
const language = storedLanguage((state) => state.locale);
|
const language = storedLanguage((state) => state.locale);
|
||||||
const setLanguage = storedLanguage((state) => state.setLocale);
|
const setLanguage = storedLanguage((state) => state.setLocale);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (!isAuthenticated) {
|
// if (!isAuthenticated) {
|
||||||
onLogout();
|
// onLogout();
|
||||||
}
|
// }
|
||||||
}, [token]);
|
// }, [token]);
|
||||||
|
|
||||||
const onLogout = () => {
|
const onLogout = () => {
|
||||||
Object.keys(Cookies.get()).forEach((cookieName) => {
|
Object.keys(Cookies.get()).forEach((cookieName) => {
|
||||||
Cookies.remove(cookieName);
|
Cookies.remove(cookieName, { path: "/" });
|
||||||
});
|
});
|
||||||
router.push("/auth");
|
router.push("/auth");
|
||||||
};
|
};
|
||||||
|
|
@ -154,7 +154,6 @@ export default function NavbarHumas(props: { size: string }) {
|
||||||
<Link href={"/"}>
|
<Link href={"/"}>
|
||||||
<img src="/logohumas.png" alt="logo" className="w-[95px]" />
|
<img src="/logohumas.png" alt="logo" className="w-[95px]" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="bg-white dark:bg-[#1F1A17] "
|
className="bg-white dark:bg-[#1F1A17] "
|
||||||
radius="none"
|
radius="none"
|
||||||
|
|
@ -394,7 +393,14 @@ export default function NavbarHumas(props: { size: string }) {
|
||||||
</div>
|
</div>
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>{" "}
|
||||||
|
<Link
|
||||||
|
href="https://eppid.polri.go.id/"
|
||||||
|
target="_blank"
|
||||||
|
className="font-semibold"
|
||||||
|
>
|
||||||
|
E-PPID
|
||||||
|
</Link>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className=" dark:bg-[#1F1A17]"
|
className=" dark:bg-[#1F1A17]"
|
||||||
onOpenChange={(state) => setIsOpen(state)}
|
onOpenChange={(state) => setIsOpen(state)}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,481 @@
|
||||||
|
import { SidebarMenuTask } from "@/types/globals";
|
||||||
|
import { Tooltip } from "@nextui-org/react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
FormCustomIcon,
|
||||||
|
FormHorizontalIcon,
|
||||||
|
FormLayoutIcon,
|
||||||
|
FormValidationIcon,
|
||||||
|
FormVerticalIcon,
|
||||||
|
} from "../../icons";
|
||||||
|
import {
|
||||||
|
ArticleIcon,
|
||||||
|
DashboardIcon,
|
||||||
|
HomeIcon,
|
||||||
|
InfoCircleIcon,
|
||||||
|
MagazineIcon,
|
||||||
|
MasterCategoryIcon,
|
||||||
|
MasterRoleIcon,
|
||||||
|
MasterUsersIcon,
|
||||||
|
MinusCircleIcon,
|
||||||
|
StaticPageIcon,
|
||||||
|
TableIcon,
|
||||||
|
} from "../../icons/sidebar-icon";
|
||||||
|
import { ThemeSwitch } from "../../theme-switch";
|
||||||
|
import { SidebarCollapseItems } from "./sidebar-collapse-items";
|
||||||
|
import { SidebarCollapseSubItems } from "./sidebar-collapse-sub-items";
|
||||||
|
import { useSidebar } from "./sidebar-context";
|
||||||
|
import { SidebarMenu } from "./sidebar-menu";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { SettingsIcon, UserProfileIcon } from "@/components/icons/globals";
|
||||||
|
|
||||||
|
interface SubMenuItems {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
modulePathUrl: string;
|
||||||
|
isSubActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MenuItems {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
modulePathUrl: string;
|
||||||
|
isSubActive: boolean;
|
||||||
|
childMenu?: SubMenuItems[];
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SidebarProps {
|
||||||
|
sidebarData: boolean;
|
||||||
|
updateSidebarData: (newData: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sideBarDummyData = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "Dashboard",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Dashboard",
|
||||||
|
modulePathUrl: "/admin/dashboard",
|
||||||
|
isGroup: true,
|
||||||
|
parentId: -1,
|
||||||
|
icon: "dashboard",
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Dashboard",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Dashboard",
|
||||||
|
modulePathUrl: "/admin/dashboard",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <DashboardIcon />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "Apps",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Dashboard",
|
||||||
|
modulePathUrl: "/admin/basic",
|
||||||
|
isGroup: true,
|
||||||
|
parentId: -1,
|
||||||
|
icon: "table",
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "Artikel",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Dashboard",
|
||||||
|
modulePathUrl: "/admin/article",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <ArticleIcon size={24} />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 30,
|
||||||
|
name: "Kategori",
|
||||||
|
moduleId: 654,
|
||||||
|
moduleName: "Master",
|
||||||
|
modulePathUrl: "/admin/master-category",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <MasterCategoryIcon size={22} />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "Majalah",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Apps",
|
||||||
|
modulePathUrl: "/admin/magazine",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <MagazineIcon size={23} />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
// {
|
||||||
|
// id: 4,
|
||||||
|
// name: "E-Magazine",
|
||||||
|
// moduleId: 652,
|
||||||
|
// moduleName: "Dashboard",
|
||||||
|
// modulePathUrl: "/admin/e-magazine",
|
||||||
|
// parentId: -1,
|
||||||
|
// icon: <TableIcon />,
|
||||||
|
// position: 1,
|
||||||
|
// statusId: 1,
|
||||||
|
// childMenu: [],
|
||||||
|
// statusName: "Active",
|
||||||
|
// childModule: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "Master",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Dashboard",
|
||||||
|
isGroup: true,
|
||||||
|
modulePathUrl: "/admin/basic",
|
||||||
|
parentId: -1,
|
||||||
|
icon: "table",
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// id: 6,
|
||||||
|
// name: "Master Menu",
|
||||||
|
// moduleId: 652,
|
||||||
|
// moduleName: "Form Custom",
|
||||||
|
// modulePathUrl: "/admin/master-menu",
|
||||||
|
// parentId: -1,
|
||||||
|
// icon: <FormCustomIcon />,
|
||||||
|
// position: 1,
|
||||||
|
// statusId: 1,
|
||||||
|
// childMenu: [],
|
||||||
|
// statusName: "Active",
|
||||||
|
// childModule: null,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: 7,
|
||||||
|
// name: "Master Module",
|
||||||
|
// moduleId: 653,
|
||||||
|
// moduleName: "Form Horizontal",
|
||||||
|
// modulePathUrl: "/admin/master-module",
|
||||||
|
// parentId: -1,
|
||||||
|
// icon: <FormHorizontalIcon />,
|
||||||
|
// position: 1,
|
||||||
|
// statusId: 1,
|
||||||
|
// childMenu: [],
|
||||||
|
// statusName: "Active",
|
||||||
|
// childModule: null,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
name: "Master Static Page",
|
||||||
|
moduleId: 652,
|
||||||
|
moduleName: "Dashboard",
|
||||||
|
modulePathUrl: "/admin/static-page",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <StaticPageIcon size={24} />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
name: "Master User",
|
||||||
|
moduleId: 654,
|
||||||
|
moduleName: "Form Vertical",
|
||||||
|
modulePathUrl: "/admin/master-user",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <MasterUsersIcon />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
name: "Master User Role",
|
||||||
|
moduleId: 656,
|
||||||
|
moduleName: "Form Validation",
|
||||||
|
modulePathUrl: "/admin/master-role",
|
||||||
|
parentId: -1,
|
||||||
|
icon: <MasterRoleIcon />,
|
||||||
|
position: 1,
|
||||||
|
statusId: 1,
|
||||||
|
childMenu: [],
|
||||||
|
statusName: "Active",
|
||||||
|
childModule: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const SidebarMobile: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const router = useRouter();
|
||||||
|
const [sidebarMenu, setSidebarMenu] = useState<SidebarMenuTask[]>();
|
||||||
|
const { isOpen, toggleSidebar } = useSidebar();
|
||||||
|
const token = Cookies.get("access_token");
|
||||||
|
const username = Cookies.get("username");
|
||||||
|
const isAuthenticated = Cookies.get("is_authenticated");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!token) {
|
||||||
|
onLogout();
|
||||||
|
}
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
const onLogout = () => {
|
||||||
|
Object.keys(Cookies.get()).forEach((cookieName) => {
|
||||||
|
Cookies.remove(cookieName);
|
||||||
|
});
|
||||||
|
router.push("/auth");
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateSidebarData(isOpen);
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex lg:hidden absolute h-screen flex-grow ${
|
||||||
|
isOpen ? "min-w-[240px]" : "hidden"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={` flex h-full flex-col p-4 mb-0 bg-gray-950 z-40 transition-width !ease-in-out justify-between ${
|
||||||
|
isOpen ? "w-[238px]" : "w-[80px]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{!isOpen && (
|
||||||
|
<div className="w-full flex justify-center items-center">
|
||||||
|
<button
|
||||||
|
className="w-5 h-5 mb-3 text-zinc-400 dark:text-zinc-400 z-50 border-1 border-zinc-400 rounded-full flex justify-center items-center"
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={`flex ${
|
||||||
|
isOpen ? "justify-between" : "justify-center"
|
||||||
|
} w-full items-center px-2`}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="flex flex-row items-center gap-3 font-bold"
|
||||||
|
>
|
||||||
|
<img src="/logohumas.png" className="w-20" />
|
||||||
|
{/* {isOpen && <span>ACME</span>} */}
|
||||||
|
</Link>
|
||||||
|
{isOpen && (
|
||||||
|
<button
|
||||||
|
className="w-5 h-5 text-zinc-400 z-50 border-1 border-zinc-400 rounded-full flex justify-center items-center"
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SidebarMenu>
|
||||||
|
{sideBarDummyData
|
||||||
|
? sideBarDummyData?.map((list: any, index: number) =>
|
||||||
|
list.isGroup ? (
|
||||||
|
<p
|
||||||
|
key={list}
|
||||||
|
className={`font-bold mr-4 text-white ${
|
||||||
|
!isOpen ? "text-center" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isOpen ? list.name : "..."}
|
||||||
|
</p>
|
||||||
|
) : list.childMenu?.length < 1 ? (
|
||||||
|
<>
|
||||||
|
{isOpen ? (
|
||||||
|
<Link key={list.id} href={list.modulePathUrl}>
|
||||||
|
{/* <div
|
||||||
|
className={`px-3.5 py-2 mr-4 rounded-lg hover:bg-zinc-400 dark:hover:text-zinc-600 flex flex-row gap-2 ${
|
||||||
|
pathname.includes(list.modulePathUrl)
|
||||||
|
? "bg-zinc-600 dark:bg-zinc-300 text-zinc-300 dark:text-zinc-500 font-bold"
|
||||||
|
: "text-zinc-600 dark:text-zinc-400"
|
||||||
|
}`}
|
||||||
|
> */}
|
||||||
|
<div
|
||||||
|
className={`px-3.5 py-2 mr-4 rounded-lg flex flex-row gap-2 ${
|
||||||
|
pathname.includes(list.modulePathUrl)
|
||||||
|
? "bg-white text-black font-bold"
|
||||||
|
: "text-white hover:bg-gray-200 hover:text-black"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{list.icon} {isOpen && list.name}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Tooltip
|
||||||
|
content={list.name}
|
||||||
|
placement="right"
|
||||||
|
delay={0}
|
||||||
|
closeDelay={0}
|
||||||
|
>
|
||||||
|
<Link key={list.id} href={list.modulePathUrl}>
|
||||||
|
<div
|
||||||
|
className={`py-2 mr-4 rounded-lg hover:bg-zinc-400 dark:hover:text-zinc-600 flex flex-row justify-center gap-1 ${
|
||||||
|
pathname.includes(list.modulePathUrl)
|
||||||
|
? "bg-zinc-300 text-zinc-500 font-bold hover:text-black"
|
||||||
|
: "text-zinc-400 hover:text-black"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{list.icon} {isOpen && list.name}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<SidebarCollapseItems
|
||||||
|
key={list.id}
|
||||||
|
title={list.name}
|
||||||
|
isActive={pathname.includes(list.modulePathUrl)}
|
||||||
|
icon={list.icon}
|
||||||
|
items={[
|
||||||
|
list?.childMenu?.map((item: any) => (
|
||||||
|
<SidebarCollapseSubItems
|
||||||
|
key={item.id}
|
||||||
|
title={item?.name}
|
||||||
|
isActive={pathname.includes(item.modulePathUrl)}
|
||||||
|
isParentActive={pathname.includes(
|
||||||
|
list.modulePathUrl
|
||||||
|
)}
|
||||||
|
path={item.modulePathUrl}
|
||||||
|
icon={item.icon}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: ""}
|
||||||
|
</SidebarMenu>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`mt-12 p-2 flex ${
|
||||||
|
isOpen ? "justify-start ml-2" : "justify-center"
|
||||||
|
} mt-auto flex flex-col items-between`}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div
|
||||||
|
className={`flex flex-row ${
|
||||||
|
isOpen ? "justify-start" : "justify-center"
|
||||||
|
} gap-2 items-center text-white `}
|
||||||
|
>
|
||||||
|
<ThemeSwitch />
|
||||||
|
{isOpen && "Theme"}
|
||||||
|
</div>
|
||||||
|
{isOpen ? (
|
||||||
|
<Link href="/settings">
|
||||||
|
<div
|
||||||
|
className={`py-2 mr-4 rounded-lg flex flex-row gap-2 ${
|
||||||
|
pathname.includes("/settings")
|
||||||
|
? "bg-white text-black font-bold px-2"
|
||||||
|
: "text-white "
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<SettingsIcon /> {isOpen && "Settings"}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Tooltip
|
||||||
|
content="Setting"
|
||||||
|
placement="right"
|
||||||
|
delay={0}
|
||||||
|
closeDelay={0}
|
||||||
|
>
|
||||||
|
<Link href="/settings">
|
||||||
|
<div
|
||||||
|
className={`py-2 rounded-lg hover:text-zinc-600 flex flex-row justify-center gap-1 ${
|
||||||
|
pathname.includes("/settings")
|
||||||
|
? "bg-zinc-300 text-zinc-500 font-bold "
|
||||||
|
: "text-zinc-400 "
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<SettingsIcon /> {isOpen && "Settings"}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{isOpen ? (
|
||||||
|
<div className="flex flex-row gap-3 items-center text-white">
|
||||||
|
<UserProfileIcon size={36} />
|
||||||
|
<div className="flex flex-col ">
|
||||||
|
<a className="cursor-pointer ">{username}</a>
|
||||||
|
<a
|
||||||
|
className="hover:text-red-600 underline text-sm cursor-pointer"
|
||||||
|
onClick={() => onLogout()}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Tooltip
|
||||||
|
content="Profile"
|
||||||
|
delay={0}
|
||||||
|
closeDelay={0}
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
className={`cursor-pointer flex flex-row ${
|
||||||
|
isOpen ? "justify-start" : "justify-center"
|
||||||
|
} gap-2 items-center text-zinc-600 dark:text-zinc-400 hover:font-semibold hover:text-zinc-700 dark:hover:text-zinc-300`}
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
>
|
||||||
|
<UserProfileIcon size={28} />
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarMobile;
|
||||||
|
|
@ -262,7 +262,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
|
|
||||||
const onLogout = () => {
|
const onLogout = () => {
|
||||||
Object.keys(Cookies.get()).forEach((cookieName) => {
|
Object.keys(Cookies.get()).forEach((cookieName) => {
|
||||||
Cookies.remove(cookieName);
|
Cookies.remove(cookieName, { path: "/" });
|
||||||
});
|
});
|
||||||
router.push("/auth");
|
router.push("/auth");
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ const ApexChartColumn = (props: {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log("sadadad", getDatas.visit, getDatas.view, getDatas.share);
|
|
||||||
setSeriesVisit(getDatas.visit);
|
setSeriesVisit(getDatas.visit);
|
||||||
setSeriesView(getDatas.view);
|
setSeriesView(getDatas.view);
|
||||||
setSeriesShare(getDatas.share);
|
setSeriesShare(getDatas.share);
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,86 @@ type ArticleData = Article & {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface TopPages {
|
||||||
|
id: number;
|
||||||
|
no: number;
|
||||||
|
title: string;
|
||||||
|
visits: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PostCount {
|
||||||
|
id: number;
|
||||||
|
no: number;
|
||||||
|
name: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dummyTopPages = [
|
||||||
|
{ id: 1, title: "Home Page", visits: 830 },
|
||||||
|
{ id: 2, title: "Media Update", visits: 762 },
|
||||||
|
{ id: 3, title: "Polda Metro Jaya", visits: 532 },
|
||||||
|
{ id: 4, title: "Kapolri: Transformasi Menuju Polri Presisi", visits: 500 },
|
||||||
|
{ id: 5, title: "Polda Jawa Barat Ungkap Kasus Narkoba", visits: 480 },
|
||||||
|
{ id: 6, title: "Polri Perketat Pengamanan Jelang Pemilu", visits: 460 },
|
||||||
|
{ id: 7, title: "Polda Jawa Tengah Berhasil Tangkap Buronan", visits: 440 },
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
title: "Divisi Humas Polri Rilis Data Kejahatan Terbaru",
|
||||||
|
visits: 420,
|
||||||
|
},
|
||||||
|
{ id: 9, title: "Polda Sumatera Utara Gerebek Pabrik Narkoba", visits: 400 },
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
title: "Kapolda Bali Imbau Wisatawan Waspada Kejahatan",
|
||||||
|
visits: 380,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const dummyPostCount = [
|
||||||
|
{ id: 1, name: "Polda Sumatera Utara", count: 132 },
|
||||||
|
{ id: 2, name: "Polda Metro Jaya", count: 128 },
|
||||||
|
{ id: 3, name: "Polda Jawa Barat", count: 120 },
|
||||||
|
{ id: 4, name: "Polda Jawa Timur", count: 115 },
|
||||||
|
{ id: 5, name: "Polda Bali", count: 110 },
|
||||||
|
{ id: 6, name: "Polda Daerah Istimewa Yogyakarta", count: 105 },
|
||||||
|
{ id: 7, name: "Polda Riau", count: 98 },
|
||||||
|
{ id: 8, name: "Polda Sulawesi Selatan", count: 92 },
|
||||||
|
{ id: 9, name: "Polda Kalimantan Timur", count: 85 },
|
||||||
|
{ id: 10, name: "Polda Jawa Tengah", count: 78 },
|
||||||
|
{ id: 11, name: "Polda Kalimantan Selatan", count: 72 },
|
||||||
|
{ id: 12, name: "Polda Sumatera Barat", count: 65 },
|
||||||
|
{ id: 13, name: "Polda Papua", count: 60 },
|
||||||
|
{ id: 14, name: "Polda Nusa Tenggara Barat", count: 54 },
|
||||||
|
{ id: 15, name: "Polda Maluku", count: 49 },
|
||||||
|
{ id: 16, name: "Polda Bengkulu", count: 43 },
|
||||||
|
{ id: 17, name: "Polda Lampung", count: 37 },
|
||||||
|
{ id: 18, name: "Polda Sulawesi Tenggara", count: 30 },
|
||||||
|
{ id: 19, name: "Polda Gorontalo", count: 24 },
|
||||||
|
{ id: 20, name: "Polda Kalimantan Barat", count: 18 },
|
||||||
|
{ id: 21, name: "Polda Kepulauan Riau", count: 10 },
|
||||||
|
{ id: 22, name: "Polda Sulawesi Barat", count: 8 },
|
||||||
|
{ id: 23, name: "Polda Papua Barat", count: 5 },
|
||||||
|
{ id: 24, name: "Polda Maluku Utara", count: 3 },
|
||||||
|
{ id: 25, name: "Polda Nusa Tenggara Timur", count: 2 },
|
||||||
|
{ id: 26, name: "Polda Kalimantan Tengah", count: 1 },
|
||||||
|
{ id: 27, name: "Polda Sulawesi Tengah", count: 1 },
|
||||||
|
{ id: 28, name: "Polda Bangka Belitung", count: 1 },
|
||||||
|
{ id: 29, name: "Polda Jambi", count: 0 },
|
||||||
|
{ id: 30, name: "Polda Banten", count: 0 },
|
||||||
|
{ id: 31, name: "Polda Aceh", count: 0 },
|
||||||
|
{ id: 32, name: "Polda Kalimantan Utara", count: 0 },
|
||||||
|
{ id: 33, name: "Polda Sulawesi Utara", count: 0 },
|
||||||
|
{ id: 34, name: "Polda Kepulauan Bangka Belitung", count: 0 },
|
||||||
|
{ id: 35, name: "Polda Sumatera Selatan", count: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
export default function DashboardContainer() {
|
export default function DashboardContainer() {
|
||||||
const username = Cookies.get("username");
|
const username = Cookies.get("username");
|
||||||
const fullname = Cookies.get("ufne");
|
const fullname = Cookies.get("ufne");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [totalPage, setTotalPage] = useState(1);
|
const [totalPage, setTotalPage] = useState(1);
|
||||||
|
const [topPagespage, setTopPagesPage] = useState(1);
|
||||||
|
const [topPagesTotalPage, setTopPagesTotalPage] = useState(1);
|
||||||
const [article, setArticle] = useState<ArticleData[]>([]);
|
const [article, setArticle] = useState<ArticleData[]>([]);
|
||||||
const [analyticsView, setAnalyticView] = useState<string[]>([
|
const [analyticsView, setAnalyticView] = useState<string[]>([
|
||||||
"visit",
|
"visit",
|
||||||
|
|
@ -49,10 +124,20 @@ export default function DashboardContainer() {
|
||||||
endDate: new Date(),
|
endDate: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [postContentDate, setPostContentDate] = useState({
|
||||||
|
startDate: new Date(new Date().setDate(new Date().getDate() - 7)),
|
||||||
|
endDate: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
const [typeDate, setTypeDate] = useState("monthly");
|
const [typeDate, setTypeDate] = useState("monthly");
|
||||||
|
|
||||||
|
const [topPages, setTopPages] = useState<TopPages[]>([]);
|
||||||
|
const [postCount, setPostCount] = useState<PostCount[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initState();
|
initState();
|
||||||
|
fetchTopPages();
|
||||||
|
fetchPostCount();
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
async function initState() {
|
async function initState() {
|
||||||
|
|
@ -66,6 +151,28 @@ export default function DashboardContainer() {
|
||||||
setTotalPage(res?.data?.meta?.totalPage);
|
setTotalPage(res?.data?.meta?.totalPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchTopPages() {
|
||||||
|
setTopPages(getTableNumber(10, dummyTopPages));
|
||||||
|
setTopPagesTotalPage(1);
|
||||||
|
}
|
||||||
|
async function fetchPostCount() {
|
||||||
|
setPostCount(getTableNumber(10, dummyPostCount));
|
||||||
|
setTopPagesTotalPage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTableNumber = (limit: number, data: any) => {
|
||||||
|
if (data) {
|
||||||
|
const startIndex = limit * (page - 1);
|
||||||
|
let iterate = 0;
|
||||||
|
const newData = data.map((value: any) => {
|
||||||
|
iterate++;
|
||||||
|
value.no = startIndex + iterate;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getMonthYear = (date: Date | string) => {
|
const getMonthYear = (date: Date | string) => {
|
||||||
const newDate = new Date(date);
|
const newDate = new Date(date);
|
||||||
|
|
||||||
|
|
@ -90,10 +197,10 @@ export default function DashboardContainer() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-8 flex justify-center">
|
<div className="px-2 lg:p-8 flex justify-center">
|
||||||
<div className="w-full flex flex-col gap-6">
|
<div className="w-full flex flex-col gap-6">
|
||||||
<div className="w-full flex flex-col md:flex-row gap-6 justify-center">
|
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center">
|
||||||
<div className="px-8 py-4 justify-between w-full md:w-[37%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col rounded-lg">
|
<div className="px-4 lg:px-8 py-4 justify-between w-full lg:w-[37%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col rounded-lg">
|
||||||
<div className="flex justify-between w-full items-center">
|
<div className="flex justify-between w-full items-center">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<p className="font-bold text-xl ">{fullname}</p>
|
<p className="font-bold text-xl ">{fullname}</p>
|
||||||
|
|
@ -111,28 +218,28 @@ export default function DashboardContainer() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full md:w-[20%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
<div className="lg:w-[20%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||||
<div className="h-1/2 flex items-center justify-center">
|
<div className="h-1/2 flex items-center justify-center">
|
||||||
<DashboardSpeecIcon />
|
<DashboardSpeecIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="">Total post</div>
|
<div className="">Total post</div>
|
||||||
<div className="font-semibold text-lg">121</div>
|
<div className="font-semibold text-lg">121</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
<div className="w-full lg:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||||
<div className="h-1/2 flex items-center justify-center">
|
<div className="h-1/2 flex items-center justify-center">
|
||||||
<DashboardConnectIcon />
|
<DashboardConnectIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="">Total views</div>
|
<div className="">Total views</div>
|
||||||
<div className="font-semibold text-lg">154</div>
|
<div className="font-semibold text-lg">154</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
<div className="w-full lg:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||||
<div className="h-1/2 flex items-center justify-center">
|
<div className="h-1/2 flex items-center justify-center">
|
||||||
<DashboardShareIcon />
|
<DashboardShareIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="">Total share</div>
|
<div className="">Total share</div>
|
||||||
<div className="font-semibold text-lg">154</div>
|
<div className="font-semibold text-lg">154</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
<div className="w-full lg:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||||
<div className="h-1/2 flex items-center justify-center">
|
<div className="h-1/2 flex items-center justify-center">
|
||||||
<DashboardCommentIcon size={50} />
|
<DashboardCommentIcon size={50} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -140,69 +247,51 @@ export default function DashboardContainer() {
|
||||||
<div className="font-semibold text-lg">530</div>
|
<div className="font-semibold text-lg">530</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-row gap-6 justify-center ">
|
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center ">
|
||||||
<div className="border-1 shadow-sm w-full rounded-lg md:w-[55%] p-6 flex flex-col">
|
<div className="border-1 shadow-sm w-screen rounded-lg lg:w-[55%] p-6 flex flex-col text-xs lg:text-sm">
|
||||||
<div className="flex justify-between mb-3">
|
<div className="flex justify-between mb-4 items-center">
|
||||||
<div className="font-semibold flex flex-col">
|
<p className="font-semibold">
|
||||||
Analytics
|
Rekapitulasi Post Berita Polda Pada Website
|
||||||
<div className="font-normal text-xs text-gray-600 flex flex-row gap-2">
|
</p>
|
||||||
<CheckboxGroup
|
<div className="w-[220px]">
|
||||||
label=""
|
<Datepicker
|
||||||
value={analyticsView}
|
value={postContentDate}
|
||||||
orientation="horizontal"
|
displayFormat="DD/MM/YYYY"
|
||||||
onValueChange={setAnalyticView}
|
asSingle={false}
|
||||||
>
|
useRange={true}
|
||||||
<Checkbox size="sm" value="visit">
|
onChange={(e: any) => setPostContentDate(e)}
|
||||||
Visit
|
inputClassName="z-50 w-full text-xs lg:text-sm bg-transparent border-1 border-gray-200 px-2 py-[6px] rounded-sm lg:rounded-lg h-[30px] lg:h-[40px] text-gray-600 dark:text-gray-300"
|
||||||
</Checkbox>
|
|
||||||
<Checkbox size="sm" value="view" color="success">
|
|
||||||
View
|
|
||||||
</Checkbox>
|
|
||||||
<Checkbox size="sm" value="share" color="warning">
|
|
||||||
Share
|
|
||||||
</Checkbox>
|
|
||||||
</CheckboxGroup>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
variant={typeDate === "monthly" ? "solid" : "bordered"}
|
|
||||||
onClick={() => setTypeDate("monthly")}
|
|
||||||
>
|
|
||||||
Bulanan
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
onClick={() => setTypeDate("weekly")}
|
|
||||||
variant={typeDate === "weekly" ? "solid" : "bordered"}
|
|
||||||
>
|
|
||||||
Mingguan
|
|
||||||
</Button>
|
|
||||||
<div className="w-auto md:w-[140px]">
|
|
||||||
<Datepicker
|
|
||||||
value={startDateValue}
|
|
||||||
displayFormat="DD/MM/YYYY"
|
|
||||||
asSingle={true}
|
|
||||||
useRange={false}
|
|
||||||
onChange={(e: any) => setStartDateValue(e)}
|
|
||||||
inputClassName="z-50 w-full text-sm bg-transparent border-1 border-gray-200 px-2 py-[6px] rounded-xl h-[40px] text-gray-600 dark:text-gray-300"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row w-full h-full">
|
|
||||||
<div className="w-full h-full">
|
|
||||||
<ApexChartColumn
|
|
||||||
type={typeDate}
|
|
||||||
date={getMonthYear(startDateValue.startDate)}
|
|
||||||
view={analyticsView}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-row border-b-1 gap-1 py-1">
|
||||||
|
<div className="w-[5%]">NO</div>
|
||||||
|
<div className="w-[50%] lg:w-[70%]">POLDA</div>
|
||||||
|
<div className="w-[45%] lg:w-[25%] text-right">
|
||||||
|
JUMLAH POST BERITA
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 lg:h-[500px] overflow-y-auto">
|
||||||
|
{postCount?.map((list) => (
|
||||||
|
<div
|
||||||
|
key={list.id}
|
||||||
|
className="flex flex-row border-b-1 gap-1 py-1"
|
||||||
|
>
|
||||||
|
<div className="w-[5%]">{list?.no}</div>
|
||||||
|
<div className="w-[85%]">{list?.name}</div>
|
||||||
|
<div
|
||||||
|
className={`w-[10%] text-center ${
|
||||||
|
list?.count === 0 && "bg-red-600 text-white"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{list?.count}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-[45%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8">
|
<div className="flex flex-col w-full lg:w-[45%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8 text-sm">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between font-semibold">
|
||||||
<p>Recent Article</p>
|
<p>Recent Article</p>
|
||||||
<Link href="/admin/article/create">
|
<Link href="/admin/article/create">
|
||||||
<Button color="primary" variant="bordered">
|
<Button color="primary" variant="bordered">
|
||||||
|
|
@ -220,7 +309,7 @@ export default function DashboardContainer() {
|
||||||
className="h-[70px] w-[70px] object-cover rounded-lg"
|
className="h-[70px] w-[70px] object-cover rounded-lg"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<p>{textEllipsis(list?.title, 40)}</p>
|
<p>{textEllipsis(list?.title, 78)}</p>
|
||||||
<p className="text-xs">
|
<p className="text-xs">
|
||||||
{convertDateFormat(list?.createdAt)}
|
{convertDateFormat(list?.createdAt)}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -244,6 +333,120 @@ export default function DashboardContainer() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full flex flex-col lg:flex-row gap-6 justify-center ">
|
||||||
|
<div className="border-1 shadow-sm w-screen rounded-lg lg:w-[55%] p-6 flex flex-col">
|
||||||
|
<div className="flex justify-between mb-3">
|
||||||
|
<div className="font-semibold flex flex-col">
|
||||||
|
Analytics
|
||||||
|
<div className="font-normal text-xs text-gray-600 flex flex-row gap-2">
|
||||||
|
<CheckboxGroup
|
||||||
|
label=""
|
||||||
|
value={analyticsView}
|
||||||
|
orientation="vertical"
|
||||||
|
onValueChange={setAnalyticView}
|
||||||
|
className="lg:hidden"
|
||||||
|
>
|
||||||
|
<Checkbox size="sm" value="visit">
|
||||||
|
Visit
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox size="sm" value="view" color="success">
|
||||||
|
View
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox size="sm" value="share" color="warning">
|
||||||
|
Share
|
||||||
|
</Checkbox>
|
||||||
|
</CheckboxGroup>
|
||||||
|
<CheckboxGroup
|
||||||
|
label=""
|
||||||
|
value={analyticsView}
|
||||||
|
orientation="horizontal"
|
||||||
|
onValueChange={setAnalyticView}
|
||||||
|
className="hidden lg:block"
|
||||||
|
>
|
||||||
|
<Checkbox size="sm" value="visit">
|
||||||
|
Visit
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox size="sm" value="view" color="success">
|
||||||
|
View
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox size="sm" value="share" color="warning">
|
||||||
|
Share
|
||||||
|
</Checkbox>
|
||||||
|
</CheckboxGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col lg:flex-row gap-2">
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant={typeDate === "monthly" ? "solid" : "bordered"}
|
||||||
|
onClick={() => setTypeDate("monthly")}
|
||||||
|
className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg"
|
||||||
|
>
|
||||||
|
Bulanan
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => setTypeDate("weekly")}
|
||||||
|
variant={typeDate === "weekly" ? "solid" : "bordered"}
|
||||||
|
className="w-[140px] text-xs lg:text-sm h-[30px] lg:h-[40px] rounded-sm lg:rounded-lg"
|
||||||
|
>
|
||||||
|
Mingguan
|
||||||
|
</Button>
|
||||||
|
<div className="w-[140px]">
|
||||||
|
<Datepicker
|
||||||
|
value={startDateValue}
|
||||||
|
displayFormat="DD/MM/YYYY"
|
||||||
|
asSingle={true}
|
||||||
|
useRange={false}
|
||||||
|
onChange={(e: any) => setStartDateValue(e)}
|
||||||
|
inputClassName="z-50 w-full text-xs lg:text-sm bg-transparent border-1 border-gray-200 px-2 py-[6px] rounded-sm lg:rounded-lg h-[30px] lg:h-[40px] text-gray-600 dark:text-gray-300"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row w-full h-full">
|
||||||
|
<div className="w-full h-[30vh] lg:h-full text-black">
|
||||||
|
<ApexChartColumn
|
||||||
|
type={typeDate}
|
||||||
|
date={getMonthYear(startDateValue.startDate)}
|
||||||
|
view={analyticsView}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col w-full lg:w-[45%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8 text-xs lg:text-sm">
|
||||||
|
<div className="flex justify-between font-semibold">
|
||||||
|
<p>Top Pages</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row border-b-1">
|
||||||
|
<div className="w-[5%]">No</div>
|
||||||
|
<div className="w-[85%]">Title</div>
|
||||||
|
<div className="w-[10%]">Visits</div>
|
||||||
|
</div>
|
||||||
|
{topPages?.map((list) => (
|
||||||
|
<div key={list.id} className="flex flex-row border-b-1">
|
||||||
|
<div className="w-[5%]">{list?.no}</div>
|
||||||
|
<div className="w-[85%]">{list?.title}</div>
|
||||||
|
<div className="w-[10%]">{list?.visits}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-2 w-full flex justify-center">
|
||||||
|
<Pagination
|
||||||
|
isCompact
|
||||||
|
showControls
|
||||||
|
showShadow
|
||||||
|
color="primary"
|
||||||
|
classNames={{
|
||||||
|
base: "bg-transparent",
|
||||||
|
wrapper: "bg-transparent",
|
||||||
|
}}
|
||||||
|
page={topPagespage}
|
||||||
|
total={topPagesTotalPage}
|
||||||
|
onChange={(page) => setTopPagesPage(page)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,30 @@
|
||||||
import { Button } from "@nextui-org/button";
|
import { Button } from "@nextui-org/button";
|
||||||
import { Input, Textarea } from "@nextui-org/input";
|
import { Input, Textarea } from "@nextui-org/input";
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import * as z from "zod";
|
import * as z from "zod";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { otpRequest, otpValidation } from "@/service/master-user";
|
import {
|
||||||
|
deleteArticleComment,
|
||||||
|
editArticleComment,
|
||||||
|
getArticleComment,
|
||||||
|
otpRequest,
|
||||||
|
otpValidation,
|
||||||
|
postArticleComment,
|
||||||
|
} from "@/service/master-user";
|
||||||
import { error } from "@/config/swal";
|
import { error } from "@/config/swal";
|
||||||
|
import { UserProfileIcon } from "@/components/icons/globals";
|
||||||
|
import { convertDateFormat } from "@/utils/global";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import OTPInput from "react-otp-input";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import { SendIcon, TimesIcon } from "@/components/icons";
|
||||||
|
import { saveActivity } from "@/service/activity-log";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
const userId = Cookies.get("uie");
|
||||||
|
const token = Cookies.get("access_token");
|
||||||
|
|
||||||
const commentSchema = z.object({
|
const commentSchema = z.object({
|
||||||
name: z.string().min(1, {
|
name: z.string().min(1, {
|
||||||
|
|
@ -24,9 +43,17 @@ const commentSchema = z.object({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function Comment() {
|
export default function Comment(props: { id: string | null }) {
|
||||||
|
const { id } = props;
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
const pathname = usePathname();
|
||||||
const [needOtp, setNeedOtp] = useState(false);
|
const [needOtp, setNeedOtp] = useState(false);
|
||||||
const [otpValue, setOtpValue] = useState("");
|
const [otpValue, setOtpValue] = useState("");
|
||||||
|
const [commentList, setCommentList] = useState<any>([]);
|
||||||
|
const [openCommentId, setOpenCommentId] = useState(0);
|
||||||
|
const [editCommentId, setEditCommentId] = useState(0);
|
||||||
|
const [replyValue, setReplyValue] = useState("");
|
||||||
|
const [editValue, setEditValue] = useState("");
|
||||||
const formOptions = {
|
const formOptions = {
|
||||||
resolver: zodResolver(commentSchema),
|
resolver: zodResolver(commentSchema),
|
||||||
};
|
};
|
||||||
|
|
@ -36,11 +63,21 @@ export default function Comment() {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
setValue,
|
setValue,
|
||||||
|
reset,
|
||||||
} = useForm<UserSettingSchema>(formOptions);
|
} = useForm<UserSettingSchema>(formOptions);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
const res = await getArticleComment(String(id));
|
||||||
|
setCommentList(res?.data?.data);
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async (values: z.infer<typeof commentSchema>) => {
|
const onSubmit = async (values: z.infer<typeof commentSchema>) => {
|
||||||
if (!needOtp) {
|
if (!needOtp) {
|
||||||
const res = await otpRequest(values.email);
|
const res = await otpRequest(values.email, values?.name);
|
||||||
if (res?.error) {
|
if (res?.error) {
|
||||||
error(res.message);
|
error(res.message);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -52,139 +89,420 @@ export default function Comment() {
|
||||||
error("OTP Tidak Sesuai");
|
error("OTP Tidak Sesuai");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const req = {
|
|
||||||
email: values.email,
|
const data = {
|
||||||
name: values.name,
|
articleId: Number(id),
|
||||||
comment: values.comment,
|
isPublic: true,
|
||||||
|
message: values.comment,
|
||||||
|
parentId: 0,
|
||||||
};
|
};
|
||||||
console.log("req", req);
|
const res = await postArticleComment(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res?.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const req: any = {
|
||||||
|
activityTypeId: 5,
|
||||||
|
url: "https://kontenhumas.com/" + pathname,
|
||||||
|
articleId: Number(id),
|
||||||
|
};
|
||||||
|
|
||||||
|
const resActivity = await saveActivity(req);
|
||||||
|
reset();
|
||||||
|
fetchData();
|
||||||
|
setNeedOtp(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Delete Comment",
|
||||||
|
text: "",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Simpan",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
doDelete(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const doDelete = async (id: number) => {
|
||||||
|
const res = await deleteArticleComment(id);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res?.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Sukses",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendComment = async (idComment: number) => {
|
||||||
|
const data = {
|
||||||
|
articleId: Number(id),
|
||||||
|
isPublic: true,
|
||||||
|
message: replyValue,
|
||||||
|
parentId: idComment,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await postArticleComment(data);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res?.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const req: any = {
|
||||||
|
activityTypeId: 5,
|
||||||
|
url: "https://kontenhumas.com/" + pathname,
|
||||||
|
articleId: Number(id),
|
||||||
|
userId: Number(userId),
|
||||||
|
};
|
||||||
|
|
||||||
|
const resActivity = await saveActivity(req, token);
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// const sendActivity = async () => {};
|
||||||
|
const editComment = async (idComment: number, parentId: number) => {
|
||||||
|
const data = {
|
||||||
|
articleId: Number(id),
|
||||||
|
isPublic: true,
|
||||||
|
id: idComment,
|
||||||
|
message: editValue,
|
||||||
|
parentId: parentId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await editArticleComment(data, idComment);
|
||||||
|
if (res?.error) {
|
||||||
|
error(res?.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setEditCommentId(0);
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const childComment = (parentId: number) => {
|
||||||
|
const filteredComment = commentList.filter(
|
||||||
|
(a: any) => a.parentId === parentId
|
||||||
|
);
|
||||||
|
|
||||||
|
return filteredComment.length > 0 ? (
|
||||||
|
<div className="flex flex-col gap-2 ml-4 w-full">
|
||||||
|
{filteredComment.map((list: any) => (
|
||||||
|
<div className="flex flex-row gap-2 " key={list?.id}>
|
||||||
|
<UserProfileIcon size={44} />
|
||||||
|
<div className="flex flex-col w-full pr-4">
|
||||||
|
<div className="flex justify-between gap-1 w-full">
|
||||||
|
<p className="text-sm font-semibold">{list?.commentFromName}</p>
|
||||||
|
<p className="text-xs">{convertDateFormat(list?.updatedAt)}</p>
|
||||||
|
</div>
|
||||||
|
{editCommentId === list?.id ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id="editComment"
|
||||||
|
placeholder=""
|
||||||
|
label=""
|
||||||
|
value={editValue}
|
||||||
|
onValueChange={setEditValue}
|
||||||
|
endContent={
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<a
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => editComment(list?.id, list?.parentId)}
|
||||||
|
>
|
||||||
|
<SendIcon />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
labelPlacement="outside"
|
||||||
|
className="w-full "
|
||||||
|
classNames={{
|
||||||
|
inputWrapper: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
variant="bordered"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
className="cursor-pointer text-warning"
|
||||||
|
onClick={() => setEditCommentId(0)}
|
||||||
|
>
|
||||||
|
<TimesIcon />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex justify-between gap-1 text-right">
|
||||||
|
<p className="text-sm">{list?.message}</p>
|
||||||
|
|
||||||
|
<div className="flex flex-row gap-2 justify-end">
|
||||||
|
{userId === "16" && (
|
||||||
|
<a
|
||||||
|
className="text-primary cursor-pointer text-xs"
|
||||||
|
onClick={() => {
|
||||||
|
setEditValue(list?.message);
|
||||||
|
setEditCommentId(list?.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{(userId === String(list?.commentFromId) ||
|
||||||
|
userId === "16") && (
|
||||||
|
<a
|
||||||
|
className="text-danger cursor-pointer text-xs"
|
||||||
|
onClick={() => handleDelete(list?.id)}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="p-3 flex flex-col gap-3" onSubmit={handleSubmit(onSubmit)}>
|
<div className="px-0 lg:px-3 flex flex-col gap-3">
|
||||||
<b>Tinggalkan balasan</b>
|
<form
|
||||||
<p className="text-xs">
|
className="py-3 px-4 flex flex-col gap-3 bg-gray-50 text-black dark:bg-stone-900 dark:text-white rounded-lg shadow-md"
|
||||||
Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai{" "}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
<span className="text-red-600">*</span>
|
>
|
||||||
</p>
|
{!needOtp ? (
|
||||||
<div className="flex flex-col gap-1">
|
<>
|
||||||
<p className="text-sm">Komentar</p>
|
<b>Tinggalkan balasan</b>
|
||||||
<Controller
|
<p className="text-xs">
|
||||||
control={control}
|
Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib
|
||||||
name="comment"
|
ditandai <span className="text-red-600">*</span>
|
||||||
render={({ field: { onChange, value } }) => (
|
</p>
|
||||||
<Textarea
|
<div className="flex flex-col gap-1">
|
||||||
type="text"
|
<p className="text-sm">Komentar</p>
|
||||||
id="comment"
|
<Controller
|
||||||
placeholder=""
|
control={control}
|
||||||
label=""
|
name="comment"
|
||||||
value={value}
|
render={({ field: { onChange, value } }) => (
|
||||||
onChange={onChange}
|
<Textarea
|
||||||
labelPlacement="outside"
|
type="text"
|
||||||
className="w-full "
|
id="comment"
|
||||||
classNames={{
|
placeholder=""
|
||||||
inputWrapper: [
|
label=""
|
||||||
"border-1 rounded-lg",
|
value={value}
|
||||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
onChange={onChange}
|
||||||
],
|
labelPlacement="outside"
|
||||||
}}
|
className="w-full "
|
||||||
variant="bordered"
|
classNames={{
|
||||||
/>
|
inputWrapper: [
|
||||||
)}
|
"border-1 rounded-lg",
|
||||||
/>
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
{errors?.comment && (
|
],
|
||||||
<p className="text-red-400 text-sm mb-3">{errors.comment?.message}</p>
|
}}
|
||||||
)}
|
variant="bordered"
|
||||||
</div>
|
/>
|
||||||
<div className="flex flex-col gap-1">
|
)}
|
||||||
<p className="text-sm">
|
/>
|
||||||
Nama <span className="text-red-600">*</span>
|
{errors?.comment && (
|
||||||
</p>
|
<p className="text-red-400 text-sm mb-3">
|
||||||
|
{errors.comment?.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<p className="text-sm">
|
||||||
|
Nama <span className="text-red-600">*</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="name"
|
name="name"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
label=""
|
label=""
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
labelPlacement="outside"
|
labelPlacement="outside"
|
||||||
className="w-full "
|
className="w-full "
|
||||||
classNames={{
|
classNames={{
|
||||||
inputWrapper: [
|
inputWrapper: [
|
||||||
"border-1 rounded-lg",
|
"border-1 rounded-lg",
|
||||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors?.name && (
|
{errors?.name && (
|
||||||
<p className="text-red-400 text-sm mb-3">{errors.name?.message}</p>
|
<p className="text-red-400 text-sm mb-3">
|
||||||
)}
|
{errors.name?.message}
|
||||||
</div>
|
</p>
|
||||||
<div className="flex flex-col gap-1">
|
)}
|
||||||
<p className="text-sm">
|
</div>
|
||||||
Email <span className="text-red-600">*</span>
|
<div className="flex flex-col gap-1">
|
||||||
</p>
|
<p className="text-sm">
|
||||||
<Controller
|
Email <span className="text-red-600">*</span>
|
||||||
control={control}
|
</p>
|
||||||
name="email"
|
<Controller
|
||||||
render={({ field: { onChange, value } }) => (
|
control={control}
|
||||||
<Input
|
name="email"
|
||||||
type="email"
|
render={({ field: { onChange, value } }) => (
|
||||||
id="email"
|
<Input
|
||||||
placeholder=""
|
type="email"
|
||||||
label=""
|
id="email"
|
||||||
value={value}
|
placeholder=""
|
||||||
onChange={onChange}
|
label=""
|
||||||
labelPlacement="outside"
|
value={value}
|
||||||
className="w-full "
|
onChange={onChange}
|
||||||
classNames={{
|
labelPlacement="outside"
|
||||||
inputWrapper: [
|
className="w-full "
|
||||||
"border-1 rounded-lg",
|
classNames={{
|
||||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
inputWrapper: [
|
||||||
],
|
"border-1 rounded-lg",
|
||||||
}}
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
variant="bordered"
|
],
|
||||||
/>
|
}}
|
||||||
)}
|
variant="bordered"
|
||||||
/>
|
/>
|
||||||
{errors?.email && (
|
)}
|
||||||
<p className="text-red-400 text-sm mb-3">{errors.email?.message}</p>
|
/>
|
||||||
)}
|
{errors?.email && (
|
||||||
</div>
|
<p className="text-red-400 text-sm mb-3">
|
||||||
{needOtp && (
|
{errors.email?.message}
|
||||||
<div className="flex flex-col gap-1">
|
</p>
|
||||||
<p>OTP</p>
|
)}
|
||||||
<Input
|
</div>
|
||||||
type="number"
|
</>
|
||||||
id="otp"
|
) : (
|
||||||
placeholder=""
|
<div className="flex flex-col gap-1">
|
||||||
label=""
|
<p className="text-xs">
|
||||||
value={otpValue}
|
Kode verifikasi sudah dikirmkan. Silahkan cek Email Anda!
|
||||||
onValueChange={setOtpValue}
|
</p>
|
||||||
labelPlacement="outside"
|
<p>OTP</p>
|
||||||
className="w-[100px] "
|
|
||||||
classNames={{
|
|
||||||
inputWrapper: [
|
|
||||||
"border-1 rounded-lg",
|
|
||||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
variant="bordered"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button className="bg-[#DD8306] text-white" radius="md" type="submit">
|
<OTPInput
|
||||||
Kirim
|
value={otpValue}
|
||||||
</Button>
|
onChange={setOtpValue}
|
||||||
</form>
|
numInputs={6}
|
||||||
|
renderSeparator={<span>-</span>}
|
||||||
|
renderInput={(props) => (
|
||||||
|
<input
|
||||||
|
{...props}
|
||||||
|
className="!w-[30px] h-[30px] bg-stone-900 dark:bg-white text-white dark:text-black rounded-sm"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button className="bg-[#DD8306] text-white" radius="sm" type="submit">
|
||||||
|
Kirim
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
<div className="flex flex-col gap-2 text-black">
|
||||||
|
{commentList?.map(
|
||||||
|
(list: any) =>
|
||||||
|
list?.parentId === 0 && (
|
||||||
|
<div
|
||||||
|
key={list?.id}
|
||||||
|
className="flex flex-col gap-3 w-full bg-white text-black dark:bg-stone-900 dark:text-white shadow-md rounded-md border-b-2 py-3 px-4 "
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<UserProfileIcon size={44} />
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<p className="text-sm font-semibold">
|
||||||
|
{list?.commentFromName}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm">{list?.message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 text-right">
|
||||||
|
<p className="text-xs">
|
||||||
|
{convertDateFormat(list?.updatedAt)}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-row gap-2 justify-end">
|
||||||
|
{userId === "16" && (
|
||||||
|
<a
|
||||||
|
className="text-primary cursor-pointer text-xs"
|
||||||
|
onClick={() => setOpenCommentId(list?.id)}
|
||||||
|
>
|
||||||
|
Balas
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{(userId === String(list?.commentFromId) ||
|
||||||
|
userId === "16") && (
|
||||||
|
<a
|
||||||
|
className="text-danger cursor-pointer text-xs"
|
||||||
|
onClick={() => handleDelete(list?.id)}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{childComment(list?.id)}
|
||||||
|
{openCommentId === list?.id && (
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id="comment"
|
||||||
|
placeholder=""
|
||||||
|
label=""
|
||||||
|
value={replyValue}
|
||||||
|
onValueChange={setReplyValue}
|
||||||
|
endContent={
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<a
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => sendComment(list?.id)}
|
||||||
|
>
|
||||||
|
<SendIcon />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
labelPlacement="outside"
|
||||||
|
className="w-full "
|
||||||
|
classNames={{
|
||||||
|
inputWrapper: [
|
||||||
|
"border-1 rounded-lg",
|
||||||
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
variant="bordered"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
className="cursor-pointer text-warning"
|
||||||
|
onClick={() => setOpenCommentId(0)}
|
||||||
|
>
|
||||||
|
<TimesIcon />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { ChevronRightIcon } from "@/components/icons";
|
import { ChevronRightIcon } from "@/components/icons";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
import { getMagazineById } from "@/service/magazine";
|
import { getMagazineById } from "@/service/magazine";
|
||||||
import {
|
import {
|
||||||
convertDateFormat,
|
convertDateFormat,
|
||||||
|
|
@ -24,16 +25,18 @@ export default function EMagazineDetail() {
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
|
loading();
|
||||||
const res = await getMagazineById(String(id));
|
const res = await getMagazineById(String(id));
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
setDetailData(data);
|
setDetailData(data);
|
||||||
setDetailFiles(data?.files);
|
setDetailFiles(data?.files);
|
||||||
|
close();
|
||||||
};
|
};
|
||||||
|
|
||||||
const doDownload = async (fileName: string, title: string): Promise<void> => {
|
const doDownload = async (fileName: string, title: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`http://38.47.180.165:8802/magazine-files/viewer/${fileName}`
|
`https://kontenhumas.com/magazine-files/viewer/${fileName}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|
@ -74,10 +77,15 @@ export default function EMagazineDetail() {
|
||||||
{detailData?.title}
|
{detailData?.title}
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-lg h-[380px] flex items-center justify-center">
|
<div className="rounded-lg h-[380px] flex items-center justify-center">
|
||||||
<img
|
{/* <img
|
||||||
src="/emagazine.jpeg"
|
src="/emagazine.jpeg"
|
||||||
alt="emagazine"
|
alt="emagazine"
|
||||||
className="h-[380px] rounded-md py-1"
|
className="h-[380px] rounded-md py-1"
|
||||||
|
/> */}
|
||||||
|
<img
|
||||||
|
src={detailData?.thumbnailUrl}
|
||||||
|
alt="emagazine"
|
||||||
|
className="h-[380px] rounded-md py-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import Link from "next/link";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { getListArticle } from "@/service/article";
|
import { getListArticle } from "@/service/article";
|
||||||
import { formatMonthString, htmlToString, textEllipsis } from "@/utils/global";
|
import { formatMonthString, htmlToString, textEllipsis } from "@/utils/global";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function ListNews() {
|
export default function ListNews() {
|
||||||
const [article, setArticle] = useState<any>([]);
|
const [article, setArticle] = useState<any>([]);
|
||||||
|
|
@ -87,12 +88,16 @@ export default function ListNews() {
|
||||||
>
|
>
|
||||||
<div className="">
|
<div className="">
|
||||||
<img
|
<img
|
||||||
src={news.thumbnailUrl}
|
src={
|
||||||
|
news.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: news.thumbnailUrl
|
||||||
|
}
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
className="rounded-md"
|
className="rounded-t-md h-[27vh] w-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2 lg:p-5 bg-[#f0f0f0] rounded-md">
|
<div className="p-2 lg:p-5 bg-[#f0f0f0] rounded-b-md">
|
||||||
<div className="font-semibold text-lg">{news?.title}</div>
|
<div className="font-semibold text-lg">{news?.title}</div>
|
||||||
<div className="flex flex-row items-center py-1 text-xs gap-2">
|
<div className="flex flex-row items-center py-1 text-xs gap-2">
|
||||||
<div className="flex flex-row items-center gap-1">
|
<div className="flex flex-row items-center gap-1">
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,27 @@ import SidebarDetail from "../../page/sidebar-detail";
|
||||||
import RelatedNews from "../../page/related-news";
|
import RelatedNews from "../../page/related-news";
|
||||||
import Comment from "./comment";
|
import Comment from "./comment";
|
||||||
import { getArticleById, getListArticle } from "@/service/article";
|
import { getArticleById, getListArticle } from "@/service/article";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ChevronRightIcon } from "@/components/icons";
|
import { ChevronRightIcon, UserIcon } from "@/components/icons";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
|
import { saveActivity } from "@/service/activity-log";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
|
const token = Cookies.get("access_token");
|
||||||
|
const uid = Cookies.get("uie");
|
||||||
|
|
||||||
export default function NewsDetailPage() {
|
export default function NewsDetailPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const id: any = params?.id;
|
const id: any = params?.id;
|
||||||
|
const pathname = usePathname();
|
||||||
const [detailArticle, setDetailArticle] = useState();
|
const [detailArticle, setDetailArticle] = useState();
|
||||||
const [articles, setArticles] = useState<any>([]);
|
const [articles, setArticles] = useState<any>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFetch();
|
initFetch();
|
||||||
getArticles();
|
getArticles();
|
||||||
|
sendActivity();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function getArticles() {
|
async function getArticles() {
|
||||||
|
|
@ -34,9 +41,22 @@ export default function NewsDetailPage() {
|
||||||
setDetailArticle(data);
|
setDetailArticle(data);
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sendActivity = async () => {
|
||||||
|
let req: any = {
|
||||||
|
activityTypeId: 2,
|
||||||
|
url: "https://kontenhumas.com/" + pathname,
|
||||||
|
articleId: Number(id?.split("-")[0]),
|
||||||
|
};
|
||||||
|
if (uid) {
|
||||||
|
req.userId = Number(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resActivity = await saveActivity(req, token);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="bg-white">
|
<div className="bg-white">
|
||||||
<div className="px-5 lg:px-0 lg:w-[70vw] mx-auto py-8">
|
<div className="px-5 lg:px-0 lg:w-[75vw] lg:mx-auto py-8">
|
||||||
<div className="flex flex-row gap-4 items-end text-black">
|
<div className="flex flex-row gap-4 items-end text-black">
|
||||||
<Link href="/" className=" font-semibold text-lg">
|
<Link href="/" className=" font-semibold text-lg">
|
||||||
Beranda
|
Beranda
|
||||||
|
|
@ -44,18 +64,18 @@ export default function NewsDetailPage() {
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
<p className=" text-lg">Berita</p>
|
<p className=" text-lg">Berita</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-black lg:flex bg-white gap-4">
|
<div className="text-black lg:flex lg:justify-around bg-white gap-4 lg:gap-16 w-full">
|
||||||
<DetailNews data={detailArticle} listArticle={articles} />
|
<DetailNews data={detailArticle} listArticle={articles} />
|
||||||
<div className="w-auto lg:w-[25%] hidden lg:block">
|
<div className="w-auto lg:w-[25%] hidden lg:block">
|
||||||
<SidebarDetail />
|
<SidebarDetail />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gray-50 text-black dark:bg-stone-900 dark:text-white h-auto my-2 md:my-5 lg:my-10 px-3 lg:px-36 rounded-lg shadow-md">
|
<div className="w-full lg:w-[70%] h-auto my-2 md:my-5 lg:my-10 px-0 lg:px-3">
|
||||||
<Comment />
|
<Comment id={id?.split("-")[0]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gray-50 text-black dark:bg-stone-900 dark:text-white lg:px-24 my-4 lg:my-8 pt-3 lg:pb-4 rounded-lg shadow-md h-fit">
|
<div className="bg-gray-50 text-black dark:bg-stone-900 dark:text-white lg:px-24 my-4 lg:my-8 pt-3 lg:pb-4 rounded-lg shadow-md h-fit ">
|
||||||
<RelatedNews />
|
<RelatedNews />
|
||||||
</div>
|
</div>
|
||||||
<div className="md:hidden text-black">
|
<div className="md:hidden text-black">
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,41 @@ import {
|
||||||
UserIcon,
|
UserIcon,
|
||||||
} from "../icons";
|
} from "../icons";
|
||||||
import { Button } from "@nextui-org/button";
|
import { Button } from "@nextui-org/button";
|
||||||
import { usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { image } from "@nextui-org/theme";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { saveActivity } from "@/service/activity-log";
|
||||||
|
|
||||||
|
const token = Cookies.get("access_token");
|
||||||
|
const uid = Cookies.get("uie");
|
||||||
|
|
||||||
export default function DetailNews(props: { data: any; listArticle: any }) {
|
export default function DetailNews(props: { data: any; listArticle: any }) {
|
||||||
const { data, listArticle } = props;
|
const { data, listArticle } = props;
|
||||||
const [prevArticle, setPrevArticle] = useState("");
|
const [prevArticle, setPrevArticle] = useState("");
|
||||||
const [nextArticle, setNextArticle] = useState("");
|
const [nextArticle, setNextArticle] = useState("");
|
||||||
|
const [imageNow, setImageNow] = useState(0);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const params = useParams();
|
||||||
|
const id: any = params?.id;
|
||||||
const shareText = "Humas Polri";
|
const shareText = "Humas Polri";
|
||||||
|
|
||||||
const handleShare = (platform: string) => {
|
const handleShare = async (platform: string) => {
|
||||||
let shareLink = "";
|
let shareLink = "";
|
||||||
const urls = "https://kontenhumas.com/" + pathname;
|
const urls = "https://kontenhumas.com/" + pathname;
|
||||||
|
|
||||||
|
let req: any = {
|
||||||
|
activityTypeId: 3,
|
||||||
|
url: "https://kontenhumas.com/" + pathname,
|
||||||
|
articleId: Number(id?.split("-")[0]),
|
||||||
|
};
|
||||||
|
if (uid) {
|
||||||
|
req.userId = Number(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resActivity = await saveActivity(req, token);
|
||||||
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case "facebook":
|
case "facebook":
|
||||||
shareLink = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
|
shareLink = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
|
||||||
|
|
@ -102,7 +122,7 @@ export default function DetailNews(props: { data: any; listArticle: any }) {
|
||||||
return { __html: doc.body.innerHTML };
|
return { __html: doc.body.innerHTML };
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2 py-4">
|
<div className="flex flex-col gap-2 py-4 lg:w-[65%] lg:mx-auto">
|
||||||
<p className="font-semibold text-xl lg:text-3xl">{data?.title}</p>
|
<p className="font-semibold text-xl lg:text-3xl">{data?.title}</p>
|
||||||
<div className="flex flex-row items-center py-1 text-xs lg:text-lg gap-2 lg:gap-4">
|
<div className="flex flex-row items-center py-1 text-xs lg:text-lg gap-2 lg:gap-4">
|
||||||
<div className="flex flex-row items-center gap-1">
|
<div className="flex flex-row items-center gap-1">
|
||||||
|
|
@ -133,36 +153,57 @@ export default function DetailNews(props: { data: any; listArticle: any }) {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center my-2 lg:my-5">
|
<div className="flex justify-center my-2 lg:my-5">
|
||||||
<img
|
<Image
|
||||||
alt="NextUI hero Image"
|
width={1440}
|
||||||
src={data?.thumbnailUrl}
|
height={1080}
|
||||||
className="object-cover w-[100%]"
|
alt="Main Image"
|
||||||
|
src={data?.files[imageNow]?.file_url}
|
||||||
|
className="object-cover w-[100%] rounded-md"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{data?.files?.length > 1 && (
|
||||||
|
<div className="flex flex-row gap-3 flex-nowrap overflow-x-auto">
|
||||||
|
{data?.files?.map((file: any, index: number) => (
|
||||||
|
<a
|
||||||
|
key={file.id}
|
||||||
|
onClick={() => setImageNow(index)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
width={480}
|
||||||
|
height={480}
|
||||||
|
alt="NextUI hero Image"
|
||||||
|
src={file?.file_url}
|
||||||
|
className="object-cover w-[75px] lg:w-[150px] h-[50px] lg:h-[100px] rounded-md"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={removeImgTags(
|
dangerouslySetInnerHTML={removeImgTags(
|
||||||
formatTextToHtmlTag(data?.htmlDescription)
|
formatTextToHtmlTag(data?.htmlDescription)
|
||||||
)}
|
)}
|
||||||
className="text-sm lg:text-xl lg:leading-8"
|
className="text-sm lg:text-xl lg:leading-8 text-justify"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-5 my-8">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 lg:gap-5 my-8">
|
||||||
<Button
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
radius="none"
|
radius="none"
|
||||||
onPress={() => handleShare("facebook")}
|
onPress={() => handleShare("facebook")}
|
||||||
className="w-[80%] bg-[#3b5998] text-white px-4 py-2 flex flex-row"
|
className="lg:w-[80%] bg-[#3b5998] text-white px-4 py-2 flex flex-row"
|
||||||
>
|
>
|
||||||
<div className="w-1/4 ">
|
<div className="w-1/4 ">
|
||||||
<SquareFacebookIcon />
|
<SquareFacebookIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-3/4 text-xl ">Facebook</div>
|
<div className=" text-xl ">Facebook</div>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
radius="none"
|
radius="none"
|
||||||
onPress={() => handleShare("x")}
|
onPress={() => handleShare("x")}
|
||||||
className="w-[80%] bg-black text-white px-4 py-2 flex flex-row"
|
className="lg:w-[80%] bg-black text-white px-4 py-2 flex flex-row"
|
||||||
>
|
>
|
||||||
<div className="w-1/4 ">
|
<div className="w-1/4 ">
|
||||||
<SquareXIcon />
|
<SquareXIcon />
|
||||||
|
|
@ -173,7 +214,7 @@ export default function DetailNews(props: { data: any; listArticle: any }) {
|
||||||
variant="solid"
|
variant="solid"
|
||||||
radius="none"
|
radius="none"
|
||||||
onPress={() => handleShare("linkedin")}
|
onPress={() => handleShare("linkedin")}
|
||||||
className="w-[80%] bg-[#0E76A8] text-white px-4 py-2 flex flex-row"
|
className="lg:w-[80%] bg-[#0E76A8] text-white px-4 py-2 flex flex-row"
|
||||||
>
|
>
|
||||||
<div className="w-1/4 ">
|
<div className="w-1/4 ">
|
||||||
<SquareLinkedInIcon />
|
<SquareLinkedInIcon />
|
||||||
|
|
@ -184,7 +225,7 @@ export default function DetailNews(props: { data: any; listArticle: any }) {
|
||||||
variant="solid"
|
variant="solid"
|
||||||
radius="none"
|
radius="none"
|
||||||
onPress={() => handleShare("whatsapp")}
|
onPress={() => handleShare("whatsapp")}
|
||||||
className="w-[80%] bg-[#25D366] text-white px-4 py-2 flex flex-row"
|
className="lg:w-[80%] bg-[#25D366] text-white px-4 py-2 flex flex-row"
|
||||||
>
|
>
|
||||||
<div className="w-1/4 ">
|
<div className="w-1/4 ">
|
||||||
<SquareWhatsappIcon />
|
<SquareWhatsappIcon />
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,12 @@ export default function RelatedNews() {
|
||||||
<Card isFooterBlurred radius="lg" className="border-none">
|
<Card isFooterBlurred radius="lg" className="border-none">
|
||||||
<img
|
<img
|
||||||
alt="headernews"
|
alt="headernews"
|
||||||
src={newsItem.thumbnailUrl}
|
src={
|
||||||
className="h-[25vh] object-cover rounded-none"
|
newsItem?.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: newsItem?.thumbnailUrl
|
||||||
|
}
|
||||||
|
className="!h-[25vh] object-cover rounded-none"
|
||||||
/>
|
/>
|
||||||
<CardFooter className="before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 md:absolute bottom-1 shadow-small z-10">
|
<CardFooter className="before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 md:absolute bottom-1 shadow-small z-10">
|
||||||
<div className="text-black dark:text-white lg:text-white">
|
<div className="text-black dark:text-white lg:text-white">
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,11 @@ export default function SidebarDetail() {
|
||||||
<div className="h-[230px] lg:h-[180px] flex flex-col gap-2 bg-gray-50 dark:bg-stone-900 p-5 rounded-lg">
|
<div className="h-[230px] lg:h-[180px] flex flex-col gap-2 bg-gray-50 dark:bg-stone-900 p-5 rounded-lg">
|
||||||
<img
|
<img
|
||||||
alt="headernews"
|
alt="headernews"
|
||||||
src={newsItem.thumbnailUrl}
|
src={
|
||||||
|
newsItem?.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: newsItem?.thumbnailUrl
|
||||||
|
}
|
||||||
className="object-cover !h-[70%] rounded-lg"
|
className="object-cover !h-[70%] rounded-lg"
|
||||||
/>
|
/>
|
||||||
<div className="text-black dark:text-white flex flex-col">
|
<div className="text-black dark:text-white flex flex-col">
|
||||||
|
|
@ -106,7 +110,11 @@ export default function SidebarDetail() {
|
||||||
<div className="h-[230px] lg:h-[180px] flex flex-col gap-2 bg-gray-50 dark:bg-stone-900 p-5 rounded-lg">
|
<div className="h-[230px] lg:h-[180px] flex flex-col gap-2 bg-gray-50 dark:bg-stone-900 p-5 rounded-lg">
|
||||||
<img
|
<img
|
||||||
alt="headernews"
|
alt="headernews"
|
||||||
src={newsItem.thumbnailUrl}
|
src={
|
||||||
|
newsItem?.thumbnailUrl == ""
|
||||||
|
? "/no-image.jpg"
|
||||||
|
: newsItem?.thumbnailUrl
|
||||||
|
}
|
||||||
className="object-cover !h-[70%] rounded-lg"
|
className="object-cover !h-[70%] rounded-lg"
|
||||||
/>
|
/>
|
||||||
<div className="text-black dark:text-white flex flex-col">
|
<div className="text-black dark:text-white flex flex-col">
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export default function ArticleTable() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initState();
|
initState();
|
||||||
}, [page, showData, startDateValue]);
|
}, [page, showData, startDateValue, selectedCategories]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCategories();
|
getCategories();
|
||||||
|
|
@ -80,7 +80,6 @@ export default function ArticleTable() {
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const res = await getArticleByCategory();
|
const res = await getArticleByCategory();
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
console.log("datass", res?.data?.data);
|
|
||||||
setCategoies(data);
|
setCategoies(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,10 +224,10 @@ export default function ArticleTable() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-3">
|
<div className="py-3">
|
||||||
<div className="flex flex-col items-start rounded-2xl gap-3">
|
<div className="flex flex-col items-start rounded-2xl gap-3">
|
||||||
<div className="flex flex-col md:flex-row gap-3 w-full">
|
<div className="flex flex-col md:flex-row gap-3 w-full">
|
||||||
<div className="flex flex-col gap-1 w-1/3">
|
<div className="flex flex-col gap-1 w-full lg:w-1/3">
|
||||||
<p className="font-semibold text-sm">Pencarian</p>
|
<p className="font-semibold text-sm">Pencarian</p>
|
||||||
<Input
|
<Input
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
|
|
@ -246,7 +245,7 @@ export default function ArticleTable() {
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-[72px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[72px]">
|
||||||
<p className="font-semibold text-sm">Data</p>
|
<p className="font-semibold text-sm">Data</p>
|
||||||
<Select
|
<Select
|
||||||
label=""
|
label=""
|
||||||
|
|
@ -268,22 +267,28 @@ export default function ArticleTable() {
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-[230px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[230px]">
|
||||||
<p className="font-semibold text-sm">Kategori</p>
|
<p className="font-semibold text-sm">Kategori</p>
|
||||||
<Select
|
<Select
|
||||||
label=""
|
label=""
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
labelPlacement="outside"
|
labelPlacement="outside"
|
||||||
placeholder="Select"
|
placeholder="Kategori"
|
||||||
selectionMode="multiple"
|
selectionMode="multiple"
|
||||||
selectedKeys={[selectedCategories]}
|
selectedKeys={selectedCategories}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
items={categories}
|
||||||
classNames={{ trigger: "border-1" }}
|
classNames={{ trigger: "border-1" }}
|
||||||
onChange={(e) => {
|
onSelectionChange={setSelectedCategories}
|
||||||
e.target.value === ""
|
renderValue={(items) => {
|
||||||
? ""
|
return items.map((item) => (
|
||||||
: setSelectedCategories(e.target.value);
|
<span
|
||||||
console.log("eeess", e.target.value);
|
key={item.props?.value}
|
||||||
|
className="text-black text-xs"
|
||||||
|
>
|
||||||
|
{item.textValue},
|
||||||
|
</span>
|
||||||
|
));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{categories?.map((category: any) => (
|
{categories?.map((category: any) => (
|
||||||
|
|
@ -293,7 +298,7 @@ export default function ArticleTable() {
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-full md:w-[240px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[240px]">
|
||||||
<p className="font-semibold text-sm">Tanggal</p>
|
<p className="font-semibold text-sm">Tanggal</p>
|
||||||
<Datepicker
|
<Datepicker
|
||||||
value={startDateValue}
|
value={startDateValue}
|
||||||
|
|
|
||||||
|
|
@ -226,10 +226,10 @@ export default function MagazineTable() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-3">
|
<div className="py-3">
|
||||||
<div className="flex flex-col items-start rounded-2xl gap-3">
|
<div className="flex flex-col items-start rounded-2xl gap-3">
|
||||||
<div className="flex flex-col md:flex-row gap-3 w-full">
|
<div className="flex flex-col md:flex-row gap-3 w-full">
|
||||||
<div className="flex flex-col gap-1 w-1/3">
|
<div className="flex flex-col gap-1 w-full lg:w-1/3">
|
||||||
<p className="font-semibold text-sm">Pencarian</p>
|
<p className="font-semibold text-sm">Pencarian</p>
|
||||||
<Input
|
<Input
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
|
|
@ -247,7 +247,7 @@ export default function MagazineTable() {
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-[72px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[72px]">
|
||||||
<p className="font-semibold text-sm">Data</p>
|
<p className="font-semibold text-sm">Data</p>
|
||||||
<Select
|
<Select
|
||||||
label=""
|
label=""
|
||||||
|
|
@ -294,7 +294,7 @@ export default function MagazineTable() {
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="flex flex-col gap-1 w-full md:w-[240px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[240px]">
|
||||||
<p className="font-semibold text-sm">Tanggal</p>
|
<p className="font-semibold text-sm">Tanggal</p>
|
||||||
<Datepicker
|
<Datepicker
|
||||||
value={startDateValue}
|
value={startDateValue}
|
||||||
|
|
|
||||||
|
|
@ -341,10 +341,10 @@ export default function CategoriesTable(props: { triggerRefresh: boolean }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-3">
|
<div className="py-3">
|
||||||
<div className="flex flex-col items-start rounded-2xl gap-3">
|
<div className="flex flex-col items-start rounded-2xl gap-3">
|
||||||
<div className="flex flex-col md:flex-row gap-3 w-full">
|
<div className="flex flex-col md:flex-row gap-3 w-full">
|
||||||
<div className="flex flex-col gap-1 w-1/3">
|
<div className="flex flex-col gap-1 w-full lg:w-1/3">
|
||||||
<p className="font-semibold text-sm">Pencarian</p>
|
<p className="font-semibold text-sm">Pencarian</p>
|
||||||
<Input
|
<Input
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
|
|
@ -362,7 +362,7 @@ export default function CategoriesTable(props: { triggerRefresh: boolean }) {
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-[72px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[72px]">
|
||||||
<p className="font-semibold text-sm">Data</p>
|
<p className="font-semibold text-sm">Data</p>
|
||||||
<Select
|
<Select
|
||||||
label=""
|
label=""
|
||||||
|
|
@ -523,7 +523,7 @@ export default function CategoriesTable(props: { triggerRefresh: boolean }) {
|
||||||
onValueChange={setTag}
|
onValueChange={setTag}
|
||||||
startContent={
|
startContent={
|
||||||
<div className="flex flex-row gap-1">
|
<div className="flex flex-row gap-1">
|
||||||
{value.map((item, index) => (
|
{value?.map((item, index) => (
|
||||||
<Chip
|
<Chip
|
||||||
color="primary"
|
color="primary"
|
||||||
key={index}
|
key={index}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
createUserLevel,
|
||||||
|
editUserLevel,
|
||||||
|
getUserLevel,
|
||||||
|
} from "@/service/master-user-level";
|
||||||
|
import { Button } from "@nextui-org/button";
|
||||||
|
import { RootRaws } from "postcss/lib/root";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
// Define the shape of the parsed data
|
||||||
|
interface CSVRow {
|
||||||
|
id: string;
|
||||||
|
parent: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
level_number: string;
|
||||||
|
group: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MappingUserLevel() {
|
||||||
|
const [parsedData, setParsedData] = useState<CSVRow[] | null>(null);
|
||||||
|
|
||||||
|
// Function to parse the CSV data
|
||||||
|
function parseCSV(csvText: string): CSVRow[] {
|
||||||
|
const rows = csvText.split("\n");
|
||||||
|
const headers = rows[0].split("\t"); // Assuming tab-delimited CSV
|
||||||
|
const data: CSVRow[] = [];
|
||||||
|
|
||||||
|
for (let i = 1; i < rows.length; i++) {
|
||||||
|
const row = rows[i].split("\t");
|
||||||
|
|
||||||
|
// Skip empty rows or rows that don't have the correct number of columns
|
||||||
|
if (
|
||||||
|
row.length === headers.length &&
|
||||||
|
row.some((cell) => cell.trim() !== "")
|
||||||
|
) {
|
||||||
|
const temp = row[0].split(";");
|
||||||
|
|
||||||
|
if (temp[0] !== "") {
|
||||||
|
temp[4].replace("/r", "");
|
||||||
|
console.log("tempppp", temp);
|
||||||
|
const rowData: CSVRow = {
|
||||||
|
id: temp[0],
|
||||||
|
parent: temp[1],
|
||||||
|
name: temp[2],
|
||||||
|
slug: temp[3],
|
||||||
|
level_number: temp[4],
|
||||||
|
group: temp[5].replace("\r", ""),
|
||||||
|
};
|
||||||
|
if (i <= 3) {
|
||||||
|
console.log("i", rowData);
|
||||||
|
}
|
||||||
|
data.push(rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the file input change event
|
||||||
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = event.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function (e) {
|
||||||
|
const csvText = e.target?.result as string;
|
||||||
|
const data = parseCSV(csvText);
|
||||||
|
console.log("data", data);
|
||||||
|
setParsedData(data);
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doPostData = async () => {
|
||||||
|
if (parsedData) {
|
||||||
|
let level1 = undefined;
|
||||||
|
let level2 = undefined;
|
||||||
|
|
||||||
|
for (let i = 0; i < parsedData.length; i++) {
|
||||||
|
if (i < 4) {
|
||||||
|
const temp = parsedData[i];
|
||||||
|
if (temp.level_number === "2") {
|
||||||
|
const request = {
|
||||||
|
aliasName: temp.slug,
|
||||||
|
group: temp.group,
|
||||||
|
isActive: true,
|
||||||
|
levelNumber: 2,
|
||||||
|
name: temp.name,
|
||||||
|
parentLevelId: level1,
|
||||||
|
provinceId: 0,
|
||||||
|
};
|
||||||
|
const res = await createUserLevel(request);
|
||||||
|
if (res?.error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
level2 = res?.data?.data?.id;
|
||||||
|
} else if (temp.level_number === "3") {
|
||||||
|
const request = {
|
||||||
|
aliasName: temp.slug,
|
||||||
|
group: temp.group,
|
||||||
|
isActive: true,
|
||||||
|
levelNumber: 3,
|
||||||
|
name: temp.name,
|
||||||
|
parentLevelId: level2,
|
||||||
|
provinceId: 0,
|
||||||
|
};
|
||||||
|
const res = await createUserLevel(request);
|
||||||
|
if (res?.error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const request = {
|
||||||
|
aliasName: temp.slug,
|
||||||
|
group: temp.group,
|
||||||
|
isActive: true,
|
||||||
|
levelNumber: 1,
|
||||||
|
name: temp.name,
|
||||||
|
parentLevelId: 0,
|
||||||
|
provinceId: 0,
|
||||||
|
};
|
||||||
|
const res = await createUserLevel(request);
|
||||||
|
if (res?.error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
level1 = res?.data?.data?.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initFecth();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initFecth = async () => {
|
||||||
|
const res = await getUserLevel();
|
||||||
|
const data = res?.data?.data;
|
||||||
|
console.log("dataa", data);
|
||||||
|
// for (const element of data) {
|
||||||
|
// if (element.aliasName === "satker") {
|
||||||
|
// console.log("namanya", element.id);
|
||||||
|
// const request = {
|
||||||
|
// aliasName: element.aliasName,
|
||||||
|
// levelNumber: 2,
|
||||||
|
// name: element.name,
|
||||||
|
// parentLevelId: 692,
|
||||||
|
// provinceId: 0,
|
||||||
|
// };
|
||||||
|
// console.log("reqq", request);
|
||||||
|
// const response = await editUserLevel(request, element.id);
|
||||||
|
// if (response?.error) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Mapping User Level</h1>
|
||||||
|
<Button onPress={doPostData}>Run</Button>
|
||||||
|
{/* File input */}
|
||||||
|
<input type="file" accept=".csv" onChange={handleFileChange} />
|
||||||
|
|
||||||
|
{/* Display parsed data */}
|
||||||
|
{parsedData && (
|
||||||
|
<div>
|
||||||
|
<h2>Parsed Data:</h2>
|
||||||
|
<pre>{JSON.stringify(parsedData, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -200,10 +200,10 @@ export default function StaticPageTable() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="p-3">
|
<div className="py-3">
|
||||||
<div className="flex flex-col items-start rounded-2xl gap-3">
|
<div className="flex flex-col items-start rounded-2xl gap-3">
|
||||||
<div className="flex flex-col md:flex-row gap-3 w-full">
|
<div className="flex flex-col md:flex-row gap-3 w-full">
|
||||||
<div className="flex flex-col gap-1 w-1/3">
|
<div className="flex flex-col gap-1 w-full lg:w-1/3">
|
||||||
<p className="font-semibold text-sm">Pencarian</p>
|
<p className="font-semibold text-sm">Pencarian</p>
|
||||||
<Input
|
<Input
|
||||||
aria-label="Pencarian..."
|
aria-label="Pencarian..."
|
||||||
|
|
@ -221,7 +221,7 @@ export default function StaticPageTable() {
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-[72px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[72px]">
|
||||||
<p className="font-semibold text-sm">Data</p>
|
<p className="font-semibold text-sm">Data</p>
|
||||||
<Select
|
<Select
|
||||||
label=""
|
label=""
|
||||||
|
|
@ -243,7 +243,7 @@ export default function StaticPageTable() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1 w-full md:w-[340px]">
|
<div className="flex flex-col gap-1 w-full lg:w-[340px]">
|
||||||
<p className="font-semibold text-sm">Tanggal</p>
|
<p className="font-semibold text-sm">Tanggal</p>
|
||||||
<Datepicker
|
<Datepicker
|
||||||
value={startDateValue}
|
value={startDateValue}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Button } from "@nextui-org/button";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "@nextui-org/react";
|
import { BreadcrumbItem, Breadcrumbs } from "@nextui-org/react";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { Image } from "@nextui-org/react";
|
import { Image } from "@nextui-org/react";
|
||||||
import { FormLayoutIcon } from "../icons";
|
import { BurgerButtonIcon, FormLayoutIcon } from "../icons";
|
||||||
import {
|
import {
|
||||||
ArticleIcon,
|
ArticleIcon,
|
||||||
DashboardIcon,
|
DashboardIcon,
|
||||||
|
|
@ -13,14 +13,18 @@ import {
|
||||||
MasterCategoryIcon,
|
MasterCategoryIcon,
|
||||||
MasterRoleIcon,
|
MasterRoleIcon,
|
||||||
MasterUsersIcon,
|
MasterUsersIcon,
|
||||||
|
MenuBurgerIcon,
|
||||||
StaticPageIcon,
|
StaticPageIcon,
|
||||||
} from "../icons/sidebar-icon";
|
} from "../icons/sidebar-icon";
|
||||||
|
import { useSidebar } from "../layout/sidebar/sidebar-context";
|
||||||
|
|
||||||
export const Breadcrumb = () => {
|
export const Breadcrumb = () => {
|
||||||
const [currentPage, setCurrentPage] = useState<React.Key>("");
|
const [currentPage, setCurrentPage] = useState<React.Key>("");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const pathnameSplit = pathname.split("/");
|
const pathnameSplit = pathname.split("/");
|
||||||
|
const { isOpen, toggleSidebar } = useSidebar();
|
||||||
|
|
||||||
pathnameSplit.shift();
|
pathnameSplit.shift();
|
||||||
let pathnameTransformed = pathnameSplit.map((item) => {
|
let pathnameTransformed = pathnameSplit.map((item) => {
|
||||||
let words = item.split("-");
|
let words = item.split("-");
|
||||||
|
|
@ -67,16 +71,28 @@ export const Breadcrumb = () => {
|
||||||
)}
|
)}
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
{pathname.includes("dashboard") && <DashboardIcon size={50} />}
|
<div className="lg:hidden">
|
||||||
{pathname.includes("article") && <ArticleIcon size={50} />}
|
{!isOpen && (
|
||||||
{pathname.includes("master-category") && (
|
<button
|
||||||
<MasterCategoryIcon size={50} />
|
className="w-5 h-5 mb-3 text-zinc-400 dark:text-zinc-400 z-50 flex justify-center items-center"
|
||||||
)}
|
onClick={toggleSidebar}
|
||||||
{pathname.includes("magazine") && <MagazineIcon size={50} />}
|
>
|
||||||
{pathname.includes("static-page") && <StaticPageIcon size={50} />}
|
<BurgerButtonIcon />
|
||||||
{pathname.includes("master-user") && <MasterUsersIcon size={50} />}
|
</button>
|
||||||
{pathname.includes("master-role") && <MasterRoleIcon size={50} />}
|
)}
|
||||||
{/* <FormLayoutIcon width={50} height={50} /> */}
|
</div>
|
||||||
|
<div className="hidden lg:block">
|
||||||
|
{pathname.includes("dashboard") && <DashboardIcon size={50} />}
|
||||||
|
{pathname.includes("article") && <ArticleIcon size={50} />}
|
||||||
|
{pathname.includes("master-category") && (
|
||||||
|
<MasterCategoryIcon size={50} />
|
||||||
|
)}
|
||||||
|
{pathname.includes("magazine") && <MagazineIcon size={50} />}
|
||||||
|
{pathname.includes("static-page") && <StaticPageIcon size={50} />}
|
||||||
|
{pathname.includes("master-user") && <MasterUsersIcon size={50} />}
|
||||||
|
{pathname.includes("master-role") && <MasterRoleIcon size={50} />}
|
||||||
|
{/* <FormLayoutIcon width={50} height={50} /> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
"use client";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
const GPRKominfo = () => {
|
const GPRKominfo = () => {
|
||||||
|
const t = useTranslations("Landing");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
|
|
@ -14,12 +18,34 @@ const GPRKominfo = () => {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// const handleResize = () => {
|
||||||
|
// const bodyElement = document.getElementById("gpr-kominfo-widget-body");
|
||||||
|
// if (bodyElement) {
|
||||||
|
// bodyElement.style.height = "67vh";
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
|
// handleResize();
|
||||||
|
|
||||||
|
// return () => {
|
||||||
|
// window.removeEventListener("resize", handleResize);
|
||||||
|
// };
|
||||||
|
// }, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex flex-col justify-between">
|
||||||
<div id="gpr-kominfo-widget-header"></div>
|
<div
|
||||||
|
id="gpr-kominfo-widget-header"
|
||||||
|
className="text-center flex justify-center items-center text-lg font-bold text-white rounded-t-xl"
|
||||||
|
>
|
||||||
|
{t("topik")}
|
||||||
|
</div>
|
||||||
<div id="gpr-kominfo-widget-body"></div>
|
<div id="gpr-kominfo-widget-body"></div>
|
||||||
<div id="gpr-kominfo-widget-footer"></div>
|
{/* <div id="gpr-kominfo-widget-footer"></div> */}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,8 @@ export const siteConfig = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{ key: "e-ppid", label: "E-PPID", href: "https://eppid.polri.go.id/" },
|
||||||
|
|
||||||
{
|
{
|
||||||
key: "public-information",
|
key: "public-information",
|
||||||
label: "informasi_publik",
|
label: "informasi_publik",
|
||||||
|
|
@ -195,7 +197,6 @@ export const siteConfig = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: "related-app",
|
key: "related-app",
|
||||||
label: "aplikasi_terkait",
|
label: "aplikasi_terkait",
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
"lebihBanyak": "See More",
|
"lebihBanyak": "See More",
|
||||||
"kategoriSatker": "Satker Cateogry",
|
"kategoriSatker": "Satker Cateogry",
|
||||||
"beritaWilayah": "Regional News",
|
"beritaWilayah": "Regional News",
|
||||||
"tutup": "Close"
|
"tutup": "Close",
|
||||||
|
"topik": "Main Topic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
"lebihBanyak": "Lihat Lebih Banyak",
|
"lebihBanyak": "Lihat Lebih Banyak",
|
||||||
"kategoriSatker": "Kategori Satker",
|
"kategoriSatker": "Kategori Satker",
|
||||||
"beritaWilayah": "Berita Wilayah",
|
"beritaWilayah": "Berita Wilayah",
|
||||||
"tutup": "Tutup"
|
"tutup": "Tutup",
|
||||||
|
"topik": "Topik Utama"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
eslint: {
|
eslint: {
|
||||||
ignoreDuringBuilds: true,
|
ignoreDuringBuilds: true,
|
||||||
},
|
},
|
||||||
images: {
|
images: {
|
||||||
domains: ['38.47.180.165'],
|
remotePatterns: [
|
||||||
},
|
{
|
||||||
}
|
hostname: "*",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = nextConfig
|
module.exports = nextConfig;
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
"react-dropzone": "^14.3.5",
|
"react-dropzone": "^14.3.5",
|
||||||
"react-hook-form": "^7.50.1",
|
"react-hook-form": "^7.50.1",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
|
"react-otp-input": "^3.1.1",
|
||||||
"react-password-checklist": "^1.8.1",
|
"react-password-checklist": "^1.8.1",
|
||||||
"react-select": "^5.8.3",
|
"react-select": "^5.8.3",
|
||||||
"react-sweetalert2": "^0.6.0",
|
"react-sweetalert2": "^0.6.0",
|
||||||
|
|
@ -9091,6 +9092,15 @@
|
||||||
"react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x"
|
"react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-otp-input": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-otp-input/-/react-otp-input-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-bjPavgJ0/Zmf/AYi4onj8FbH93IjeD+e8pWwxIJreDEWsU1ILR5fs8jEJmMGWSBe/yyvPP6X/W6Mk9UkOCkTPw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.6 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": ">=16.8.6 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-password-checklist": {
|
"node_modules/react-password-checklist": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-password-checklist/-/react-password-checklist-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-password-checklist/-/react-password-checklist-1.8.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
"react-dropzone": "^14.3.5",
|
"react-dropzone": "^14.3.5",
|
||||||
"react-hook-form": "^7.50.1",
|
"react-hook-form": "^7.50.1",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
|
"react-otp-input": "^3.1.1",
|
||||||
"react-password-checklist": "^1.8.1",
|
"react-password-checklist": "^1.8.1",
|
||||||
"react-select": "^5.8.3",
|
"react-select": "^5.8.3",
|
||||||
"react-sweetalert2": "^0.6.0",
|
"react-sweetalert2": "^0.6.0",
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 MiB |
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { PaginationRequest } from "@/types/globals";
|
||||||
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
|
httpGet,
|
||||||
|
httpPost,
|
||||||
|
httpPut,
|
||||||
|
} from "./http-config/axios-base-service";
|
||||||
|
|
||||||
|
export async function saveActivity(data: any, token?: string) {
|
||||||
|
const headers = token
|
||||||
|
? {
|
||||||
|
"content-type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
const pathUrl = `/activity-logs`;
|
||||||
|
return await httpPost(pathUrl, headers, data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import {
|
||||||
|
httpDeleteInterceptor,
|
||||||
|
httpGet,
|
||||||
|
httpPost,
|
||||||
|
httpPut,
|
||||||
|
} from "./http-config/axios-base-service";
|
||||||
|
|
||||||
|
export async function createUserLevel(data: any) {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
const pathUrl = `/user-levels`;
|
||||||
|
return await httpPost(pathUrl, headers, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserLevel() {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
return await httpGet(`/user-levels?levelNumber=2&parentLevelId=79`, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function editUserLevel(data: any, id: number) {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
const pathUrl = `/user-levels/${id}`;
|
||||||
|
return await httpPut(pathUrl, headers, data);
|
||||||
|
}
|
||||||
|
|
@ -73,11 +73,11 @@ export async function checkUsernames(username: string) {
|
||||||
return await httpPost(`/users/forgot-password`, headers, { username });
|
return await httpPost(`/users/forgot-password`, headers, { username });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function otpRequest(email: string) {
|
export async function otpRequest(email: string, name: string) {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
};
|
};
|
||||||
return await httpPost(`/users/otp-request`, headers, { email });
|
return await httpPost(`/users/otp-request`, headers, { email, name });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function otpValidation(email: string, otpCode: string) {
|
export async function otpValidation(email: string, otpCode: string) {
|
||||||
|
|
@ -86,3 +86,34 @@ export async function otpValidation(email: string, otpCode: string) {
|
||||||
};
|
};
|
||||||
return await httpPost(`/users/otp-validation`, headers, { email, otpCode });
|
return await httpPost(`/users/otp-validation`, headers, { email, otpCode });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function postArticleComment(data: any) {
|
||||||
|
const headers = token
|
||||||
|
? {
|
||||||
|
"content-type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
return await httpPost(`/article-comments`, headers, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function editArticleComment(data: any, id: number) {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
};
|
||||||
|
return await httpPut(`/article-comments/${id}`, headers, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getArticleComment(id: string) {
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
return await httpGet(`/article-comments?articleId=${id}`, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteArticleComment(id: number) {
|
||||||
|
return await httpDeleteInterceptor(`/article-comments/${id}`);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,7 @@ main {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.komdigi-styling #gpr-kominfo-widget-body {
|
||||||
|
height: 67vh !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue