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() {
|
export default function CreateArticle() {
|
||||||
return (
|
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 /> */}
|
{/* <FormArticle /> */}
|
||||||
<CreateArticleForm />
|
<CreateArticleForm />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -22,25 +22,14 @@ 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 w-full">
|
<div className="px-2 md:px-4 md:py-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="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
||||||
<Link href="/admin/article/create">
|
<Link href="/admin/article/create" className="mx-3">
|
||||||
<Button size="md" className="bg-[#F07C00] text-white">
|
<Button size="md" className="bg-[#F07C00] text-white">
|
||||||
New Article
|
Tambah Artikel
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</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 />
|
<ArticleTable />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,20 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { AddIcon } from "@/components/icons";
|
import { AddIcon } from "@/components/icons";
|
||||||
import ArticleTable from "@/components/table/article-table";
|
|
||||||
import MagazineTable from "@/components/table/magazine/magazine-table";
|
import MagazineTable from "@/components/table/magazine/magazine-table";
|
||||||
import generatedArticleIds from "@/store/generated-article-store";
|
import { Button } from "@nextui-org/react";
|
||||||
import { Button, Card } from "@nextui-org/react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
|
|
||||||
export default function MagazineTablePage() {
|
export default function MagazineTablePage() {
|
||||||
const router = useRouter();
|
|
||||||
const setGeneratedArticleIdStore = generatedArticleIds(
|
|
||||||
(state) => state.setArticleIds
|
|
||||||
);
|
|
||||||
|
|
||||||
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 w-full">
|
<div className="px-2 md:px-4 md:py-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="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
||||||
<Link href="/admin/magazine/create">
|
<Link href="/admin/magazine/create" className="mx-3">
|
||||||
<Button size="md" className="bg-[#F07C00] text-white">
|
<Button size="md" className="bg-[#F07C00] text-white">
|
||||||
Tambah Majalah
|
Tambah Majalah
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</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 />
|
<MagazineTable />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -163,218 +163,209 @@ 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 w-full">
|
<div className="px-2 md:px-4 md:py-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="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
className="bg-[#F07C00] text-white"
|
className="bg-[#F07C00] text-white mx-3"
|
||||||
onPress={onOpen}
|
onPress={onOpen}
|
||||||
>
|
>
|
||||||
Tambah Kategori
|
Tambah Kategori
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</Button>
|
</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} />
|
<CategoriesTable triggerRefresh={refresh} />
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,21 @@ import Link from "next/link";
|
||||||
|
|
||||||
export default function MasterRolePage() {
|
export default function MasterRolePage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] overflow-x-hidden overflow-y-scroll gap-0 grid rounded-lg">
|
<div className="overflow-x-hidden overflow-y-scroll">
|
||||||
<div className="px-4">
|
<div className="px-2 md:px-4 md:py-4 w-full">
|
||||||
<Card className="rounded-md my-5 pl-5 py-2">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
||||||
<Link href="/admin/master-role/create">
|
<Link href="/admin/master-role/create" className="mx-3">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
color="primary"
|
color="primary"
|
||||||
className="bg-[#F07C00] text-white"
|
className="bg-[#F07C00] text-white"
|
||||||
>
|
>
|
||||||
|
Peran Baru
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
New Role
|
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</Card>
|
|
||||||
<Card className="rounded-md my-5">
|
|
||||||
<MasterRoleTable />
|
<MasterRoleTable />
|
||||||
</Card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,21 @@ import Link from "next/link";
|
||||||
|
|
||||||
export default function MasterUserPage() {
|
export default function MasterUserPage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-[96vh] overflow-x-hidden overflow-y-scroll gap-0 grid rounded-lg">
|
<div className="overflow-x-hidden overflow-y-scroll">
|
||||||
<div className="px-4">
|
<div className="px-2 md:px-4 md:py-4 w-full">
|
||||||
<Card className="rounded-md my-5 pl-5 py-2">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
||||||
<Link href="/admin/master-user/create">
|
<Link href="/admin/master-user/create" className="mx-3">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
color="primary"
|
color="primary"
|
||||||
className="bg-[#F07C00] text-white"
|
className="bg-[#F07C00] text-white"
|
||||||
>
|
>
|
||||||
|
Pengguna Baru
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
New User
|
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</Card>
|
|
||||||
<Card className="rounded-md my-5">
|
|
||||||
<MasterUserTable />
|
<MasterUserTable />
|
||||||
</Card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,18 @@ import Link from "next/link";
|
||||||
export default function StaticPageGeneratorList() {
|
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 w-full">
|
<div className="px-2 md:px-4 md:py-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">
|
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl py-3">
|
||||||
<Link href="/admin/static-page/create">
|
<Link href="/admin/static-page/create" className="mx-3">
|
||||||
<Button
|
<Button
|
||||||
size="md"
|
size="md"
|
||||||
color="primary"
|
color="primary"
|
||||||
className="bg-[#F07C00] text-white"
|
className="bg-[#F07C00] text-white"
|
||||||
>
|
>
|
||||||
|
Tambah Halaman
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
Create Page
|
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
|
||||||
<div className="bg-white shadow-lg dark:bg-[#18181b] rounded-xl p-2 mt-4">
|
|
||||||
<StaticPageTable />
|
<StaticPageTable />
|
||||||
</div>
|
</div>
|
||||||
</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";
|
"use client";
|
||||||
import { HumasLayout } from "@/components/layout/humas-layout";
|
import { HumasLayout } from "@/components/layout/humas-layout";
|
||||||
import ListEnewsPolri from "@/components/table/tabel-emajalah-polri";
|
import ListEnewsPolri from "@/components/table/tabel-emajalah-polri";
|
||||||
import React from "react";
|
import React, { Suspense, useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function ListEnewsPage() {
|
export default function ListEnewsPage() {
|
||||||
|
const [hasMounted, setHasMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHasMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
if (!hasMounted) return null;
|
||||||
return (
|
return (
|
||||||
<HumasLayout>
|
<HumasLayout>
|
||||||
<ListEnewsPolri />
|
<Suspense>
|
||||||
|
<ListEnewsPolri />
|
||||||
|
</Suspense>
|
||||||
</HumasLayout>
|
</HumasLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,22 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { HumasLayout } from "@/components/layout/humas-layout";
|
import { HumasLayout } from "@/components/layout/humas-layout";
|
||||||
import EMagazineDetail from "@/components/main/detail/e-magazine-detail";
|
import EMagazineDetail from "@/components/main/detail/e-magazine-detail";
|
||||||
import React from "react";
|
import React, { Suspense, useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function EnewsDetailPage() {
|
export default function EnewsDetailPage() {
|
||||||
|
const [hasMounted, setHasMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHasMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
if (!hasMounted) return null;
|
||||||
return (
|
return (
|
||||||
<HumasLayout>
|
<HumasLayout>
|
||||||
<EMagazineDetail />
|
<Suspense>
|
||||||
|
<EMagazineDetail />
|
||||||
|
</Suspense>
|
||||||
</HumasLayout>
|
</HumasLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,13 +64,13 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"bg-background font-sans antialiased",
|
"bg-background font-sans antialiased overflow-hidden",
|
||||||
fontSans.variable
|
fontSans.variable
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<NextIntlClientProvider locale={localeNow} messages={messages}>
|
<NextIntlClientProvider locale={localeNow} messages={messages}>
|
||||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
||||||
<main className="">{children}</main>
|
<main className="overflow-y-auto">{children}</main>
|
||||||
</Providers>
|
</Providers>
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,27 @@ function CustomEditor(props) {
|
||||||
props.onChange(data);
|
props.onChange(data);
|
||||||
}}
|
}}
|
||||||
config={{
|
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;
|
export default CustomEditor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,10 @@ import { close, error, loading } from "@/config/swal";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { getCategoryById } from "@/service/master-categories";
|
import { getCategoryById } from "@/service/master-categories";
|
||||||
import { saveManualContext } from "@/service/generate-article";
|
import {
|
||||||
|
saveManualContext,
|
||||||
|
updateManualArticle,
|
||||||
|
} from "@/service/generate-article";
|
||||||
|
|
||||||
const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -52,6 +55,17 @@ const categorySchema = z.object({
|
||||||
value: z.number(),
|
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({
|
const createArticleSchema = z.object({
|
||||||
title: z.string().min(2, {
|
title: z.string().min(2, {
|
||||||
message: "Judul harus diisi",
|
message: "Judul harus diisi",
|
||||||
|
|
@ -81,6 +95,7 @@ export default function CreateArticleForm() {
|
||||||
const [tag, setTag] = useState("");
|
const [tag, setTag] = useState("");
|
||||||
const [thumbnailImg, setThumbnailImg] = useState<File[]>([]);
|
const [thumbnailImg, setThumbnailImg] = useState<File[]>([]);
|
||||||
const [selectedMainImage, setSelectedMainImage] = useState<number>();
|
const [selectedMainImage, setSelectedMainImage] = useState<number>();
|
||||||
|
const [diseData, setDiseData] = useState<DiseData>();
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
onDrop: (acceptedFiles) => {
|
onDrop: (acceptedFiles) => {
|
||||||
|
|
@ -148,6 +163,12 @@ export default function CreateArticleForm() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (useAi === false) {
|
||||||
|
setValue("description", "");
|
||||||
|
}
|
||||||
|
}, [useAi]);
|
||||||
|
|
||||||
function removeImgTags(htmlString: string) {
|
function removeImgTags(htmlString: string) {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(String(htmlString), "text/html");
|
const doc = parser.parseFromString(String(htmlString), "text/html");
|
||||||
|
|
@ -160,26 +181,50 @@ export default function CreateArticleForm() {
|
||||||
const saveArticleToDise = async (
|
const saveArticleToDise = async (
|
||||||
values: z.infer<typeof createArticleSchema>
|
values: z.infer<typeof createArticleSchema>
|
||||||
) => {
|
) => {
|
||||||
const request = {
|
if (useAi) {
|
||||||
title: values.title,
|
const request = {
|
||||||
articleBody: removeImgTags(values.description),
|
id: diseData?.id,
|
||||||
metaDescription: values.title,
|
title: values.title,
|
||||||
metaTitle: values.title,
|
articleBody: removeImgTags(values.description),
|
||||||
mainKeyword: values.title,
|
metaDescription: diseData?.metaDescription,
|
||||||
additionalKeywords: values.title,
|
metaTitle: diseData?.metaTitle,
|
||||||
createdBy: "345",
|
mainKeyword: diseData?.mainKeyword,
|
||||||
style: "Informational",
|
additionalKeywords: diseData?.additionalKeywords,
|
||||||
projectId: 2,
|
createdBy: "345",
|
||||||
clientId: "humasClientIdtest",
|
style: "Informational",
|
||||||
lang: "id",
|
projectId: 2,
|
||||||
};
|
clientId: "humasClientIdtest",
|
||||||
|
lang: "id",
|
||||||
|
};
|
||||||
|
const res = await updateManualArticle(request);
|
||||||
|
if (res.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await saveManualContext(request);
|
return diseData?.id;
|
||||||
if (res.error) {
|
} else {
|
||||||
res.message;
|
const request = {
|
||||||
return 0;
|
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>) => {
|
const save = async (values: z.infer<typeof createArticleSchema>) => {
|
||||||
|
|
@ -192,7 +237,7 @@ export default function CreateArticleForm() {
|
||||||
tags: values.tags.join(","),
|
tags: values.tags.join(","),
|
||||||
description: htmlToString(removeImgTags(values.description)),
|
description: htmlToString(removeImgTags(values.description)),
|
||||||
htmlDescription: removeImgTags(values.description),
|
htmlDescription: removeImgTags(values.description),
|
||||||
// aiArticleId: saveArticleToDise(values),
|
aiArticleId: await saveArticleToDise(values),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await createArticle(formData);
|
const response = await createArticle(formData);
|
||||||
|
|
@ -418,7 +463,13 @@ export default function CreateArticleForm() {
|
||||||
|
|
||||||
{useAi && (
|
{useAi && (
|
||||||
<GenerateSingleArticleForm
|
<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}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
// <CustomEditor onChange={onChange} initialData={value} />
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
// <JoditEditor
|
|
||||||
// ref={editor}
|
|
||||||
// value={value}
|
|
||||||
// onChange={onChange}
|
|
||||||
// className="dark:text-black"
|
|
||||||
// />
|
|
||||||
<CustomEditor
|
|
||||||
onChange={onChange}
|
|
||||||
initialData={value}
|
|
||||||
/>
|
|
||||||
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors?.description && (
|
{errors?.description && (
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,17 @@ const createArticleSchema = z.object({
|
||||||
}), // Array berisi string
|
}), // 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 }) {
|
export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
const { isDetail } = props;
|
const { isDetail } = props;
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
@ -94,6 +105,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
const [detailfiles, setDetailFiles] = useState<any>([]);
|
const [detailfiles, setDetailFiles] = useState<any>([]);
|
||||||
const [mainImage, setMainImage] = useState(0);
|
const [mainImage, setMainImage] = useState(0);
|
||||||
const [thumbnail, setThumbnail] = useState("");
|
const [thumbnail, setThumbnail] = useState("");
|
||||||
|
const [diseId, setDiseId] = useState(0);
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
onDrop: (acceptedFiles) => {
|
onDrop: (acceptedFiles) => {
|
||||||
|
|
@ -137,6 +149,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
setValue("description", data?.htmlDescription);
|
setValue("description", data?.htmlDescription);
|
||||||
setValue("tags", data?.tags ? data.tags.split(",") : []);
|
setValue("tags", data?.tags ? data.tags.split(",") : []);
|
||||||
setThumbnail(data?.thumbnailUrl);
|
setThumbnail(data?.thumbnailUrl);
|
||||||
|
setDiseId(data?.aiArticleId);
|
||||||
setDetailFiles(data?.files);
|
setDetailFiles(data?.files);
|
||||||
|
|
||||||
setupInitCategory(data?.categories);
|
setupInitCategory(data?.categories);
|
||||||
|
|
@ -205,7 +218,6 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
description: htmlToString(values.description),
|
description: htmlToString(values.description),
|
||||||
htmlDescription: values.description,
|
htmlDescription: values.description,
|
||||||
};
|
};
|
||||||
console.log("vals", formData);
|
|
||||||
const response = await updateArticle(String(id), formData);
|
const response = await updateArticle(String(id), formData);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
|
|
@ -345,7 +357,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
>
|
>
|
||||||
<div className="w-[65%] bg-white rounded-lg p-8 flex flex-col gap-1">
|
<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>
|
<p className="text-sm">Judul</p>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -409,7 +421,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
|
|
||||||
{useAi && (
|
{useAi && (
|
||||||
<GenerateSingleArticleForm
|
<GenerateSingleArticleForm
|
||||||
content={(data) => setValue("description", data)}
|
content={(data) => setValue("description", data?.articleBody)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -417,7 +429,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) =>
|
||||||
// <CustomEditor onChange={onChange} initialData={value} />
|
// <CustomEditor onChange={onChange} initialData={value} />
|
||||||
// <JoditEditor
|
// <JoditEditor
|
||||||
// ref={editor}
|
// ref={editor}
|
||||||
|
|
@ -426,16 +438,12 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
// config={{ readonly: isDetail }}
|
// config={{ readonly: isDetail }}
|
||||||
// className="dark:text-black"
|
// className="dark:text-black"
|
||||||
// />
|
// />
|
||||||
isDetail ?
|
isDetail ? (
|
||||||
<ViewEditor
|
<ViewEditor initialData={value} />
|
||||||
initialData={value}
|
) : (
|
||||||
/>
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
:
|
)
|
||||||
<CustomEditor
|
}
|
||||||
onChange={onChange}
|
|
||||||
initialData={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
{errors?.description && (
|
{errors?.description && (
|
||||||
<p className="text-red-400 text-sm mb-3">
|
<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: {
|
export default function GenerateSingleArticleForm(props: {
|
||||||
content: (data: string) => void;
|
content: (data: DiseData) => void;
|
||||||
}) {
|
}) {
|
||||||
const [selectedWritingSyle, setSelectedWritingStyle] =
|
const [selectedWritingSyle, setSelectedWritingStyle] =
|
||||||
useState("Informational");
|
useState("Informational");
|
||||||
|
|
@ -162,14 +173,23 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
const getArticleDetail = async () => {
|
const getArticleDetail = async () => {
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
const res = await getDetailArticle(selectedId);
|
const res = await getDetailArticle(selectedId);
|
||||||
const data = res?.data?.data?.articleBody;
|
const data = res?.data?.data;
|
||||||
checkArticleStatus(data);
|
checkArticleStatus(data?.articleBody);
|
||||||
if (data !== null) {
|
if (data?.articleBody !== null) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
props.content(data);
|
props.content(data);
|
||||||
} else {
|
} else {
|
||||||
setIsLoading(true);
|
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 }) {
|
export default function GetSeoScore(props: { id: string }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSeoScore();
|
fetchSeoScore();
|
||||||
}, []);
|
}, [props.id]);
|
||||||
|
|
||||||
const [totalScoreSEO, setTotalScoreSEO] = useState();
|
const [totalScoreSEO, setTotalScoreSEO] = useState();
|
||||||
const [errorSEO, setErrorSEO] = useState<any>([]);
|
const [errorSEO, setErrorSEO] = useState<any>([]);
|
||||||
|
|
@ -16,9 +16,9 @@ export default function GetSeoScore(props: { id: string }) {
|
||||||
const [optimizedSEO, setOptimizedSEO] = useState<any>([]);
|
const [optimizedSEO, setOptimizedSEO] = useState<any>([]);
|
||||||
|
|
||||||
const fetchSeoScore = async () => {
|
const fetchSeoScore = async () => {
|
||||||
const res = await getSeoScore("1931");
|
const res = await getSeoScore(props?.id);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
error(res.message);
|
// error(res.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setTotalScoreSEO(res.data.data?.seo_analysis?.score || 0);
|
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="overflow-y-auto my-2">
|
||||||
<div className="text-black flex flex-col rounded-md gap-3">
|
<div className="text-black flex flex-col rounded-md gap-3">
|
||||||
<p className="font-semibold text-lg"> SEO Score</p>
|
<p className="font-semibold text-lg"> SEO Score</p>
|
||||||
<div className="flex flex-row gap-5 w-full">
|
{totalScoreSEO ? (
|
||||||
<CircularProgress
|
<div className="flex flex-row gap-5 w-full">
|
||||||
aria-label=""
|
<CircularProgress
|
||||||
color="warning"
|
aria-label=""
|
||||||
showValueLabel={true}
|
color="warning"
|
||||||
size="lg"
|
showValueLabel={true}
|
||||||
value={Number(totalScoreSEO) * 100}
|
size="lg"
|
||||||
/>
|
value={Number(totalScoreSEO) * 100}
|
||||||
<div>
|
/>
|
||||||
{/* <ApexChartDonut value={Number(totalScoreSEO) * 100} /> */}
|
<div>
|
||||||
</div>
|
{/* <ApexChartDonut value={Number(totalScoreSEO) * 100} /> */}
|
||||||
<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>
|
||||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-yellow-500 rounded-lg">
|
<div className="flex flex-row gap-5">
|
||||||
{/* <p className="text-warning w-[15px] h-[15px] text-center mt-[-10px]">
|
<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> */}
|
</p> */}
|
||||||
Warning : {warningSEO.length || 0}
|
Warning : {warningSEO.length || 0}
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2 py-1 border radius-md flex flex-row gap-2 items-center border-green-500 rounded-lg">
|
<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" /> */}
|
{/* <CheckIcon size={15} className="text-success" /> */}
|
||||||
Optimize : {optimizedSEO.length || 0}
|
Optimize : {optimizedSEO.length || 0}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
<Accordion
|
"Belum ada Data"
|
||||||
variant="splitted"
|
)}
|
||||||
itemClasses={{
|
{totalScoreSEO && (
|
||||||
base: "!bg-transparent",
|
<Accordion
|
||||||
title: "text-black",
|
variant="splitted"
|
||||||
}}
|
itemClasses={{
|
||||||
>
|
base: "!bg-transparent",
|
||||||
<AccordionItem
|
title: "text-black",
|
||||||
key="1"
|
}}
|
||||||
aria-label="Error"
|
|
||||||
// startContent={<TimesIcon size={20} className="text-danger" />}
|
|
||||||
title={`${errorSEO?.length || 0} Errors`}
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<AccordionItem
|
||||||
{errorSEO?.map((item: any) => (
|
key="1"
|
||||||
<p
|
aria-label="Error"
|
||||||
key={item}
|
// startContent={<TimesIcon size={20} className="text-danger" />}
|
||||||
className="w-full border border-red-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
title={`${errorSEO?.length || 0} Errors`}
|
||||||
>
|
>
|
||||||
{item}
|
<div className="flex flex-col gap-2">
|
||||||
</p>
|
{errorSEO?.map((item: any) => (
|
||||||
))}
|
<p
|
||||||
</div>
|
key={item}
|
||||||
</AccordionItem>
|
className="w-full border border-red-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||||
<AccordionItem
|
>
|
||||||
key="2"
|
{item}
|
||||||
aria-label="Warning"
|
</p>
|
||||||
// startContent={
|
))}
|
||||||
// <p className="text-warning w-[20px] h-[20px] text-center mt-[-10px]">
|
</div>
|
||||||
// !
|
</AccordionItem>
|
||||||
// </p>
|
<AccordionItem
|
||||||
// }
|
key="2"
|
||||||
title={`${warningSEO?.length || 0} Warnings`}
|
aria-label="Warning"
|
||||||
>
|
// startContent={
|
||||||
<div className="flex flex-col gap-2">
|
// <p className="text-warning w-[20px] h-[20px] text-center mt-[-10px]">
|
||||||
{warningSEO?.map((item: any) => (
|
// !
|
||||||
<p
|
// </p>
|
||||||
key={item}
|
// }
|
||||||
className="w-full border border-yellow-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
title={`${warningSEO?.length || 0} Warnings`}
|
||||||
>
|
>
|
||||||
{item}
|
<div className="flex flex-col gap-2">
|
||||||
</p>
|
{warningSEO?.map((item: any) => (
|
||||||
))}
|
<p
|
||||||
</div>
|
key={item}
|
||||||
</AccordionItem>
|
className="w-full border border-yellow-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||||
<AccordionItem
|
>
|
||||||
key="3"
|
{item}
|
||||||
aria-label="Optimized"
|
</p>
|
||||||
// startContent={<CheckIcon size={20} className="text-success" />}
|
))}
|
||||||
title={`${optimizedSEO?.length || 0} Optimized`}
|
</div>
|
||||||
>
|
</AccordionItem>
|
||||||
<div className="flex flex-col gap-2">
|
<AccordionItem
|
||||||
{optimizedSEO?.map((item: any) => (
|
key="3"
|
||||||
<p
|
aria-label="Optimized"
|
||||||
key={item}
|
// startContent={<CheckIcon size={20} className="text-success" />}
|
||||||
className="w-full border border-green-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
title={`${optimizedSEO?.length || 0} Optimized`}
|
||||||
>
|
>
|
||||||
{item}
|
<div className="flex flex-col gap-2">
|
||||||
</p>
|
{optimizedSEO?.map((item: any) => (
|
||||||
))}
|
<p
|
||||||
</div>
|
key={item}
|
||||||
</AccordionItem>
|
className="w-full border border-green-500 rounded-md h-[40px] text-left flex flex-col justify-center px-3"
|
||||||
</Accordion>
|
>
|
||||||
|
{item}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -101,116 +101,7 @@ export default function Login() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// <HumasLayout>
|
<div className="flex flex-row h-screen">
|
||||||
// <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
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: "url(headerbanner1.png)",
|
backgroundImage: "url(headerbanner1.png)",
|
||||||
|
|
@ -220,9 +111,11 @@ export default function Login() {
|
||||||
}}
|
}}
|
||||||
className="h-screen hidden md:block md:w-3/5"
|
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>
|
||||||
<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="text-[72px] text-[#DD8306] font-semibold mb-10">Login</p>
|
||||||
<p className="my-2 text-white">Username</p>
|
<p className="my-2 text-white">Username</p>
|
||||||
<Input
|
<Input
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,12 @@ import {
|
||||||
} from "@/components/icons/globals";
|
} from "@/components/icons/globals";
|
||||||
import { createMagazine, uploadMagazineFile } from "@/service/magazine";
|
import { createMagazine, uploadMagazineFile } from "@/service/magazine";
|
||||||
|
|
||||||
// const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
// () => {
|
() => {
|
||||||
// return import("@/components/editor/custom-editor");
|
return import("@/components/editor/custom-editor");
|
||||||
// },
|
},
|
||||||
// { ssr: false }
|
{ ssr: false }
|
||||||
// );
|
);
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -375,13 +375,7 @@ export default function NewCreateMagazineForm() {
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
// <CustomEditor onChange={onChange} initialData={value} />
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
<JoditEditor
|
|
||||||
ref={editor}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className="dark:text-black"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors?.description && (
|
{errors?.description && (
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,12 @@ import {
|
||||||
uploadMagazineFile,
|
uploadMagazineFile,
|
||||||
} from "@/service/magazine";
|
} from "@/service/magazine";
|
||||||
|
|
||||||
// const CustomEditor = dynamic(
|
const CustomEditor = dynamic(
|
||||||
// () => {
|
() => {
|
||||||
// return import("@/components/editor/custom-editor");
|
return import("@/components/editor/custom-editor");
|
||||||
// },
|
},
|
||||||
// { ssr: false }
|
{ ssr: false }
|
||||||
// );
|
);
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -445,14 +445,7 @@ export default function EditMagazineForm(props: { isDetail: boolean }) {
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
// <CustomEditor onChange={onChange} initialData={value} />
|
<CustomEditor onChange={onChange} initialData={value} />
|
||||||
<JoditEditor
|
|
||||||
ref={editor}
|
|
||||||
value={value}
|
|
||||||
config={{ readonly: isDetail }}
|
|
||||||
onChange={onChange}
|
|
||||||
className="dark:text-black"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors?.description && (
|
{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>
|
</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/pagination";
|
||||||
import "swiper/css/effect-fade";
|
import "swiper/css/effect-fade";
|
||||||
import "swiper/css/navigation";
|
import "swiper/css/navigation";
|
||||||
|
import { getListMagazine } from "@/service/magazine";
|
||||||
|
|
||||||
export default function ENewsPolri() {
|
export default function ENewsPolri() {
|
||||||
const [article, setArticle] = useState<any>([]);
|
const [article, setArticle] = useState<any>([]);
|
||||||
|
|
@ -20,7 +21,7 @@ export default function ENewsPolri() {
|
||||||
async function getArticle() {
|
async function getArticle() {
|
||||||
const req = { page: 1, search: "", limit: "10" };
|
const req = { page: 1, search: "", limit: "10" };
|
||||||
|
|
||||||
const response = await getListArticle(req);
|
const response = await getListMagazine(req);
|
||||||
setArticle(response?.data?.data);
|
setArticle(response?.data?.data);
|
||||||
}
|
}
|
||||||
getArticle();
|
getArticle();
|
||||||
|
|
@ -68,13 +69,13 @@ 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
|
||||||
alt="headernews"
|
alt="thumbnail"
|
||||||
src="/headernews.png"
|
src={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">
|
||||||
<div className="text-white">
|
<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">
|
<p className="text-left font-semibold text-sm">
|
||||||
{textEllipsis(newsItem.title, 40)}
|
{textEllipsis(newsItem.title, 40)}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export default function SidebarNav() {
|
||||||
onClick={() => setSelectedTab("media")}
|
onClick={() => setSelectedTab("media")}
|
||||||
className={
|
className={
|
||||||
selectedTab === "media"
|
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"
|
: "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 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"
|
: "text-slate-300 cursor-pointer py-2"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ export const AdminLayout = ({ children }: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<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} />
|
<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 />
|
<Breadcrumb />
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ interface Props {
|
||||||
|
|
||||||
export const HumasLayout = ({ children }: Props) => {
|
export const HumasLayout = ({ children }: Props) => {
|
||||||
return (
|
return (
|
||||||
<section className="flex flex-col">
|
<section className="flex flex-col overflow-auto">
|
||||||
<NavbarHumas size="sm" />
|
<NavbarHumas size="sm" />
|
||||||
<NavbarHumas size="lg" />
|
<NavbarHumas size="lg" />
|
||||||
<NewsTicker />
|
<NewsTicker />
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import { useSidebar } from "./sidebar-context";
|
||||||
import { SidebarMenu } from "./sidebar-menu";
|
import { SidebarMenu } from "./sidebar-menu";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
import { SettingsIcon, UserProfileIcon } from "@/components/icons/globals";
|
||||||
|
|
||||||
interface SubMenuItems {
|
interface SubMenuItems {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -114,13 +115,13 @@ const sideBarDummyData = [
|
||||||
childModule: null,
|
childModule: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 30,
|
||||||
name: "Majalah",
|
name: "Category",
|
||||||
moduleId: 652,
|
moduleId: 654,
|
||||||
moduleName: "Apps",
|
moduleName: "Master",
|
||||||
modulePathUrl: "/admin/magazine",
|
modulePathUrl: "/admin/master-category",
|
||||||
parentId: -1,
|
parentId: -1,
|
||||||
icon: <MagazineIcon />,
|
icon: <MasterCategoryIcon size={22} />,
|
||||||
position: 1,
|
position: 1,
|
||||||
statusId: 1,
|
statusId: 1,
|
||||||
childMenu: [],
|
childMenu: [],
|
||||||
|
|
@ -128,19 +129,20 @@ const sideBarDummyData = [
|
||||||
childModule: null,
|
childModule: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 4,
|
||||||
name: "Static Page",
|
name: "Majalah",
|
||||||
moduleId: 652,
|
moduleId: 652,
|
||||||
moduleName: "Dashboard",
|
moduleName: "Apps",
|
||||||
modulePathUrl: "/admin/static-page",
|
modulePathUrl: "/admin/magazine",
|
||||||
parentId: -1,
|
parentId: -1,
|
||||||
icon: <StaticPageIcon />,
|
icon: <MagazineIcon size={23} />,
|
||||||
position: 1,
|
position: 1,
|
||||||
statusId: 1,
|
statusId: 1,
|
||||||
childMenu: [],
|
childMenu: [],
|
||||||
statusName: "Active",
|
statusName: "Active",
|
||||||
childModule: null,
|
childModule: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// id: 4,
|
// id: 4,
|
||||||
// name: "E-Magazine",
|
// name: "E-Magazine",
|
||||||
|
|
@ -199,13 +201,13 @@ const sideBarDummyData = [
|
||||||
// childModule: null,
|
// childModule: null,
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
id: 30,
|
id: 11,
|
||||||
name: "Master Category",
|
name: "Master Static Page",
|
||||||
moduleId: 654,
|
moduleId: 652,
|
||||||
moduleName: "Master",
|
moduleName: "Dashboard",
|
||||||
modulePathUrl: "/admin/master-category",
|
modulePathUrl: "/admin/static-page",
|
||||||
parentId: -1,
|
parentId: -1,
|
||||||
icon: <MasterCategoryIcon />,
|
icon: <StaticPageIcon size={24} />,
|
||||||
position: 1,
|
position: 1,
|
||||||
statusId: 1,
|
statusId: 1,
|
||||||
childMenu: [],
|
childMenu: [],
|
||||||
|
|
@ -249,6 +251,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
const [sidebarMenu, setSidebarMenu] = useState<SidebarMenuTask[]>();
|
const [sidebarMenu, setSidebarMenu] = useState<SidebarMenuTask[]>();
|
||||||
const { isOpen, toggleSidebar } = useSidebar();
|
const { isOpen, toggleSidebar } = useSidebar();
|
||||||
const token = Cookies.get("access_token");
|
const token = Cookies.get("access_token");
|
||||||
|
const fullname = Cookies.get("ufne");
|
||||||
const isAuthenticated = Cookies.get("is_authenticated");
|
const isAuthenticated = Cookies.get("is_authenticated");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -270,12 +273,12 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`hidden md:flex h-full flex-grow ${
|
className={`hidden md:flex h-screen flex-grow ${
|
||||||
isOpen ? "min-w-[240px]" : "min-w-[80px]"
|
isOpen ? "min-w-[240px]" : "min-w-[80px]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<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]"
|
isOpen ? "w-[238px]" : "w-[80px]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
@ -304,7 +307,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
</Link>
|
</Link>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<button
|
<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}
|
onClick={toggleSidebar}
|
||||||
>
|
>
|
||||||
<ChevronLeftIcon />
|
<ChevronLeftIcon />
|
||||||
|
|
@ -318,7 +321,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
list.isGroup ? (
|
list.isGroup ? (
|
||||||
<p
|
<p
|
||||||
key={list}
|
key={list}
|
||||||
className={`font-bold mr-4 ${
|
className={`font-bold mr-4 text-white ${
|
||||||
!isOpen ? "text-center" : ""
|
!isOpen ? "text-center" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
@ -338,8 +341,8 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
<div
|
<div
|
||||||
className={`px-3.5 py-2 mr-4 rounded-lg flex flex-row gap-2 ${
|
className={`px-3.5 py-2 mr-4 rounded-lg flex flex-row gap-2 ${
|
||||||
pathname.includes(list.modulePathUrl)
|
pathname.includes(list.modulePathUrl)
|
||||||
? "bg-black dark:bg-white text-white dark:text-black font-bold"
|
? "bg-white 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"
|
: "text-white hover:bg-gray-200 hover:text-black"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{list.icon} {isOpen && list.name}
|
{list.icon} {isOpen && list.name}
|
||||||
|
|
@ -356,8 +359,8 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
<div
|
<div
|
||||||
className={`py-2 mr-4 rounded-lg hover:bg-zinc-400 dark:hover:text-zinc-600 flex flex-row justify-center gap-1 ${
|
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)
|
pathname.includes(list.modulePathUrl)
|
||||||
? "bg-zinc-600 dark:bg-zinc-300 text-zinc-300 dark:text-zinc-500 font-bold"
|
? "bg-zinc-300 text-zinc-500 font-bold hover:text-black"
|
||||||
: "text-zinc-600 dark:text-zinc-400"
|
: "text-zinc-400 hover:text-black"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{list.icon} {isOpen && list.name}
|
{list.icon} {isOpen && list.name}
|
||||||
|
|
@ -399,23 +402,50 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div
|
<div
|
||||||
className={`cursor-pointer flex flex-row ${
|
className={`flex flex-row ${
|
||||||
isOpen ? "justify-start" : "justify-center"
|
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 />
|
<ThemeSwitch />
|
||||||
{isOpen && "Theme"}
|
{isOpen && "Theme"}
|
||||||
</div>
|
</div>
|
||||||
{isOpen ? (
|
{isOpen ? (
|
||||||
<div className="flex flex-row gap-3">
|
<Link href="/settings">
|
||||||
<Image
|
<div
|
||||||
src="/pengaduan.png"
|
className={`py-2 mr-4 rounded-lg flex flex-row gap-2 ${
|
||||||
width={72}
|
pathname.includes("/settings")
|
||||||
height={72}
|
? "bg-white text-black font-bold px-2"
|
||||||
alt="profile"
|
: "text-white "
|
||||||
/>
|
}`}
|
||||||
<div className="flex flex-col">
|
>
|
||||||
<a className="cursor-pointer">Nama User</a>
|
<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
|
<a
|
||||||
className="hover:text-red-600 underline text-sm cursor-pointer"
|
className="hover:text-red-600 underline text-sm cursor-pointer"
|
||||||
onClick={() => onLogout()}
|
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`}
|
} 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}
|
onClick={toggleSidebar}
|
||||||
>
|
>
|
||||||
<Image
|
<UserProfileIcon size={28} />
|
||||||
src="/pengaduan.png"
|
|
||||||
width={48}
|
|
||||||
height={48}
|
|
||||||
alt="profile"
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,151 @@
|
||||||
'use client'
|
"use client";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from '@nextui-org/breadcrumbs'
|
import { ChevronRightIcon } from "@/components/icons";
|
||||||
import { Button } from '@nextui-org/button'
|
import { getMagazineById } from "@/service/magazine";
|
||||||
import React from 'react'
|
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() {
|
export default function EMagazineDetail() {
|
||||||
return (
|
const params = useParams();
|
||||||
<div className='w-auto bg-[#E2E2E2] text-black'>
|
const id = params?.id;
|
||||||
<div className='p-1 md:p-5 lg:px-8'>
|
const [detailData, setDetailData] = useState<any>();
|
||||||
<div className='font-bold text-xl'>E-Majalah Polri</div>
|
const [detailFiles, setDetailFiles] = useState<any>([]);
|
||||||
<div className="pt-2">
|
|
||||||
<Breadcrumbs color='primary'>
|
useEffect(() => {
|
||||||
<BreadcrumbItem href='/'>Beranda</BreadcrumbItem>
|
initFetch();
|
||||||
<BreadcrumbItem href='/e-majalah-polri/daftar-majalah'>E-Majalah Polri</BreadcrumbItem>
|
}, [id]);
|
||||||
<BreadcrumbItem>Judul</BreadcrumbItem>
|
|
||||||
</Breadcrumbs>
|
const initFetch = async () => {
|
||||||
</div>
|
const res = await getMagazineById(String(id));
|
||||||
<div className='pt-5 space-y-4'>
|
const data = res?.data?.data;
|
||||||
<div className='font-semibold border-b-4 border-red-700 leading-loose'>MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023</div>
|
setDetailData(data);
|
||||||
<div className='bg-[#FFFFFF] rounded-lg h-[380px] flex items-center justify-center'>
|
setDetailFiles(data?.files);
|
||||||
<img src="/emagazine.jpeg" alt="emagazine" className='h-[380px] rounded-md py-1' />
|
};
|
||||||
</div>
|
|
||||||
<div>
|
const doDownload = async (fileName: string, title: string): Promise<void> => {
|
||||||
<Button
|
try {
|
||||||
className='w-full bg-[#DD8306] text-sm font-semibold'
|
const response = await fetch(
|
||||||
>
|
`http://38.47.180.165:8802/magazine-files/viewer/${fileName}`
|
||||||
Download
|
);
|
||||||
</Button>
|
|
||||||
</div>
|
if (!response.ok) {
|
||||||
<div className='bg-[#FFFFFF] rounded-md text-sm font-medium'>
|
throw new Error("File not found or server error");
|
||||||
<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>
|
const blob = await response.blob();
|
||||||
</div>
|
const url = window.URL.createObjectURL(blob);
|
||||||
<div className='flex justify-between border border-gray-300 p-3'>
|
const anchor = document.createElement("a");
|
||||||
<div className='w-5/6'>Download</div>
|
|
||||||
<div className='w-1/6 text-center border-l border-gray-300'>51</div>
|
anchor.href = url;
|
||||||
</div>
|
anchor.download = title === "" ? fileName : title;
|
||||||
<div className='flex justify-between border border-gray-300 p-3'>
|
document.body.appendChild(anchor);
|
||||||
<div className='w-5/6'>File Size</div>
|
anchor.click();
|
||||||
<div className='w-1/6 text-center border-l border-gray-300'>12,.58MB</div>
|
window.URL.revokeObjectURL(url);
|
||||||
</div>
|
document.body.removeChild(anchor);
|
||||||
<div className='flex justify-between border border-gray-300 p-3'>
|
} catch (error) {
|
||||||
<div className='w-5/6'>Download</div>
|
console.error("Download failed:", error);
|
||||||
<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'>
|
return (
|
||||||
<div className='w-5/6'>Created Date</div>
|
<div className="w-auto bg-[#E2E2E2] text-black">
|
||||||
<div className='w-1/6 text-center border-l border-gray-300'>1 Oktober 2024</div>
|
<div className="p-1 md:p-5 lg:px-36">
|
||||||
</div>
|
<div className="flex flex-row gap-4 items-end text-black">
|
||||||
<div className='flex justify-between border border-b border-gray-300 p-3 rounded-b-md'>
|
<Link href="/" className=" font-semibold text-lg">
|
||||||
<div className='w-5/6'>Last Update</div>
|
Beranda
|
||||||
<div className='w-1/6 text-center border-l border-gray-300'>1 Oktober 2024</div>
|
</Link>
|
||||||
</div>
|
<ChevronRightIcon />
|
||||||
</div>
|
<Link
|
||||||
</div>
|
href="/e-majalah-polri/daftar-majalah"
|
||||||
</div>
|
className=" font-semibold text-lg"
|
||||||
|
>
|
||||||
|
E-Majalah Polri
|
||||||
|
</Link>
|
||||||
</div>
|
</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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mx-5 my-5">
|
<div className="mx-3 my-5">
|
||||||
<div className="flex flex-col items-center rounded-2xl">
|
<div className="flex flex-col items-center rounded-2xl">
|
||||||
<Table
|
<Table
|
||||||
// selectionMode="multiple"
|
// selectionMode="multiple"
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ export default function MasterUserTable() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mx-5 my-5">
|
<div className="mx-3 my-5">
|
||||||
<div className="flex flex-col items-center rounded-2xl">
|
<div className="flex flex-col items-center rounded-2xl">
|
||||||
<Table
|
<Table
|
||||||
// selectionMode="multiple"
|
// 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 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-1/3">
|
||||||
<p className="font-semibold text-sm">Search</p>
|
<p className="font-semibold text-sm">Pencarian</p>
|
||||||
<Input
|
<Input
|
||||||
aria-label="Search"
|
aria-label="Pencarian..."
|
||||||
classNames={{
|
classNames={{
|
||||||
inputWrapper: "bg-default-100",
|
inputWrapper: "bg-default-100",
|
||||||
input: "text-sm",
|
input: "text-sm",
|
||||||
|
|
@ -222,7 +222,7 @@ export default function StaticPageTable() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 w-[72px]">
|
<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
|
<Select
|
||||||
label=""
|
label=""
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
|
|
@ -242,29 +242,9 @@ export default function StaticPageTable() {
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</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]">
|
<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
|
<Datepicker
|
||||||
value={startDateValue}
|
value={startDateValue}
|
||||||
displayFormat="DD/MM/YYYY"
|
displayFormat="DD/MM/YYYY"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
Button,
|
Button,
|
||||||
Input,
|
Input,
|
||||||
|
Pagination,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectSection,
|
SelectSection,
|
||||||
|
|
@ -16,7 +17,21 @@ import {
|
||||||
} from "@nextui-org/react";
|
} from "@nextui-org/react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import SidebarNav from "../landing/SidebarNav";
|
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() {
|
export default function ListEnewsPolri() {
|
||||||
const searchInput = (
|
const searchInput = (
|
||||||
|
|
@ -57,68 +72,92 @@ export default function ListEnewsPolri() {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableData = [
|
const [magazines, setMagazines] = useState<any>([]);
|
||||||
{
|
const [page, setPage] = useState(1);
|
||||||
key: "1",
|
const [search, setSearch] = useState("");
|
||||||
tahun: "MAJALAH TRIBRATA NEWS SUMSEL EDISI 33/ VII-IX/2023",
|
const [totalPage, setTotalPage] = useState(1);
|
||||||
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 header = [
|
const [startDateValue, setStartDateValue] = useState({
|
||||||
{
|
startDate: null,
|
||||||
key: "tahun",
|
endDate: null,
|
||||||
label: "Daftar E-Majalah Polri",
|
});
|
||||||
},
|
|
||||||
{
|
useEffect(() => {
|
||||||
key: "icon",
|
getMagazines();
|
||||||
label: "",
|
}, [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 (
|
return (
|
||||||
<div className="md:flex ">
|
<div className="md:flex ">
|
||||||
<div className="w-auto bg-[#E2E2E2] md:w-2/3 lg:w-[75%] text-black">
|
<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="p-1 md:py-5 lg:px-36">
|
||||||
<div className="font-bold text-xl">E-Majalah Polri</div>
|
<div className="flex flex-row gap-4 items-end text-black">
|
||||||
<div className="pt-2">
|
<Link href="/" className=" font-semibold text-lg">
|
||||||
<Breadcrumbs color="primary">
|
Beranda
|
||||||
<BreadcrumbItem href="/">Beranda</BreadcrumbItem>
|
</Link>
|
||||||
<BreadcrumbItem>E-Majalah Polri</BreadcrumbItem>
|
<ChevronRightIcon />
|
||||||
</Breadcrumbs>
|
<p className=" font-semibold text-lg">E-Majalah Polri</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-4 space-y-5">
|
<div className="pt-4 space-y-5 ">
|
||||||
<div className="font-semibold text-lg border-b-4 border-red-700 leading-loose">
|
{/* <div className="font-semibold text-lg border-b-4 border-red-700 leading-loose">
|
||||||
E-majalah Polri
|
E-majalah Polri
|
||||||
</div>
|
</div> */}
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-end gap-3 flex-col md:flex-row">
|
||||||
{searchInput}
|
<Input
|
||||||
<Select
|
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"
|
label="Sort By"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-1/5"
|
className="w-1/5"
|
||||||
|
|
@ -147,44 +186,25 @@ export default function ListEnewsPolri() {
|
||||||
<SelectItem key={list.id}>{list.label}</SelectItem>
|
<SelectItem key={list.id}>{list.label}</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectSection>
|
</SelectSection>
|
||||||
</Select>
|
</Select> */}
|
||||||
<Select
|
<div className="flex flex-col gap-1 w-full md:w-[240px]">
|
||||||
label="Tanggal Publikasi"
|
<p className="font-semibold text-xs md:text-sm">
|
||||||
size="sm"
|
Tanggal Publikasi
|
||||||
className="w-1/4"
|
</p>
|
||||||
classNames={{
|
<Datepicker
|
||||||
// base: "bg-red-500",
|
value={startDateValue}
|
||||||
// mainWrapper: "border-2 border-red-500",
|
displayFormat="DD/MM/YYYY"
|
||||||
label: "text-black",
|
useRange={false}
|
||||||
value: "!text-black",
|
asSingle={true}
|
||||||
trigger: "bg-white hover:!bg-gray-100",
|
onChange={(e: any) => setStartDateValue(e)}
|
||||||
// innerWrapper: "bg-red-500"
|
inputClassName="z-50 w-full text-sm bg-white border-1 border-gray-200 px-2 py-[6px] rounded-xl h-[40px] text-black"
|
||||||
// selectorIcon: "bg-red-500"
|
/>
|
||||||
// listboxWrapper: "bg-red-500"
|
</div>
|
||||||
// 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>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<Table
|
<Table
|
||||||
color="warning"
|
color="warning"
|
||||||
selectionMode="single"
|
|
||||||
defaultSelectedKeys={["1"]}
|
|
||||||
aria-label="Example static collection table"
|
aria-label="Example static collection table"
|
||||||
classNames={{
|
classNames={{
|
||||||
wrapper: "bg-white",
|
wrapper: "bg-white",
|
||||||
|
|
@ -203,19 +223,38 @@ export default function ListEnewsPolri() {
|
||||||
</TableColumn>
|
</TableColumn>
|
||||||
)}
|
)}
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody items={tableData}>
|
<TableBody items={magazines}>
|
||||||
{(item) => (
|
{(item: any) => (
|
||||||
<TableRow key={item.key}>
|
<TableRow key={item.id}>
|
||||||
<TableCell>{item.tahun}</TableCell>
|
<TableCell>
|
||||||
|
<Link href={`/e-majalah-polri/detail/${item.id}`}>
|
||||||
|
{item.title}
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Link href={`/e-majalah-polri/detail/${item.key}`}>
|
<Link href={`/e-majalah-polri/detail/${item.key}`}>
|
||||||
{item.icon}
|
<EyeFilledIcon color="#DD8306" />
|
||||||
</Link>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,15 @@ 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 { FormLayoutIcon } from "../icons";
|
||||||
|
import {
|
||||||
|
ArticleIcon,
|
||||||
|
DashboardIcon,
|
||||||
|
MagazineIcon,
|
||||||
|
MasterCategoryIcon,
|
||||||
|
MasterRoleIcon,
|
||||||
|
MasterUsersIcon,
|
||||||
|
StaticPageIcon,
|
||||||
|
} from "../icons/sidebar-icon";
|
||||||
|
|
||||||
export const Breadcrumb = () => {
|
export const Breadcrumb = () => {
|
||||||
const [currentPage, setCurrentPage] = useState<React.Key>("");
|
const [currentPage, setCurrentPage] = useState<React.Key>("");
|
||||||
|
|
@ -58,7 +67,16 @@ export const Breadcrumb = () => {
|
||||||
)}
|
)}
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</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>
|
</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,
|
httpGet,
|
||||||
httpPost,
|
httpPost,
|
||||||
} from "./http-config/axios-base-service";
|
} from "./http-config/axios-base-service";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
|
const token = Cookies.get("access_token");
|
||||||
|
|
||||||
export async function listMasterUsers(data: any) {
|
export async function listMasterUsers(data: any) {
|
||||||
const headers = {
|
const headers = {
|
||||||
|
|
@ -34,6 +37,7 @@ export async function postSignIn(data: any) {
|
||||||
export async function getProfile() {
|
export async function getProfile() {
|
||||||
const headers = {
|
const headers = {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
};
|
};
|
||||||
return await httpGet(`/users/info`, headers);
|
return await httpGet(`/users/info`, headers);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,3 +77,18 @@
|
||||||
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
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