fix: landing magazine, article aiId, feat:setting form
This commit is contained in:
parent
7ad85eb8a9
commit
a738e61277
|
|
@ -4,7 +4,7 @@ import { Card } from "@nextui-org/react";
|
|||
|
||||
export default function CreateArticle() {
|
||||
return (
|
||||
<div className="h-[96vh] bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||
<div className="bg-transparent p-8 !bg-slate-100 dark:!bg-black overflow-y-auto">
|
||||
{/* <FormArticle /> */}
|
||||
<CreateArticleForm />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,25 +22,14 @@ export default function BasicPage() {
|
|||
|
||||
return (
|
||||
<div className="overflow-x-hidden overflow-y-scroll">
|
||||
<div className="px-2 md:px-4 w-full">
|
||||
<div className="rounded-md my-5 px-5 py-2 shadow-lg bg-white dark:bg-[#18181b] flex flex-row gap-3">
|
||||
<Link href="/admin/article/create">
|
||||
<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">
|
||||
<Link href="/admin/article/create" className="mx-3">
|
||||
<Button size="md" className="bg-[#F07C00] text-white">
|
||||
New Article
|
||||
Tambah Artikel
|
||||
<AddIcon />
|
||||
</Button>
|
||||
</Link>
|
||||
{/* <Button
|
||||
size="md"
|
||||
color="primary"
|
||||
className="bg-[#F07C00] text-white"
|
||||
onPress={goGenerate}
|
||||
>
|
||||
<AddIcon />
|
||||
Generate Article
|
||||
</Button> */}
|
||||
</div>
|
||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-2">
|
||||
<ArticleTable />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,39 +1,20 @@
|
|||
"use client";
|
||||
import { AddIcon } from "@/components/icons";
|
||||
import ArticleTable from "@/components/table/article-table";
|
||||
import MagazineTable from "@/components/table/magazine/magazine-table";
|
||||
import generatedArticleIds from "@/store/generated-article-store";
|
||||
import { Button, Card } from "@nextui-org/react";
|
||||
import { Button } from "@nextui-org/react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function MagazineTablePage() {
|
||||
const router = useRouter();
|
||||
const setGeneratedArticleIdStore = generatedArticleIds(
|
||||
(state) => state.setArticleIds
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="overflow-x-hidden overflow-y-scroll">
|
||||
<div className="px-2 md:px-4 w-full">
|
||||
<div className="rounded-md my-5 px-5 py-2 shadow-lg bg-white dark:bg-[#18181b] flex flex-row gap-3">
|
||||
<Link href="/admin/magazine/create">
|
||||
<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">
|
||||
<Link href="/admin/magazine/create" className="mx-3">
|
||||
<Button size="md" className="bg-[#F07C00] text-white">
|
||||
Tambah Majalah
|
||||
<AddIcon />
|
||||
</Button>
|
||||
</Link>
|
||||
{/* <Button
|
||||
size="md"
|
||||
color="primary"
|
||||
className="bg-[#F07C00] text-white"
|
||||
onPress={goGenerate}
|
||||
>
|
||||
<AddIcon />
|
||||
Generate Article
|
||||
</Button> */}
|
||||
</div>
|
||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-2">
|
||||
<MagazineTable />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -163,218 +163,209 @@ export default function MasterCategoryTable() {
|
|||
|
||||
return (
|
||||
<div className="overflow-x-hidden overflow-y-scroll">
|
||||
<div className="px-2 md:px-4 w-full">
|
||||
<div className="rounded-md my-5 px-5 py-2 shadow-lg bg-white dark:bg-[#18181b] flex flex-row gap-3">
|
||||
<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">
|
||||
<Button
|
||||
size="md"
|
||||
className="bg-[#F07C00] text-white"
|
||||
className="bg-[#F07C00] text-white mx-3"
|
||||
onPress={onOpen}
|
||||
>
|
||||
Tambah Kategori
|
||||
<AddIcon />
|
||||
</Button>
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="3xl">
|
||||
<ModalContent>
|
||||
{() => (
|
||||
<>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
Kategori Baru
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="flex flex-col gap-3"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Nama Kategori</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="title"
|
||||
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?.title && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title?.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Deskripsi</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Textarea
|
||||
type="text"
|
||||
id="description"
|
||||
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?.title && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title?.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm mt-3">Tag Terkait</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="tags"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
placeholder=""
|
||||
label=""
|
||||
value={tag}
|
||||
onValueChange={setTag}
|
||||
startContent={
|
||||
<div className="flex flex-row gap-1">
|
||||
{value.map((item, index) => (
|
||||
<Chip
|
||||
color="primary"
|
||||
key={index}
|
||||
className=""
|
||||
onClose={() => {
|
||||
const filteredTags = value.filter(
|
||||
(tag) => tag !== item
|
||||
);
|
||||
if (filteredTags.length === 0) {
|
||||
setError("tags", {
|
||||
type: "manual",
|
||||
message: "Tags tidak boleh kosong",
|
||||
});
|
||||
} else {
|
||||
clearErrors("tags");
|
||||
setValue(
|
||||
"tags",
|
||||
filteredTags as [
|
||||
string,
|
||||
...string[]
|
||||
]
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
if (tag.trim() !== "") {
|
||||
setValue("tags", [...value, tag.trim()]);
|
||||
setTag("");
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}}
|
||||
labelPlacement="outside"
|
||||
className="w-full h-fit"
|
||||
classNames={{
|
||||
inputWrapper: [
|
||||
"border-1 rounded-lg",
|
||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||
],
|
||||
}}
|
||||
variant="bordered"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm mt-3">Thumbnail</p>
|
||||
{files.length < 1 && (
|
||||
<Fragment>
|
||||
<div {...getRootProps({ className: "dropzone" })}>
|
||||
<input {...getInputProps()} />
|
||||
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
||||
<CloudUploadIcon />
|
||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||
Tarik file disini atau klik untuk upload.
|
||||
</h4>
|
||||
<div className=" text-xs text-muted-foreground">
|
||||
( Upload file dengan format .jpg, .jpeg, atau
|
||||
.png. Ukuran maksimal 100mb.)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{files.length > 0 && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<img
|
||||
src={URL.createObjectURL(files[0])}
|
||||
className="w-[30%]"
|
||||
alt="thumbnail"
|
||||
/>
|
||||
<Button
|
||||
className=" border-none rounded-full"
|
||||
variant="bordered"
|
||||
onClick={() => handleRemoveFile(files[0])}
|
||||
>
|
||||
<TimesIcon />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ModalFooter className="self-end grow items-end">
|
||||
<Button color="primary" type="submit">
|
||||
Simpan
|
||||
</Button>
|
||||
<Button
|
||||
color="danger"
|
||||
variant="light"
|
||||
onPress={onClose}
|
||||
>
|
||||
Tutup
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalBody>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-2">
|
||||
<CategoriesTable triggerRefresh={refresh} />
|
||||
</div>
|
||||
</div>
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="3xl">
|
||||
<ModalContent>
|
||||
{() => (
|
||||
<>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
Kategori Baru
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="flex flex-col gap-3"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Nama Kategori</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="title"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="title"
|
||||
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?.title && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title?.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Deskripsi</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Textarea
|
||||
type="text"
|
||||
id="description"
|
||||
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?.title && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.title?.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm mt-3">Tag Terkait</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="tags"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
placeholder=""
|
||||
label=""
|
||||
value={tag}
|
||||
onValueChange={setTag}
|
||||
startContent={
|
||||
<div className="flex flex-row gap-1">
|
||||
{value.map((item, index) => (
|
||||
<Chip
|
||||
color="primary"
|
||||
key={index}
|
||||
className=""
|
||||
onClose={() => {
|
||||
const filteredTags = value.filter(
|
||||
(tag) => tag !== item
|
||||
);
|
||||
if (filteredTags.length === 0) {
|
||||
setError("tags", {
|
||||
type: "manual",
|
||||
message: "Tags tidak boleh kosong",
|
||||
});
|
||||
} else {
|
||||
clearErrors("tags");
|
||||
setValue(
|
||||
"tags",
|
||||
filteredTags as [string, ...string[]]
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
if (tag.trim() !== "") {
|
||||
setValue("tags", [...value, tag.trim()]);
|
||||
setTag("");
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}}
|
||||
labelPlacement="outside"
|
||||
className="w-full h-fit"
|
||||
classNames={{
|
||||
inputWrapper: [
|
||||
"border-1 rounded-lg",
|
||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||
],
|
||||
}}
|
||||
variant="bordered"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm mt-3">Thumbnail</p>
|
||||
{files.length < 1 && (
|
||||
<Fragment>
|
||||
<div {...getRootProps({ className: "dropzone" })}>
|
||||
<input {...getInputProps()} />
|
||||
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
||||
<CloudUploadIcon />
|
||||
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||
Tarik file disini atau klik untuk upload.
|
||||
</h4>
|
||||
<div className=" text-xs text-muted-foreground">
|
||||
( Upload file dengan format .jpg, .jpeg, atau
|
||||
.png. Ukuran maksimal 100mb.)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{files.length > 0 && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<img
|
||||
src={URL.createObjectURL(files[0])}
|
||||
className="w-[30%]"
|
||||
alt="thumbnail"
|
||||
/>
|
||||
<Button
|
||||
className=" border-none rounded-full"
|
||||
variant="bordered"
|
||||
onClick={() => handleRemoveFile(files[0])}
|
||||
>
|
||||
<TimesIcon />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ModalFooter className="self-end grow items-end">
|
||||
<Button color="primary" type="submit">
|
||||
Simpan
|
||||
</Button>
|
||||
<Button color="danger" variant="light" onPress={onClose}>
|
||||
Tutup
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalBody>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,23 +6,21 @@ import Link from "next/link";
|
|||
|
||||
export default function MasterRolePage() {
|
||||
return (
|
||||
<div className="h-[96vh] overflow-x-hidden overflow-y-scroll gap-0 grid rounded-lg">
|
||||
<div className="px-4">
|
||||
<Card className="rounded-md my-5 pl-5 py-2">
|
||||
<Link href="/admin/master-role/create">
|
||||
<div className="overflow-x-hidden overflow-y-scroll">
|
||||
<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">
|
||||
<Link href="/admin/master-role/create" className="mx-3">
|
||||
<Button
|
||||
size="md"
|
||||
color="primary"
|
||||
className="bg-[#F07C00] text-white"
|
||||
>
|
||||
Peran Baru
|
||||
<AddIcon />
|
||||
New Role
|
||||
</Button>
|
||||
</Link>
|
||||
</Card>
|
||||
<Card className="rounded-md my-5">
|
||||
<MasterRoleTable />
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,23 +6,21 @@ import Link from "next/link";
|
|||
|
||||
export default function MasterUserPage() {
|
||||
return (
|
||||
<div className="h-[96vh] overflow-x-hidden overflow-y-scroll gap-0 grid rounded-lg">
|
||||
<div className="px-4">
|
||||
<Card className="rounded-md my-5 pl-5 py-2">
|
||||
<Link href="/admin/master-user/create">
|
||||
<div className="overflow-x-hidden overflow-y-scroll">
|
||||
<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">
|
||||
<Link href="/admin/master-user/create" className="mx-3">
|
||||
<Button
|
||||
size="md"
|
||||
color="primary"
|
||||
className="bg-[#F07C00] text-white"
|
||||
>
|
||||
Pengguna Baru
|
||||
<AddIcon />
|
||||
New User
|
||||
</Button>
|
||||
</Link>
|
||||
</Card>
|
||||
<Card className="rounded-md my-5">
|
||||
<MasterUserTable />
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,20 +6,18 @@ import Link from "next/link";
|
|||
export default function StaticPageGeneratorList() {
|
||||
return (
|
||||
<div className="overflow-x-hidden overflow-y-scroll rounded-lg">
|
||||
<div className="px-2 md:px-4 w-full">
|
||||
<div className="rounded-md mt-4 px-5 py-2 bg-white dark:bg-[#18181b] flex flex-row gap-3 shadow-lg">
|
||||
<Link href="/admin/static-page/create">
|
||||
<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">
|
||||
<Link href="/admin/static-page/create" className="mx-3">
|
||||
<Button
|
||||
size="md"
|
||||
color="primary"
|
||||
className="bg-[#F07C00] text-white"
|
||||
>
|
||||
Tambah Halaman
|
||||
<AddIcon />
|
||||
Create Page
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-2 mt-4">
|
||||
<StaticPageTable />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
"use client";
|
||||
|
||||
import { AdminLayout } from "@/components/layout/admin-layout";
|
||||
|
||||
export default function AdminPageLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<AdminLayout>
|
||||
{children}
|
||||
</AdminLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
"use client";
|
||||
import PasswordForm from "@/components/form/settings/password";
|
||||
import ProfileForm from "@/components/form/settings/profile";
|
||||
import { Tab, Tabs } from "@nextui-org/react";
|
||||
|
||||
export default function Settings() {
|
||||
return (
|
||||
<div className="w-full lg:w-[60%] p-5">
|
||||
<div className="flex flex-col bg-gray-100 dark:bg-stone-900 text-black dark:text-white rounded-md p-5">
|
||||
<Tabs aria-label="Tabs radius" radius="sm">
|
||||
<Tab key="profile" title="Profile">
|
||||
<ProfileForm />
|
||||
</Tab>
|
||||
<Tab key="music" title="Password">
|
||||
<PasswordForm />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,12 +1,22 @@
|
|||
"use client";
|
||||
import { HumasLayout } from "@/components/layout/humas-layout";
|
||||
import ListEnewsPolri from "@/components/table/tabel-emajalah-polri";
|
||||
import React from "react";
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
|
||||
export default function ListEnewsPage() {
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
}, []);
|
||||
|
||||
// Render
|
||||
if (!hasMounted) return null;
|
||||
return (
|
||||
<HumasLayout>
|
||||
<ListEnewsPolri />
|
||||
<Suspense>
|
||||
<ListEnewsPolri />
|
||||
</Suspense>
|
||||
</HumasLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
"use client";
|
||||
import { HumasLayout } from "@/components/layout/humas-layout";
|
||||
import EMagazineDetail from "@/components/main/detail/e-magazine-detail";
|
||||
import React from "react";
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
|
||||
export default function EnewsDetailPage() {
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setHasMounted(true);
|
||||
}, []);
|
||||
|
||||
// Render
|
||||
if (!hasMounted) return null;
|
||||
return (
|
||||
<HumasLayout>
|
||||
<EMagazineDetail />
|
||||
<Suspense>
|
||||
<EMagazineDetail />
|
||||
</Suspense>
|
||||
</HumasLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
|||
</head>
|
||||
<body
|
||||
className={clsx(
|
||||
"bg-background font-sans antialiased",
|
||||
"bg-background font-sans antialiased overflow-hidden",
|
||||
fontSans.variable
|
||||
)}
|
||||
>
|
||||
<NextIntlClientProvider locale={localeNow} messages={messages}>
|
||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
||||
<main className="">{children}</main>
|
||||
<main className="overflow-y-auto">{children}</main>
|
||||
</Providers>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -15,13 +15,27 @@ function CustomEditor(props) {
|
|||
props.onChange(data);
|
||||
}}
|
||||
config={{
|
||||
toolbar: [ 'heading', 'fontsize', 'bold', 'italic', 'link', 'numberedList', 'bulletedList', 'undo', 'redo', 'alignment', 'outdent', 'indent', 'blockQuote', 'insertTable', 'codeBlock', 'sourceEditing']
|
||||
toolbar: [
|
||||
"heading",
|
||||
"fontsize",
|
||||
"bold",
|
||||
"italic",
|
||||
"link",
|
||||
"numberedList",
|
||||
"bulletedList",
|
||||
"undo",
|
||||
"redo",
|
||||
"alignment",
|
||||
"outdent",
|
||||
"indent",
|
||||
"blockQuote",
|
||||
"insertTable",
|
||||
"codeBlock",
|
||||
"sourceEditing",
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomEditor;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ import { close, error, loading } from "@/config/swal";
|
|||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { getCategoryById } from "@/service/master-categories";
|
||||
import { saveManualContext } from "@/service/generate-article";
|
||||
import {
|
||||
saveManualContext,
|
||||
updateManualArticle,
|
||||
} from "@/service/generate-article";
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
|
|
@ -52,6 +55,17 @@ const categorySchema = z.object({
|
|||
value: z.number(),
|
||||
});
|
||||
|
||||
interface DiseData {
|
||||
id: number;
|
||||
articleBody: string;
|
||||
title: string;
|
||||
metaTitle: string;
|
||||
description: string;
|
||||
metaDescription: string;
|
||||
mainKeyword: string;
|
||||
additionalKeywords: string;
|
||||
}
|
||||
|
||||
const createArticleSchema = z.object({
|
||||
title: z.string().min(2, {
|
||||
message: "Judul harus diisi",
|
||||
|
|
@ -81,6 +95,7 @@ export default function CreateArticleForm() {
|
|||
const [tag, setTag] = useState("");
|
||||
const [thumbnailImg, setThumbnailImg] = useState<File[]>([]);
|
||||
const [selectedMainImage, setSelectedMainImage] = useState<number>();
|
||||
const [diseData, setDiseData] = useState<DiseData>();
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
|
|
@ -148,6 +163,12 @@ export default function CreateArticleForm() {
|
|||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (useAi === false) {
|
||||
setValue("description", "");
|
||||
}
|
||||
}, [useAi]);
|
||||
|
||||
function removeImgTags(htmlString: string) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(String(htmlString), "text/html");
|
||||
|
|
@ -160,26 +181,50 @@ export default function CreateArticleForm() {
|
|||
const saveArticleToDise = async (
|
||||
values: z.infer<typeof createArticleSchema>
|
||||
) => {
|
||||
const request = {
|
||||
title: values.title,
|
||||
articleBody: removeImgTags(values.description),
|
||||
metaDescription: values.title,
|
||||
metaTitle: values.title,
|
||||
mainKeyword: values.title,
|
||||
additionalKeywords: values.title,
|
||||
createdBy: "345",
|
||||
style: "Informational",
|
||||
projectId: 2,
|
||||
clientId: "humasClientIdtest",
|
||||
lang: "id",
|
||||
};
|
||||
if (useAi) {
|
||||
const request = {
|
||||
id: diseData?.id,
|
||||
title: values.title,
|
||||
articleBody: removeImgTags(values.description),
|
||||
metaDescription: diseData?.metaDescription,
|
||||
metaTitle: diseData?.metaTitle,
|
||||
mainKeyword: diseData?.mainKeyword,
|
||||
additionalKeywords: diseData?.additionalKeywords,
|
||||
createdBy: "345",
|
||||
style: "Informational",
|
||||
projectId: 2,
|
||||
clientId: "humasClientIdtest",
|
||||
lang: "id",
|
||||
};
|
||||
const res = await updateManualArticle(request);
|
||||
if (res.error) {
|
||||
error(res.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const res = await saveManualContext(request);
|
||||
if (res.error) {
|
||||
res.message;
|
||||
return 0;
|
||||
return diseData?.id;
|
||||
} else {
|
||||
const request = {
|
||||
title: values.title,
|
||||
articleBody: removeImgTags(values.description),
|
||||
metaDescription: values.title,
|
||||
metaTitle: values.title,
|
||||
mainKeyword: values.title,
|
||||
additionalKeywords: values.title,
|
||||
createdBy: "345",
|
||||
style: "Informational",
|
||||
projectId: 2,
|
||||
clientId: "humasClientIdtest",
|
||||
lang: "id",
|
||||
};
|
||||
|
||||
const res = await saveManualContext(request);
|
||||
if (res.error) {
|
||||
res.message;
|
||||
return 0;
|
||||
}
|
||||
return res?.data?.data?.id;
|
||||
}
|
||||
return res?.data?.data?.id;
|
||||
};
|
||||
|
||||
const save = async (values: z.infer<typeof createArticleSchema>) => {
|
||||
|
|
@ -192,7 +237,7 @@ export default function CreateArticleForm() {
|
|||
tags: values.tags.join(","),
|
||||
description: htmlToString(removeImgTags(values.description)),
|
||||
htmlDescription: removeImgTags(values.description),
|
||||
// aiArticleId: saveArticleToDise(values),
|
||||
aiArticleId: await saveArticleToDise(values),
|
||||
};
|
||||
|
||||
const response = await createArticle(formData);
|
||||
|
|
@ -418,7 +463,13 @@ export default function CreateArticleForm() {
|
|||
|
||||
{useAi && (
|
||||
<GenerateSingleArticleForm
|
||||
content={(data) => setValue("description", data)}
|
||||
content={(data) => {
|
||||
setDiseData(data);
|
||||
setValue(
|
||||
"description",
|
||||
data?.articleBody ? data?.articleBody : ""
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -427,18 +478,7 @@ export default function CreateArticleForm() {
|
|||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
// <CustomEditor onChange={onChange} initialData={value} />
|
||||
// <JoditEditor
|
||||
// ref={editor}
|
||||
// value={value}
|
||||
// onChange={onChange}
|
||||
// className="dark:text-black"
|
||||
// />
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={value}
|
||||
/>
|
||||
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
)}
|
||||
/>
|
||||
{errors?.description && (
|
||||
|
|
|
|||
|
|
@ -79,6 +79,17 @@ const createArticleSchema = z.object({
|
|||
}), // Array berisi string
|
||||
});
|
||||
|
||||
interface DiseData {
|
||||
id: number;
|
||||
articleBody: string;
|
||||
title: string;
|
||||
metaTitle: string;
|
||||
description: string;
|
||||
metaDescription: string;
|
||||
mainKeyword: string;
|
||||
additionalKeywords: string;
|
||||
}
|
||||
|
||||
export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||
const { isDetail } = props;
|
||||
const params = useParams();
|
||||
|
|
@ -94,6 +105,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
const [detailfiles, setDetailFiles] = useState<any>([]);
|
||||
const [mainImage, setMainImage] = useState(0);
|
||||
const [thumbnail, setThumbnail] = useState("");
|
||||
const [diseId, setDiseId] = useState(0);
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
|
|
@ -137,6 +149,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
setValue("description", data?.htmlDescription);
|
||||
setValue("tags", data?.tags ? data.tags.split(",") : []);
|
||||
setThumbnail(data?.thumbnailUrl);
|
||||
setDiseId(data?.aiArticleId);
|
||||
setDetailFiles(data?.files);
|
||||
|
||||
setupInitCategory(data?.categories);
|
||||
|
|
@ -205,7 +218,6 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
description: htmlToString(values.description),
|
||||
htmlDescription: values.description,
|
||||
};
|
||||
console.log("vals", formData);
|
||||
const response = await updateArticle(String(id), formData);
|
||||
|
||||
if (response?.error) {
|
||||
|
|
@ -345,7 +357,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<div className="w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1">
|
||||
{isDetail && <GetSeoScore id="1961" />}
|
||||
{isDetail && <GetSeoScore id={String(diseId)} />}
|
||||
<p className="text-sm">Judul</p>
|
||||
<Controller
|
||||
control={control}
|
||||
|
|
@ -409,7 +421,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
|
||||
{useAi && (
|
||||
<GenerateSingleArticleForm
|
||||
content={(data) => setValue("description", data)}
|
||||
content={(data) => setValue("description", data?.articleBody)}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -417,7 +429,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
render={({ field: { onChange, value } }) =>
|
||||
// <CustomEditor onChange={onChange} initialData={value} />
|
||||
// <JoditEditor
|
||||
// ref={editor}
|
||||
|
|
@ -426,16 +438,12 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
|||
// config={{ readonly: isDetail }}
|
||||
// className="dark:text-black"
|
||||
// />
|
||||
isDetail ?
|
||||
<ViewEditor
|
||||
initialData={value}
|
||||
/>
|
||||
:
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
initialData={value}
|
||||
/>
|
||||
)}
|
||||
isDetail ? (
|
||||
<ViewEditor initialData={value} />
|
||||
) : (
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
{errors?.description && (
|
||||
<p className="text-red-400 text-sm mb-3">
|
||||
|
|
|
|||
|
|
@ -60,8 +60,19 @@ const articleSize = [
|
|||
},
|
||||
];
|
||||
|
||||
interface DiseData {
|
||||
id: number;
|
||||
articleBody: string;
|
||||
title: string;
|
||||
metaTitle: string;
|
||||
description: string;
|
||||
metaDescription: string;
|
||||
mainKeyword: string;
|
||||
additionalKeywords: string;
|
||||
}
|
||||
|
||||
export default function GenerateSingleArticleForm(props: {
|
||||
content: (data: string) => void;
|
||||
content: (data: DiseData) => void;
|
||||
}) {
|
||||
const [selectedWritingSyle, setSelectedWritingStyle] =
|
||||
useState("Informational");
|
||||
|
|
@ -162,14 +173,23 @@ export default function GenerateSingleArticleForm(props: {
|
|||
const getArticleDetail = async () => {
|
||||
if (selectedId) {
|
||||
const res = await getDetailArticle(selectedId);
|
||||
const data = res?.data?.data?.articleBody;
|
||||
checkArticleStatus(data);
|
||||
if (data !== null) {
|
||||
const data = res?.data?.data;
|
||||
checkArticleStatus(data?.articleBody);
|
||||
if (data?.articleBody !== null) {
|
||||
setIsLoading(false);
|
||||
props.content(data);
|
||||
} else {
|
||||
setIsLoading(true);
|
||||
props.content("");
|
||||
props.content({
|
||||
id: data?.id,
|
||||
articleBody: "",
|
||||
title: "",
|
||||
metaTitle: "",
|
||||
description: "",
|
||||
metaDescription: "",
|
||||
additionalKeywords: "",
|
||||
mainKeyword: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useEffect, useRef, useState } from "react";
|
|||
export default function GetSeoScore(props: { id: string }) {
|
||||
useEffect(() => {
|
||||
fetchSeoScore();
|
||||
}, []);
|
||||
}, [props.id]);
|
||||
|
||||
const [totalScoreSEO, setTotalScoreSEO] = useState();
|
||||
const [errorSEO, setErrorSEO] = useState<any>([]);
|
||||
|
|
@ -16,9 +16,9 @@ export default function GetSeoScore(props: { id: string }) {
|
|||
const [optimizedSEO, setOptimizedSEO] = useState<any>([]);
|
||||
|
||||
const fetchSeoScore = async () => {
|
||||
const res = await getSeoScore("1931");
|
||||
const res = await getSeoScore(props?.id);
|
||||
if (res.error) {
|
||||
error(res.message);
|
||||
// error(res.message);
|
||||
return false;
|
||||
}
|
||||
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
|
||||
|
|
@ -42,97 +42,103 @@ export default function GetSeoScore(props: { id: string }) {
|
|||
<div className="overflow-y-auto my-2">
|
||||
<div className="text-black flex flex-col rounded-md gap-3">
|
||||
<p className="font-semibold text-lg"> SEO Score</p>
|
||||
<div className="flex flex-row gap-5 w-full">
|
||||
<CircularProgress
|
||||
aria-label=""
|
||||
color="warning"
|
||||
showValueLabel={true}
|
||||
size="lg"
|
||||
value={Number(totalScoreSEO) * 100}
|
||||
/>
|
||||
<div>
|
||||
{/* <ApexChartDonut value={Number(totalScoreSEO) * 100} /> */}
|
||||
</div>
|
||||
<div className="flex flex-row gap-5">
|
||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-red-500 rounded-lg">
|
||||
{/* <TimesIcon size={15} className="text-danger" /> */}
|
||||
Error : {errorSEO.length || 0}
|
||||
{totalScoreSEO ? (
|
||||
<div className="flex flex-row gap-5 w-full">
|
||||
<CircularProgress
|
||||
aria-label=""
|
||||
color="warning"
|
||||
showValueLabel={true}
|
||||
size="lg"
|
||||
value={Number(totalScoreSEO) * 100}
|
||||
/>
|
||||
<div>
|
||||
{/* <ApexChartDonut value={Number(totalScoreSEO) * 100} /> */}
|
||||
</div>
|
||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-yellow-500 rounded-lg">
|
||||
{/* <p className="text-warning w-[15px] h-[15px] text-center mt-[-10px]">
|
||||
<div className="flex flex-row gap-5">
|
||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-red-500 rounded-lg">
|
||||
{/* <TimesIcon size={15} className="text-danger" /> */}
|
||||
Error : {errorSEO.length || 0}
|
||||
</div>
|
||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-yellow-500 rounded-lg">
|
||||
{/* <p className="text-warning w-[15px] h-[15px] text-center mt-[-10px]">
|
||||
!
|
||||
</p> */}
|
||||
Warning : {warningSEO.length || 0}
|
||||
</div>
|
||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-green-500 rounded-lg">
|
||||
{/* <CheckIcon size={15} className="text-success" /> */}
|
||||
Optimize : {optimizedSEO.length || 0}
|
||||
Warning : {warningSEO.length || 0}
|
||||
</div>
|
||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-green-500 rounded-lg">
|
||||
{/* <CheckIcon size={15} className="text-success" /> */}
|
||||
Optimize : {optimizedSEO.length || 0}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion
|
||||
variant="splitted"
|
||||
itemClasses={{
|
||||
base: "!bg-transparent",
|
||||
title: "text-black",
|
||||
}}
|
||||
>
|
||||
<AccordionItem
|
||||
key="1"
|
||||
aria-label="Error"
|
||||
// startContent={<TimesIcon size={20} className="text-danger" />}
|
||||
title={`${errorSEO?.length || 0} Errors`}
|
||||
) : (
|
||||
"Belum ada Data"
|
||||
)}
|
||||
{totalScoreSEO && (
|
||||
<Accordion
|
||||
variant="splitted"
|
||||
itemClasses={{
|
||||
base: "!bg-transparent",
|
||||
title: "text-black",
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{errorSEO?.map((item: any) => (
|
||||
<p
|
||||
key={item}
|
||||
className="w-full border border-red-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="2"
|
||||
aria-label="Warning"
|
||||
// startContent={
|
||||
// <p className="text-warning w-[20px] h-[20px] text-center mt-[-10px]">
|
||||
// !
|
||||
// </p>
|
||||
// }
|
||||
title={`${warningSEO?.length || 0} Warnings`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{warningSEO?.map((item: any) => (
|
||||
<p
|
||||
key={item}
|
||||
className="w-full border border-yellow-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="3"
|
||||
aria-label="Optimized"
|
||||
// startContent={<CheckIcon size={20} className="text-success" />}
|
||||
title={`${optimizedSEO?.length || 0} Optimized`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{optimizedSEO?.map((item: any) => (
|
||||
<p
|
||||
key={item}
|
||||
className="w-full border border-green-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
<AccordionItem
|
||||
key="1"
|
||||
aria-label="Error"
|
||||
// startContent={<TimesIcon size={20} className="text-danger" />}
|
||||
title={`${errorSEO?.length || 0} Errors`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{errorSEO?.map((item: any) => (
|
||||
<p
|
||||
key={item}
|
||||
className="w-full border border-red-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="2"
|
||||
aria-label="Warning"
|
||||
// startContent={
|
||||
// <p className="text-warning w-[20px] h-[20px] text-center mt-[-10px]">
|
||||
// !
|
||||
// </p>
|
||||
// }
|
||||
title={`${warningSEO?.length || 0} Warnings`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{warningSEO?.map((item: any) => (
|
||||
<p
|
||||
key={item}
|
||||
className="w-full border border-yellow-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="3"
|
||||
aria-label="Optimized"
|
||||
// startContent={<CheckIcon size={20} className="text-success" />}
|
||||
title={`${optimizedSEO?.length || 0} Optimized`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
{optimizedSEO?.map((item: any) => (
|
||||
<p
|
||||
key={item}
|
||||
className="w-full border border-green-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||
>
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -101,116 +101,7 @@ export default function Login() {
|
|||
};
|
||||
|
||||
return (
|
||||
// <HumasLayout>
|
||||
// <div className="bg-white text-black md:flex px-0 md:px-2 lg:px-5">
|
||||
// <div className="w-auto md:w-1/2 p-2 md:p-5 lg:p-10 space-y-5">
|
||||
// <div className="text-xl font-bold">
|
||||
// Selamat Datang di Portal Resmi Humas Polri
|
||||
// </div>
|
||||
// <div>
|
||||
// <Input
|
||||
// classNames={{
|
||||
// input: ["w-full", "bg-transparent", "!text-black"],
|
||||
// mainWrapper: ["w-full", "bg-transparent"],
|
||||
// innerWrapper: ["bg-transparent"],
|
||||
// label: ["!text-black", "font-semibold"],
|
||||
// inputWrapper: [
|
||||
// "bg-transparent",
|
||||
// "dark:bg-transparent",
|
||||
// "hover:bg-transparent",
|
||||
// "dark:hover:bg-transparent",
|
||||
// "group-data-[focused=true]:bg-transparent",
|
||||
// "dark:group-data-[focused=true]:bg-transaparent",
|
||||
// "group-data-[focused=false]:bg-transparent",
|
||||
// "focus-within:!bg-transparent",
|
||||
// ],
|
||||
// }}
|
||||
// isRequired
|
||||
// type="text"
|
||||
// label="Username"
|
||||
// placeholder="Masukkan username anda!"
|
||||
// variant="underlined"
|
||||
// onChange={(e: any) => {
|
||||
// setValUsername(e.target.value.trim());
|
||||
// }}
|
||||
// onPaste={(e: any) => {
|
||||
// setValUsername(e.target.value.trim());
|
||||
// }}
|
||||
// onCopy={(e: any) => {
|
||||
// setValUsername(e.target.value.trim());
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// <div>
|
||||
// <Input
|
||||
// classNames={{
|
||||
// input: ["w-full", "bg-transparent", "!text-black"],
|
||||
// mainWrapper: ["w-full", "bg-transparent"],
|
||||
// innerWrapper: ["bg-transparent"],
|
||||
// label: ["!text-black", "font-semibold"],
|
||||
// inputWrapper: [
|
||||
// "bg-transparent",
|
||||
// "dark:bg-transparent",
|
||||
// "hover:bg-transparent",
|
||||
// "dark:hover:bg-transparent",
|
||||
// "group-data-[focused=true]:bg-transparent",
|
||||
// "dark:group-data-[focused=true]:bg-transaparent",
|
||||
// "group-data-[focused=false]:bg-transparent",
|
||||
// "focus-within:!bg-transparent",
|
||||
// ],
|
||||
// }}
|
||||
// isRequired
|
||||
// endContent={
|
||||
// <button
|
||||
// className="focus:outline-none"
|
||||
// type="button"
|
||||
// onClick={toggleVisibility}
|
||||
// >
|
||||
// {isVisible ? (
|
||||
// <EyeSlashFilledIcon className="text-2xl text-default-400 pointer-events-none" />
|
||||
// ) : (
|
||||
// <EyeFilledIcon className="text-2xl text-default-400 pointer-events-none" />
|
||||
// )}
|
||||
// </button>
|
||||
// }
|
||||
// type={isVisible ? "text" : "password"}
|
||||
// label="Password"
|
||||
// placeholder="Masukkan password anda"
|
||||
// variant="underlined"
|
||||
// onChange={(event) => setPassword(event.target.value)}
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// <div>
|
||||
// <Button
|
||||
// size="lg"
|
||||
// className="w-full bg-[#DD8306] rounded-md font-semibold"
|
||||
// onPress={onSubmit}
|
||||
// >
|
||||
// Login
|
||||
// </Button>
|
||||
// </div>
|
||||
// <div className="flex justify-center text-xs font-medium py-3">
|
||||
// Don't have account? Register Now
|
||||
// </div>
|
||||
// <div>
|
||||
// <Link href={`/form-permohonan-informasi`}>
|
||||
// <Button
|
||||
// size="lg"
|
||||
// variant="bordered"
|
||||
// className="w-full text-[#DD8306] borde-2 border-[#DD8306] rounded-md font-semibold"
|
||||
// >
|
||||
// Register
|
||||
// </Button>
|
||||
// </Link>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="hidden sm:flex w-1/2 items-center justify-center p-10">
|
||||
// <img src="/login.png" alt="logo" />
|
||||
// </div>
|
||||
// </div>
|
||||
// </HumasLayout>
|
||||
<div className="flex flex-row">
|
||||
<div className="flex flex-row h-screen">
|
||||
<div
|
||||
style={{
|
||||
backgroundImage: "url(headerbanner1.png)",
|
||||
|
|
@ -220,9 +111,11 @@ export default function Login() {
|
|||
}}
|
||||
className="h-screen hidden md:block md:w-3/5"
|
||||
>
|
||||
<img src="divhumas.png" className="w-[120px]" />
|
||||
<Link href="/">
|
||||
<img src="divhumas.png" className="w-[120px]" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="bg-[#1F1A17] w-2/5 md:px-24 md:py-36 flex flex-col">
|
||||
<div className="bg-[#1F1A17] w-full md:w-2/5 p-8 md:px-24 md:py-36 flex flex-col">
|
||||
<p className="text-[72px] text-[#DD8306] font-semibold mb-10">Login</p>
|
||||
<p className="my-2 text-white">Username</p>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -35,12 +35,12 @@ import {
|
|||
} from "@/components/icons/globals";
|
||||
import { createMagazine, uploadMagazineFile } from "@/service/magazine";
|
||||
|
||||
// const CustomEditor = dynamic(
|
||||
// () => {
|
||||
// return import("@/components/editor/custom-editor");
|
||||
// },
|
||||
// { ssr: false }
|
||||
// );
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -375,13 +375,7 @@ export default function NewCreateMagazineForm() {
|
|||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
// <CustomEditor onChange={onChange} initialData={value} />
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className="dark:text-black"
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
)}
|
||||
/>
|
||||
{errors?.description && (
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ import {
|
|||
uploadMagazineFile,
|
||||
} from "@/service/magazine";
|
||||
|
||||
// const CustomEditor = dynamic(
|
||||
// () => {
|
||||
// return import("@/components/editor/custom-editor");
|
||||
// },
|
||||
// { ssr: false }
|
||||
// );
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -445,14 +445,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
|||
control={control}
|
||||
name="description"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
// <CustomEditor onChange={onChange} initialData={value} />
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={value}
|
||||
config={{ readonly: isDetail }}
|
||||
onChange={onChange}
|
||||
className="dark:text-black"
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
)}
|
||||
/>
|
||||
{errors?.description && (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
"use client";
|
||||
import { FormEvent, Fragment, useEffect, useRef, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Input, Textarea } from "@nextui-org/input";
|
||||
|
||||
const formSchema = z.object({
|
||||
password: z.string().min(2, {
|
||||
message: "Judul harus diisi",
|
||||
}),
|
||||
passwordConf: z.string().min(2, {
|
||||
message: "Slug harus diisi",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function PasswordForm() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
const formOptions = {
|
||||
resolver: zodResolver(formSchema),
|
||||
};
|
||||
type UserSettingSchema = z.infer<typeof formSchema>;
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
getValues,
|
||||
watch,
|
||||
setError,
|
||||
clearErrors,
|
||||
} = useForm<UserSettingSchema>(formOptions);
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
console.log("values");
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="flex flex-col gap-3 " onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Password</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="password"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="password"
|
||||
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?.password && (
|
||||
<p className="text-red-400 text-sm mb-3">
|
||||
{errors.password?.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Konfirmasi Password</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="passwordConf"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="passwordConf"
|
||||
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?.passwordConf && (
|
||||
<p className="text-red-400 text-sm mb-3">
|
||||
{errors.passwordConf?.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
"use client";
|
||||
import { FormEvent, Fragment, useEffect, useRef, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Input, Textarea } from "@nextui-org/input";
|
||||
import { Button } from "@nextui-org/button";
|
||||
import { Radio, RadioGroup } from "@nextui-org/react";
|
||||
|
||||
const formSchema = z.object({
|
||||
fullname: z.string().min(1, {
|
||||
message: "Harus diisi",
|
||||
}),
|
||||
username: z.string().min(1, {
|
||||
message: "Harus diisi",
|
||||
}),
|
||||
email: z
|
||||
.string()
|
||||
.email({
|
||||
message: "Email tidak valid",
|
||||
})
|
||||
.min(2, {
|
||||
message: "Harus diisi",
|
||||
}),
|
||||
nrp: z.string().min(1, {
|
||||
message: "Harus diisi",
|
||||
}),
|
||||
gender: z.string().min(1, {
|
||||
message: "Harus diisi",
|
||||
}),
|
||||
phoneNumber: z.string().min(1, {
|
||||
message: "Harus diisi",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function ProfileForm() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
const formOptions = {
|
||||
resolver: zodResolver(formSchema),
|
||||
};
|
||||
type UserSettingSchema = z.infer<typeof formSchema>;
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
getValues,
|
||||
watch,
|
||||
setError,
|
||||
clearErrors,
|
||||
} = useForm<UserSettingSchema>(formOptions);
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
console.log("values");
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="flex flex-col gap-3 " onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Nama Lengkap</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="fullname"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="fullname"
|
||||
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?.fullname && (
|
||||
<p className="text-red-400 text-sm mb-3">
|
||||
{errors.fullname?.message}
|
||||
</p>
|
||||
)}
|
||||
</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">
|
||||
<p className="text-sm">Email</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="email"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="email"
|
||||
id="email"
|
||||
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?.email && (
|
||||
<p className="text-red-400 text-sm mb-3">{errors.email?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">NRP</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="nrp"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="number"
|
||||
id="nrp"
|
||||
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?.nrp && (
|
||||
<p className="text-red-400 text-sm mb-3">{errors.nrp?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Gender</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="gender"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<RadioGroup
|
||||
label=""
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Radio value="male">Laki-laki</Radio>
|
||||
<Radio value="female">Perempuan</Radio>
|
||||
</RadioGroup>
|
||||
)}
|
||||
/>
|
||||
{errors?.gender && (
|
||||
<p className="text-red-400 text-sm mb-3">{errors.gender?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<Button color="primary" type="submit">
|
||||
Simpan
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
@ -148,3 +148,49 @@ export const FileIcon = ({
|
|||
/>
|
||||
</svg>
|
||||
);
|
||||
export const UserProfileIcon = ({
|
||||
size,
|
||||
height = 24,
|
||||
width = 24,
|
||||
fill = "currentColor",
|
||||
...props
|
||||
}: IconSvgProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size || width}
|
||||
height={size || height}
|
||||
{...props}
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11 7c0 1.66-1.34 3-3 3S5 8.66 5 7s1.34-3 3-3s3 1.34 3 3"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M16 8c0 4.42-3.58 8-8 8s-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8M4 13.75C4.16 13.484 5.71 11 7.99 11c2.27 0 3.83 2.49 3.99 2.75A6.98 6.98 0 0 0 14.99 8c0-3.87-3.13-7-7-7s-7 3.13-7 7c0 2.38 1.19 4.49 3.01 5.75"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export const SettingsIcon = ({
|
||||
size,
|
||||
height = 24,
|
||||
width = 24,
|
||||
fill = "currentColor",
|
||||
...props
|
||||
}: IconSvgProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size || width}
|
||||
height={size || height}
|
||||
{...props}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m9.25 22l-.4-3.2q-.325-.125-.612-.3t-.563-.375L4.7 19.375l-2.75-4.75l2.575-1.95Q4.5 12.5 4.5 12.338v-.675q0-.163.025-.338L1.95 9.375l2.75-4.75l2.975 1.25q.275-.2.575-.375t.6-.3l.4-3.2h5.5l.4 3.2q.325.125.613.3t.562.375l2.975-1.25l2.75 4.75l-2.575 1.95q.025.175.025.338v.674q0 .163-.05.338l2.575 1.95l-2.75 4.75l-2.95-1.25q-.275.2-.575.375t-.6.3l-.4 3.2zm2.8-6.5q1.45 0 2.475-1.025T15.55 12t-1.025-2.475T12.05 8.5q-1.475 0-2.488 1.025T8.55 12t1.013 2.475T12.05 15.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import "swiper/css";
|
|||
import "swiper/css/pagination";
|
||||
import "swiper/css/effect-fade";
|
||||
import "swiper/css/navigation";
|
||||
import { getListMagazine } from "@/service/magazine";
|
||||
|
||||
export default function ENewsPolri() {
|
||||
const [article, setArticle] = useState<any>([]);
|
||||
|
|
@ -20,7 +21,7 @@ export default function ENewsPolri() {
|
|||
async function getArticle() {
|
||||
const req = { page: 1, search: "", limit: "10" };
|
||||
|
||||
const response = await getListArticle(req);
|
||||
const response = await getListMagazine(req);
|
||||
setArticle(response?.data?.data);
|
||||
}
|
||||
getArticle();
|
||||
|
|
@ -68,13 +69,13 @@ export default function ENewsPolri() {
|
|||
<SwiperSlide key={newsItem.id}>
|
||||
<Card isFooterBlurred radius="lg" className="border-none">
|
||||
<img
|
||||
alt="headernews"
|
||||
src="/headernews.png"
|
||||
className="h-[25vh] object-cover rounded-none"
|
||||
alt="thumbnail"
|
||||
src={newsItem?.thumbnailUrl ? "" : "/no-image.jpg"}
|
||||
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">
|
||||
<div className="text-white">
|
||||
<Link href={`news/detail/${newsItem.id}`}>
|
||||
<Link href={`/e-majalah-polri/detail/${newsItem.id}`}>
|
||||
<p className="text-left font-semibold text-sm">
|
||||
{textEllipsis(newsItem.title, 40)}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export default function SidebarNav() {
|
|||
onClick={() => setSelectedTab("media")}
|
||||
className={
|
||||
selectedTab === "media"
|
||||
? "text-black border-b-3 border-red-400 cursor-pointer py-2"
|
||||
? "text-black dark:text-white border-b-3 border-red-400 cursor-pointer py-2"
|
||||
: "text-slate-300 cursor-pointer py-2"
|
||||
}
|
||||
>
|
||||
|
|
@ -40,7 +40,7 @@ export default function SidebarNav() {
|
|||
onClick={() => setSelectedTab("video")}
|
||||
className={
|
||||
selectedTab === "video"
|
||||
? "text-black border-b-3 border-red-400 cursor-pointer py-2"
|
||||
? "text-black dark:text-white border-b-3 border-red-400 cursor-pointer py-2"
|
||||
: "text-slate-300 cursor-pointer py-2"
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ export const AdminLayout = ({ children }: Props) => {
|
|||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<div className="h-screen flex items-center flex-row">
|
||||
<div className="!h-screen flex items-center flex-row !overflow-y-hidden">
|
||||
<Sidebar sidebarData={isOpen} updateSidebarData={updateSidebarData} />
|
||||
<div className={`w-full h-full flex flex-col`}>
|
||||
<div className={`w-full h-full flex flex-col overflow-hidden`}>
|
||||
<Breadcrumb />
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ interface Props {
|
|||
|
||||
export const HumasLayout = ({ children }: Props) => {
|
||||
return (
|
||||
<section className="flex flex-col">
|
||||
<section className="flex flex-col overflow-auto">
|
||||
<NavbarHumas size="sm" />
|
||||
<NavbarHumas size="lg" />
|
||||
<NewsTicker />
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ 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;
|
||||
|
|
@ -114,13 +115,13 @@ const sideBarDummyData = [
|
|||
childModule: null,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Majalah",
|
||||
moduleId: 652,
|
||||
moduleName: "Apps",
|
||||
modulePathUrl: "/admin/magazine",
|
||||
id: 30,
|
||||
name: "Category",
|
||||
moduleId: 654,
|
||||
moduleName: "Master",
|
||||
modulePathUrl: "/admin/master-category",
|
||||
parentId: -1,
|
||||
icon: <MagazineIcon />,
|
||||
icon: <MasterCategoryIcon size={22} />,
|
||||
position: 1,
|
||||
statusId: 1,
|
||||
childMenu: [],
|
||||
|
|
@ -128,19 +129,20 @@ const sideBarDummyData = [
|
|||
childModule: null,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Static Page",
|
||||
id: 4,
|
||||
name: "Majalah",
|
||||
moduleId: 652,
|
||||
moduleName: "Dashboard",
|
||||
modulePathUrl: "/admin/static-page",
|
||||
moduleName: "Apps",
|
||||
modulePathUrl: "/admin/magazine",
|
||||
parentId: -1,
|
||||
icon: <StaticPageIcon />,
|
||||
icon: <MagazineIcon size={23} />,
|
||||
position: 1,
|
||||
statusId: 1,
|
||||
childMenu: [],
|
||||
statusName: "Active",
|
||||
childModule: null,
|
||||
},
|
||||
|
||||
// {
|
||||
// id: 4,
|
||||
// name: "E-Magazine",
|
||||
|
|
@ -199,13 +201,13 @@ const sideBarDummyData = [
|
|||
// childModule: null,
|
||||
// },
|
||||
{
|
||||
id: 30,
|
||||
name: "Master Category",
|
||||
moduleId: 654,
|
||||
moduleName: "Master",
|
||||
modulePathUrl: "/admin/master-category",
|
||||
id: 11,
|
||||
name: "Master Static Page",
|
||||
moduleId: 652,
|
||||
moduleName: "Dashboard",
|
||||
modulePathUrl: "/admin/static-page",
|
||||
parentId: -1,
|
||||
icon: <MasterCategoryIcon />,
|
||||
icon: <StaticPageIcon size={24} />,
|
||||
position: 1,
|
||||
statusId: 1,
|
||||
childMenu: [],
|
||||
|
|
@ -249,6 +251,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
const [sidebarMenu, setSidebarMenu] = useState<SidebarMenuTask[]>();
|
||||
const { isOpen, toggleSidebar } = useSidebar();
|
||||
const token = Cookies.get("access_token");
|
||||
const fullname = Cookies.get("ufne");
|
||||
const isAuthenticated = Cookies.get("is_authenticated");
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -270,12 +273,12 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={`hidden md:flex h-full flex-grow ${
|
||||
className={`hidden md:flex h-screen flex-grow ${
|
||||
isOpen ? "min-w-[240px]" : "min-w-[80px]"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={` flex h-full flex-col rounded-lg p-4 mb-0 bg-gray-100 dark:bg-stone-900 z-40 transition-width !ease-in-out justify-between ${
|
||||
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]"
|
||||
}`}
|
||||
>
|
||||
|
|
@ -304,7 +307,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
</Link>
|
||||
{isOpen && (
|
||||
<button
|
||||
className="w-5 h-5 text-zinc-400 dark:text-zinc-400 z-50 border-1 border-zinc-400 rounded-full flex justify-center items-center"
|
||||
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 />
|
||||
|
|
@ -318,7 +321,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
list.isGroup ? (
|
||||
<p
|
||||
key={list}
|
||||
className={`font-bold mr-4 ${
|
||||
className={`font-bold mr-4 text-white ${
|
||||
!isOpen ? "text-center" : ""
|
||||
}`}
|
||||
>
|
||||
|
|
@ -338,8 +341,8 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
<div
|
||||
className={`px-3.5 py-2 mr-4 rounded-lg flex flex-row gap-2 ${
|
||||
pathname.includes(list.modulePathUrl)
|
||||
? "bg-black dark:bg-white text-white dark:text-black font-bold"
|
||||
: "text-black dark:text-white hover:bg-stone-900 hover:text-white dark:hover:bg-gray-200 dark:hover:text-black"
|
||||
? "bg-white text-black font-bold"
|
||||
: "text-white hover:bg-gray-200 hover:text-black"
|
||||
}`}
|
||||
>
|
||||
{list.icon} {isOpen && list.name}
|
||||
|
|
@ -356,8 +359,8 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
<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-600 dark:bg-zinc-300 text-zinc-300 dark:text-zinc-500 font-bold"
|
||||
: "text-zinc-600 dark:text-zinc-400"
|
||||
? "bg-zinc-300 text-zinc-500 font-bold hover:text-black"
|
||||
: "text-zinc-400 hover:text-black"
|
||||
}`}
|
||||
>
|
||||
{list.icon} {isOpen && list.name}
|
||||
|
|
@ -399,23 +402,50 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div
|
||||
className={`cursor-pointer flex flex-row ${
|
||||
className={`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`}
|
||||
} gap-2 items-center text-white `}
|
||||
>
|
||||
<ThemeSwitch />
|
||||
{isOpen && "Theme"}
|
||||
</div>
|
||||
{isOpen ? (
|
||||
<div className="flex flex-row gap-3">
|
||||
<Image
|
||||
src="/pengaduan.png"
|
||||
width={72}
|
||||
height={72}
|
||||
alt="profile"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<a className="cursor-pointer">Nama User</a>
|
||||
<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 ">{fullname}</a>
|
||||
<a
|
||||
className="hover:text-red-600 underline text-sm cursor-pointer"
|
||||
onClick={() => onLogout()}
|
||||
|
|
@ -437,12 +467,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
|||
} 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}
|
||||
>
|
||||
<Image
|
||||
src="/pengaduan.png"
|
||||
width={48}
|
||||
height={48}
|
||||
alt="profile"
|
||||
/>
|
||||
<UserProfileIcon size={28} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,60 +1,151 @@
|
|||
'use client'
|
||||
import { BreadcrumbItem, Breadcrumbs } from '@nextui-org/breadcrumbs'
|
||||
import { Button } from '@nextui-org/button'
|
||||
import React from 'react'
|
||||
"use client";
|
||||
import { ChevronRightIcon } from "@/components/icons";
|
||||
import { getMagazineById } from "@/service/magazine";
|
||||
import {
|
||||
convertDateFormat,
|
||||
formatMonthString,
|
||||
formatTextToHtmlTag,
|
||||
} from "@/utils/global";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "@nextui-org/breadcrumbs";
|
||||
import { Button } from "@nextui-org/button";
|
||||
import { Accordion, AccordionItem } from "@nextui-org/react";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export default function EMagazineDetail() {
|
||||
return (
|
||||
<div className='w-auto bg-[#E2E2E2] text-black'>
|
||||
<div className='p-1 md:p-5 lg:px-8'>
|
||||
<div className='font-bold text-xl'>E-Majalah Polri</div>
|
||||
<div className="pt-2">
|
||||
<Breadcrumbs color='primary'>
|
||||
<BreadcrumbItem href='/'>Beranda</BreadcrumbItem>
|
||||
<BreadcrumbItem href='/e-majalah-polri/daftar-majalah'>E-Majalah Polri</BreadcrumbItem>
|
||||
<BreadcrumbItem>Judul</BreadcrumbItem>
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
<div className='pt-5 space-y-4'>
|
||||
<div className='font-semibold border-b-4 border-red-700 leading-loose'>MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023</div>
|
||||
<div className='bg-[#FFFFFF] rounded-lg h-[380px] flex items-center justify-center'>
|
||||
<img src="/emagazine.jpeg" alt="emagazine" className='h-[380px] rounded-md py-1' />
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
className='w-full bg-[#DD8306] text-sm font-semibold'
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
<div className='bg-[#FFFFFF] rounded-md text-sm font-medium'>
|
||||
<div className='flex justify-between text-white border-t-2 border-l border-r rounded-t-md border-gray-300 p-3 bg-[#9D9D9D]'>
|
||||
<div className='w-5/6'>Judul</div>
|
||||
<div className='w-1/6 text-center border-l border-gray-300'>Keterangan</div>
|
||||
</div>
|
||||
<div className='flex justify-between border border-gray-300 p-3'>
|
||||
<div className='w-5/6'>Download</div>
|
||||
<div className='w-1/6 text-center border-l border-gray-300'>51</div>
|
||||
</div>
|
||||
<div className='flex justify-between border border-gray-300 p-3'>
|
||||
<div className='w-5/6'>File Size</div>
|
||||
<div className='w-1/6 text-center border-l border-gray-300'>12,.58MB</div>
|
||||
</div>
|
||||
<div className='flex justify-between border border-gray-300 p-3'>
|
||||
<div className='w-5/6'>Download</div>
|
||||
<div className='w-1/6 text-center border-l border-gray-300'>10</div>
|
||||
</div>
|
||||
<div className='flex justify-between border border-gray-300 p-3'>
|
||||
<div className='w-5/6'>Created Date</div>
|
||||
<div className='w-1/6 text-center border-l border-gray-300'>1 Oktober 2024</div>
|
||||
</div>
|
||||
<div className='flex justify-between border border-b border-gray-300 p-3 rounded-b-md'>
|
||||
<div className='w-5/6'>Last Update</div>
|
||||
<div className='w-1/6 text-center border-l border-gray-300'>1 Oktober 2024</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
const params = useParams();
|
||||
const id = params?.id;
|
||||
const [detailData, setDetailData] = useState<any>();
|
||||
const [detailFiles, setDetailFiles] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, [id]);
|
||||
|
||||
const initFetch = async () => {
|
||||
const res = await getMagazineById(String(id));
|
||||
const data = res?.data?.data;
|
||||
setDetailData(data);
|
||||
setDetailFiles(data?.files);
|
||||
};
|
||||
|
||||
const doDownload = async (fileName: string, title: string): Promise<void> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://38.47.180.165:8802/magazine-files/viewer/${fileName}`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("File not found or server error");
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const anchor = document.createElement("a");
|
||||
|
||||
anchor.href = url;
|
||||
anchor.download = title === "" ? fileName : title;
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(anchor);
|
||||
} catch (error) {
|
||||
console.error("Download failed:", error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="w-auto bg-[#E2E2E2] text-black">
|
||||
<div className="p-1 md:p-5 lg:px-36">
|
||||
<div className="flex flex-row gap-4 items-end text-black">
|
||||
<Link href="/" className=" font-semibold text-lg">
|
||||
Beranda
|
||||
</Link>
|
||||
<ChevronRightIcon />
|
||||
<Link
|
||||
href="/e-majalah-polri/daftar-majalah"
|
||||
className=" font-semibold text-lg"
|
||||
>
|
||||
E-Majalah Polri
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
<div className="pt-5 space-y-4">
|
||||
<div className="font-semibold border-b-4 border-red-700 leading-loose">
|
||||
{detailData?.title}
|
||||
</div>
|
||||
<div className="rounded-lg h-[380px] flex items-center justify-center">
|
||||
<img
|
||||
src="/emagazine.jpeg"
|
||||
alt="emagazine"
|
||||
className="h-[380px] rounded-md py-1"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={formatTextToHtmlTag(
|
||||
detailData?.description
|
||||
)}
|
||||
className="text-sm lg:text-xl lg:leading-8 justify-center flex"
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{detailFiles?.map((file: any, index: number) => (
|
||||
<Accordion key={file?.id} variant="splitted">
|
||||
<AccordionItem
|
||||
key={file?.id}
|
||||
aria-label={file?.title}
|
||||
className="p-2"
|
||||
title={
|
||||
<p className="text-black dark:text-white font-semibold text-xs md:text-sm">
|
||||
{file?.title === "" ? `File ${index + 1}` : file?.title}
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<div className="bg-[#FFFFFF] rounded-md font-medium text-xs md:text-sm">
|
||||
<div className="flex justify-between items-center border text-black dark:text-white border-gray-300 p-3 rounded-t-md bg-gray-100 dark:bg-stone-900">
|
||||
<div className="w-[35%] md:w-[20%]">Nama File</div>
|
||||
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
|
||||
{file?.fileName}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between border items-center border-gray-300 p-3">
|
||||
<div className="w-[35%] md:w-[20%]">Deskripsi</div>
|
||||
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
|
||||
{file?.description == "" ? "-" : file?.description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between border items-center border-gray-300 p-3">
|
||||
<div className="w-[35%] md:w-[20%]">Ukuran File</div>
|
||||
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
|
||||
{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 className="flex justify-between items-center border rounded-b-md border-gray-300 p-3">
|
||||
<div className="w-[35%] md:w-[20%]">Tanggal Publish</div>
|
||||
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
|
||||
{formatMonthString(file?.createdAt)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
className="w-full bg-[#DD8306] text-sm font-semibold text-white mt-2"
|
||||
radius="sm"
|
||||
onPress={() => doDownload(file?.fileName, file?.title)}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ export default function MasterRoleTable() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="mx-5 my-5">
|
||||
<div className="mx-3 my-5">
|
||||
<div className="flex flex-col items-center rounded-2xl">
|
||||
<Table
|
||||
// selectionMode="multiple"
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ export default function MasterUserTable() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="mx-5 my-5">
|
||||
<div className="mx-3 my-5">
|
||||
<div className="flex flex-col items-center rounded-2xl">
|
||||
<Table
|
||||
// selectionMode="multiple"
|
||||
|
|
|
|||
|
|
@ -204,9 +204,9 @@ export default function StaticPageTable() {
|
|||
<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 gap-1 w-1/3">
|
||||
<p className="font-semibold text-sm">Search</p>
|
||||
<p className="font-semibold text-sm">Pencarian</p>
|
||||
<Input
|
||||
aria-label="Search"
|
||||
aria-label="Pencarian..."
|
||||
classNames={{
|
||||
inputWrapper: "bg-default-100",
|
||||
input: "text-sm",
|
||||
|
|
@ -222,7 +222,7 @@ export default function StaticPageTable() {
|
|||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 w-[72px]">
|
||||
<p className="font-semibold text-sm">Show</p>
|
||||
<p className="font-semibold text-sm">Data</p>
|
||||
<Select
|
||||
label=""
|
||||
variant="bordered"
|
||||
|
|
@ -242,29 +242,9 @@ export default function StaticPageTable() {
|
|||
</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 w-[170px]">
|
||||
<p className="font-semibold text-sm">Category</p>
|
||||
<Select
|
||||
label=""
|
||||
variant="bordered"
|
||||
labelPlacement="outside"
|
||||
placeholder="Select"
|
||||
selectedKeys={[showData]}
|
||||
className="w-full"
|
||||
onChange={(e) =>
|
||||
e.target.value === "" ? "" : setShowData(e.target.value)
|
||||
}
|
||||
>
|
||||
<SelectItem key="10" value="10">
|
||||
Polda Metro Jaya
|
||||
</SelectItem>
|
||||
<SelectItem key="5" value="5">
|
||||
Polda Sumatera Utara
|
||||
</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 w-full md:w-[340px]">
|
||||
<p className="font-semibold text-sm">Date</p>
|
||||
<p className="font-semibold text-sm">Tanggal</p>
|
||||
<Datepicker
|
||||
value={startDateValue}
|
||||
displayFormat="DD/MM/YYYY"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
Breadcrumbs,
|
||||
Button,
|
||||
Input,
|
||||
Pagination,
|
||||
Select,
|
||||
SelectItem,
|
||||
SelectSection,
|
||||
|
|
@ -16,7 +17,21 @@ import {
|
|||
} from "@nextui-org/react";
|
||||
import Link from "next/link";
|
||||
import SidebarNav from "../landing/SidebarNav";
|
||||
import { EyeFilledIcon, SearchIcon } from "../icons";
|
||||
import { ChevronRightIcon, EyeFilledIcon, SearchIcon } from "../icons";
|
||||
import { getListMagazine } from "@/service/magazine";
|
||||
import { useEffect, useState } from "react";
|
||||
import Datepicker from "react-tailwindcss-datepicker";
|
||||
|
||||
const header = [
|
||||
{
|
||||
key: "title",
|
||||
label: "Daftar E-Majalah Polri",
|
||||
},
|
||||
{
|
||||
key: "icon",
|
||||
label: "",
|
||||
},
|
||||
];
|
||||
|
||||
export default function ListEnewsPolri() {
|
||||
const searchInput = (
|
||||
|
|
@ -57,68 +72,92 @@ export default function ListEnewsPolri() {
|
|||
/>
|
||||
);
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
key: "1",
|
||||
tahun: "MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023",
|
||||
icon: <EyeFilledIcon color="#DD8306" />,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
tahun: "Tribrata Edisi 1 2023",
|
||||
icon: <EyeFilledIcon color="#DD8306" />,
|
||||
},
|
||||
{
|
||||
key: "3",
|
||||
tahun: "MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023",
|
||||
icon: <EyeFilledIcon color="#DD8306" />,
|
||||
},
|
||||
{
|
||||
key: "4",
|
||||
tahun: "MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023",
|
||||
icon: <EyeFilledIcon color="#DD8306" />,
|
||||
},
|
||||
{
|
||||
key: "5",
|
||||
tahun: "MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023",
|
||||
icon: <EyeFilledIcon color="#DD8306" />,
|
||||
},
|
||||
];
|
||||
const [magazines, setMagazines] = useState<any>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [search, setSearch] = useState("");
|
||||
const [totalPage, setTotalPage] = useState(1);
|
||||
|
||||
const header = [
|
||||
{
|
||||
key: "tahun",
|
||||
label: "Daftar E-Majalah Polri",
|
||||
},
|
||||
{
|
||||
key: "icon",
|
||||
label: "",
|
||||
},
|
||||
];
|
||||
const [startDateValue, setStartDateValue] = useState({
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
getMagazines();
|
||||
}, [page, startDateValue]);
|
||||
|
||||
async function getMagazines() {
|
||||
const req = {
|
||||
page: page,
|
||||
search: search,
|
||||
limit: "10",
|
||||
startDate:
|
||||
startDateValue.startDate === null ? "" : startDateValue.startDate,
|
||||
endDate: startDateValue.endDate === null ? "" : startDateValue.endDate,
|
||||
};
|
||||
|
||||
const response = await getListMagazine(req);
|
||||
setMagazines(response?.data?.data);
|
||||
setTotalPage(response?.data?.meta?.totalPage);
|
||||
}
|
||||
|
||||
const category = [
|
||||
{ label: "Title", value: "title" },
|
||||
{ label: "Date", value: "date" },
|
||||
{ label: "Created At", value: "createdAt" },
|
||||
];
|
||||
return (
|
||||
<div className="md:flex ">
|
||||
<div className="w-auto bg-[#E2E2E2] md:w-2/3 lg:w-[75%] text-black">
|
||||
<div className="p-1 md:p-5 lg:p-8">
|
||||
<div className="font-bold text-xl">E-Majalah Polri</div>
|
||||
<div className="pt-2">
|
||||
<Breadcrumbs color="primary">
|
||||
<BreadcrumbItem href="/">Beranda</BreadcrumbItem>
|
||||
<BreadcrumbItem>E-Majalah Polri</BreadcrumbItem>
|
||||
</Breadcrumbs>
|
||||
<div className="p-1 md:py-5 lg:px-36">
|
||||
<div className="flex flex-row gap-4 items-end text-black">
|
||||
<Link href="/" className=" font-semibold text-lg">
|
||||
Beranda
|
||||
</Link>
|
||||
<ChevronRightIcon />
|
||||
<p className=" font-semibold text-lg">E-Majalah Polri</p>
|
||||
</div>
|
||||
<div className="pt-4 space-y-5">
|
||||
<div className="font-semibold text-lg border-b-4 border-red-700 leading-loose">
|
||||
<div className="pt-4 space-y-5 ">
|
||||
{/* <div className="font-semibold text-lg border-b-4 border-red-700 leading-loose">
|
||||
E-majalah Polri
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
{searchInput}
|
||||
<Select
|
||||
</div> */}
|
||||
<div className="flex items-end gap-3 flex-col md:flex-row">
|
||||
<Input
|
||||
aria-label="Search"
|
||||
classNames={{
|
||||
input: [
|
||||
"w-full",
|
||||
"bg-transparent",
|
||||
"h-[20px]",
|
||||
"!text-black",
|
||||
],
|
||||
mainWrapper: ["w-full", "bg-transparent"],
|
||||
innerWrapper: ["bg-transparent", "h-[20px]"],
|
||||
inputWrapper: [
|
||||
"bg-white",
|
||||
"dark:bg-white",
|
||||
"hover:!bg-gray-300",
|
||||
"dark:hover:bg-gray-300",
|
||||
"group-data-[focused=true]:bg-transparent",
|
||||
"dark:group-data-[focused=true]:bg-transaparent",
|
||||
"group-data-[focused=false]:bg-transparent",
|
||||
"focus-within:!bg-transparent",
|
||||
"h-[20px]",
|
||||
"dark:focus-within:!bg-gray-100",
|
||||
],
|
||||
}}
|
||||
labelPlacement="outside"
|
||||
type="text"
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
startContent={
|
||||
<SearchIcon className="text-base text-default-400 pointer-events-none flex-shrink-0" />
|
||||
}
|
||||
endContent={
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-[#DD8306] font-semibold text-white"
|
||||
onPress={getMagazines}
|
||||
>
|
||||
Cari
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
{/* <Select
|
||||
label="Sort By"
|
||||
size="sm"
|
||||
className="w-1/5"
|
||||
|
|
@ -147,44 +186,25 @@ export default function ListEnewsPolri() {
|
|||
<SelectItem key={list.id}>{list.label}</SelectItem>
|
||||
))}
|
||||
</SelectSection>
|
||||
</Select>
|
||||
<Select
|
||||
label="Tanggal Publikasi"
|
||||
size="sm"
|
||||
className="w-1/4"
|
||||
classNames={{
|
||||
// base: "bg-red-500",
|
||||
// mainWrapper: "border-2 border-red-500",
|
||||
label: "text-black",
|
||||
value: "!text-black",
|
||||
trigger: "bg-white hover:!bg-gray-100",
|
||||
// innerWrapper: "bg-red-500"
|
||||
// selectorIcon: "bg-red-500"
|
||||
// listboxWrapper: "bg-red-500"
|
||||
// listbox: "bg-red-500"
|
||||
popoverContent: "bg-white",
|
||||
}}
|
||||
listboxProps={{
|
||||
itemClasses: {
|
||||
base: "text-black",
|
||||
wrapper: "!bg-white ",
|
||||
},
|
||||
}}
|
||||
// onChange={onChangeFilterEnterprising}
|
||||
>
|
||||
<SelectSection>
|
||||
{category.map((list: any) => (
|
||||
<SelectItem key={list.id}>{list.label}</SelectItem>
|
||||
))}
|
||||
</SelectSection>
|
||||
</Select>
|
||||
</Select> */}
|
||||
<div className="flex flex-col gap-1 w-full md:w-[240px]">
|
||||
<p className="font-semibold text-xs md:text-sm">
|
||||
Tanggal Publikasi
|
||||
</p>
|
||||
<Datepicker
|
||||
value={startDateValue}
|
||||
displayFormat="DD/MM/YYYY"
|
||||
useRange={false}
|
||||
asSingle={true}
|
||||
onChange={(e: any) => setStartDateValue(e)}
|
||||
inputClassName="z-50 w-full text-sm bg-white border-1 border-gray-200 px-2 py-[6px] rounded-xl h-[40px] text-black"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Table
|
||||
color="warning"
|
||||
selectionMode="single"
|
||||
defaultSelectedKeys={["1"]}
|
||||
aria-label="Example static collection table"
|
||||
classNames={{
|
||||
wrapper: "bg-white",
|
||||
|
|
@ -203,19 +223,38 @@ export default function ListEnewsPolri() {
|
|||
</TableColumn>
|
||||
)}
|
||||
</TableHeader>
|
||||
<TableBody items={tableData}>
|
||||
{(item) => (
|
||||
<TableRow key={item.key}>
|
||||
<TableCell>{item.tahun}</TableCell>
|
||||
<TableBody items={magazines}>
|
||||
{(item: any) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell>
|
||||
<Link href={`/e-majalah-polri/detail/${item.id}`}>
|
||||
{item.title}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Link href={`/e-majalah-polri/detail/${item.key}`}>
|
||||
{item.icon}
|
||||
<EyeFilledIcon color="#DD8306" />
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div className="my-2 w-full flex justify-center">
|
||||
<Pagination
|
||||
isCompact
|
||||
showControls
|
||||
showShadow
|
||||
color="primary"
|
||||
classNames={{
|
||||
base: "bg-transparent",
|
||||
wrapper: "bg-transparent",
|
||||
}}
|
||||
page={page}
|
||||
total={totalPage}
|
||||
onChange={(page) => setPage(page)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,15 @@ import { BreadcrumbItem, Breadcrumbs } from "@nextui-org/react";
|
|||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { Image } from "@nextui-org/react";
|
||||
import { FormLayoutIcon } from "../icons";
|
||||
import {
|
||||
ArticleIcon,
|
||||
DashboardIcon,
|
||||
MagazineIcon,
|
||||
MasterCategoryIcon,
|
||||
MasterRoleIcon,
|
||||
MasterUsersIcon,
|
||||
StaticPageIcon,
|
||||
} from "../icons/sidebar-icon";
|
||||
|
||||
export const Breadcrumb = () => {
|
||||
const [currentPage, setCurrentPage] = useState<React.Key>("");
|
||||
|
|
@ -58,7 +67,16 @@ export const Breadcrumb = () => {
|
|||
)}
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
<FormLayoutIcon width={50} height={50} />
|
||||
{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>
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -3,6 +3,9 @@ import {
|
|||
httpGet,
|
||||
httpPost,
|
||||
} from "./http-config/axios-base-service";
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
const token = Cookies.get("access_token");
|
||||
|
||||
export async function listMasterUsers(data: any) {
|
||||
const headers = {
|
||||
|
|
@ -34,6 +37,7 @@ export async function postSignIn(data: any) {
|
|||
export async function getProfile() {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
return await httpGet(`/users/info`, headers);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,3 +77,18 @@
|
|||
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.ck-editor__editable_inline:not(.ck-comment__input *) {
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%; /* Pastikan tinggi body penuh */
|
||||
overflow: hidden; /* Nonaktifkan scrolling global */
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100vh; /* Pastikan tinggi main sesuai viewport */
|
||||
overflow-y: auto; /* Aktifkan scrolling hanya di elemen main */
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue