feat:schedule, detail konten,task,blog

This commit is contained in:
Anang Yusman 2025-01-18 23:13:47 +08:00
parent 95860da6d6
commit e643eda2c6
21 changed files with 707 additions and 195 deletions

View File

@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { cn } from "@/lib/utils"; import { cn, getCookiesDecrypt } from "@/lib/utils";
import { format } from "date-fns"; import { format } from "date-fns";
import { import {
Popover, Popover,
@ -84,6 +84,7 @@ const EventModal = ({
event: any; event: any;
selectedDate: any; selectedDate: any;
}) => { }) => {
const roleId = Number(getCookiesDecrypt("urie")) || 0;
const [detail, setDetail] = useState<any>(); const [detail, setDetail] = useState<any>();
const [startDate, setStartDate] = useState<Date>(new Date()); const [startDate, setStartDate] = useState<Date>(new Date());
const [endDate, setEndDate] = useState<Date>(new Date()); const [endDate, setEndDate] = useState<Date>(new Date());
@ -1082,7 +1083,18 @@ const EventModal = ({
</div> </div>
</div> </div>
<div className="flex flex-wrap gap-2 mt-10"> <div className="flex flex-wrap gap-2 mt-10">
<Button type="submit" disabled={isPending} className="flex-1"> <Button
style={
roleId > 11
? {
display: "none",
}
: {}
}
type="submit"
disabled={isPending}
className="flex-1"
>
{isPending ? ( {isPending ? (
<> <>
<Loader2 className="me-2 h-4 w-4 animate-spin" /> <Loader2 className="me-2 h-4 w-4 animate-spin" />
@ -1095,6 +1107,13 @@ const EventModal = ({
)} )}
</Button> </Button>
<Button <Button
style={
roleId > 11
? {
display: "none",
}
: {}
}
className="flex-1 bg-red-500 text-white" className="flex-1 bg-red-500 text-white"
type="button" type="button"
onClick={() => handleDelete(event?.event?.id)} onClick={() => handleDelete(event?.event?.id)}

View File

@ -12,7 +12,11 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { format } from "date-fns"; import { format } from "date-fns";
import { Link } from "@/components/navigation"; import { Link, useRouter } from "@/components/navigation";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { deleteBlog } from "@/service/blog/blog";
import { error, loading } from "@/lib/swal";
const columns: ColumnDef<any>[] = [ const columns: ColumnDef<any>[] = [
{ {
@ -84,6 +88,48 @@ const columns: ColumnDef<any>[] = [
header: "Actions", header: "Actions",
enableHiding: false, enableHiding: false,
cell: ({ row }) => { cell: ({ row }) => {
const router = useRouter();
const MySwal = withReactContent(Swal);
async function deleteProcess(id: any) {
loading();
const resDelete = await deleteBlog(id);
if (resDelete?.error) {
error(resDelete.message);
return false;
}
success();
}
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
}
const handleDeleteBlog = (id: any) => {
MySwal.fire({
title: "Hapus Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
deleteProcess(id);
}
});
};
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@ -108,7 +154,10 @@ const columns: ColumnDef<any>[] = [
Edit Edit
</DropdownMenuItem> </DropdownMenuItem>
</Link> </Link>
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"> <DropdownMenuItem
onClick={() => handleDeleteBlog(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" /> <Trash2 className="w-4 h-4 me-1.5" />
Delete Delete
</DropdownMenuItem> </DropdownMenuItem>

View File

@ -12,14 +12,14 @@ import { Button } from "@/components/ui/button";
import InternalTable from "./internal/components/internal-table"; import InternalTable from "./internal/components/internal-table";
const CommunicationPage = () => { const CommunicationPage = () => {
const [tab, setTab] = useState("Komunikasi"); const [tab, setTab] = useState("Pertanyaan Internal");
return ( return (
<div> <div>
<SiteBreadcrumb /> <SiteBreadcrumb />
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3"> <div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
<div className="flex justify-between py-3"> <div className="flex justify-between py-3">
<p className="text-lg">{tab}</p> <p className="text-lg">{tab}</p>
{tab === "Komunikasi" && ( {tab === "Pertanyaan Internal" && (
<Link href="/shared/communication/internal/create"> <Link href="/shared/communication/internal/create">
<Button color="primary" size="md"> <Button color="primary" size="md">
<PlusIcon /> <PlusIcon />
@ -39,15 +39,15 @@ const CommunicationPage = () => {
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5"> <div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
<Button <Button
rounded="md" rounded="md"
onClick={() => setTab("Komunikasi")} onClick={() => setTab("Pertanyaan Internal")}
className={` hover:text-white className={` hover:text-white
${ ${
tab === "Komunikasi" tab === "Pertanyaan Internal"
? "bg-black text-white " ? "bg-black text-white "
: "bg-white text-black " : "bg-white text-black "
}`} }`}
> >
Komunikasi Pertanyaan Internal
</Button> </Button>
<Button <Button
rounded="md" rounded="md"
@ -74,7 +74,7 @@ const CommunicationPage = () => {
Kolaborasi Kolaborasi
</Button> </Button>
</div> </div>
{tab === "Komunikasi" && <InternalTable />} {tab === "Pertanyaan Internal" && <InternalTable />}
{tab === "Eskalasi" && <EscalationTable />} {tab === "Eskalasi" && <EscalationTable />}
{tab === "Kolaborasi" && <CollaborationTable />} {tab === "Kolaborasi" && <CollaborationTable />}
</div> </div>

View File

@ -7,6 +7,7 @@ import {
CarouselNext, CarouselNext,
CarouselPrevious, CarouselPrevious,
} from "@/components/ui/carousel"; } from "@/components/ui/carousel";
import { listCuratedContent } from "@/service/curated-content/curated-content";
import { getListContent } from "@/service/landing/landing"; import { getListContent } from "@/service/landing/landing";
import { import {
formatDateToIndonesian, formatDateToIndonesian,
@ -21,10 +22,12 @@ const AudioSliderPage = () => {
const [audioData, setAudioData] = useState<any>(); const [audioData, setAudioData] = useState<any>();
const [displayAudio, setDisplayAudio] = useState<any[]>([]); const [displayAudio, setDisplayAudio] = useState<any[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState("");
useEffect(() => { useEffect(() => {
initFetch(); initFetch();
}, []); }, [page, limit, search]);
useEffect(() => { useEffect(() => {
if (audioData?.length > 0) { if (audioData?.length > 0) {
@ -35,14 +38,12 @@ const AudioSliderPage = () => {
}, [audioData]); }, [audioData]);
const initFetch = async () => { const initFetch = async () => {
const response = await getListContent({ const response = await listCuratedContent(search, limit, page - 1, 4, "1");
page: page - 1,
size: 12,
sortBy: "createdAt",
contentTypeId: "4",
});
console.log(response); console.log(response);
setAudioData(response?.data?.data?.content);
const data = response?.data?.data;
const contentData = data?.content;
setAudioData(contentData);
}; };
const shuffleAndSetVideos = () => { const shuffleAndSetVideos = () => {

View File

@ -28,7 +28,7 @@ const VideoSliderPage = () => {
}, [allVideoData]); }, [allVideoData]);
const fetchData = async () => { const fetchData = async () => {
const response = await listCuratedContent(search, limit, page - 1, 1, "2"); const response = await listCuratedContent(search, limit, page - 1, 2, "1");
console.log(response); console.log(response);
const data = response?.data?.data; const data = response?.data?.data;

View File

@ -30,6 +30,7 @@ import {
} from "@/service/content/content"; } from "@/service/content/content";
import { getBlog, postBlog } from "@/service/blog/blog"; import { getBlog, postBlog } from "@/service/blog/blog";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import dynamic from "next/dynamic";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -76,6 +77,13 @@ const initialCategories: Category[] = [
}, },
]; ];
const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
);
export default function FormBlogDetail() { export default function FormBlogDetail() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -236,12 +244,7 @@ export default function FormBlogDetail() {
control={control} control={control}
name="narration" name="narration"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<JoditEditor <ViewEditor initialData={detail?.narration} />
ref={editor}
value={detail.narration}
onChange={onChange}
className="dark:text-black"
/>
)} )}
/> />
{errors.narration?.message && ( {errors.narration?.message && (
@ -300,7 +303,7 @@ export default function FormBlogDetail() {
</div> </div>
</Card> </Card>
<div className="w-4/12"> <div className="w-4/12">
<Card className=" h-[600px]"> <Card className=" h-[650px]">
<div className="px-3 py-3"> <div className="px-3 py-3">
<Label htmlFor="fileInput">Gambar Utama</Label> <Label htmlFor="fileInput">Gambar Utama</Label>
<Input <Input

View File

@ -28,8 +28,10 @@ import {
getTagsBySubCategoryId, getTagsBySubCategoryId,
listEnableCategory, listEnableCategory,
} from "@/service/content/content"; } from "@/service/content/content";
import { getBlog, postBlog } from "@/service/blog/blog"; import { getBlog, postBlog, uploadThumbnailBlog } from "@/service/blog/blog";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import dynamic from "next/dynamic";
import { loading } from "@/lib/swal";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -38,7 +40,7 @@ const taskSchema = z.object({
narration: z narration: z
.string() .string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }), .min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
categoryName: z.string().min(1, { message: "Kategori diperlukan" }), categoryId: z.string().min(1, { message: "Kategori diperlukan" }),
}); });
type Category = { type Category = {
@ -77,6 +79,13 @@ const initialCategories: Category[] = [
}, },
]; ];
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormBlogUpdate() { export default function FormBlogUpdate() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -98,6 +107,9 @@ export default function FormBlogUpdate() {
const [detail, setDetail] = useState<Detail>(); const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false); const [refresh, setRefresh] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const [thumbnail, setThumbnail] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null);
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
allUnit: false, allUnit: false,
@ -117,18 +129,6 @@ export default function FormBlogUpdate() {
resolver: zodResolver(taskSchema), resolver: zodResolver(taskSchema),
}); });
const handleRemoveTag = (index: any) => {
setTags((prevTags) => prevTags.filter((_, i) => i !== index));
};
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => { const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index)); setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
}; };
@ -141,15 +141,20 @@ export default function FormBlogUpdate() {
setDetail(details); setDetail(details);
// Set categoryId dari API ke form dan Select if (details?.tags) {
setValue("categoryName", details?.categoryName); setTags(details.tags.split(",").map((tag: string) => tag.trim()));
setSelectedTarget(details?.categoryId); // Untuk dropdown }
setValue("categoryId", details?.categoryName);
setSelectedTarget(details?.categoryId);
} }
} }
initState(); initState();
}, [refresh, setValue]); }, [refresh, setValue]);
const save = async (data: TaskSchema) => { const save = async (data: TaskSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = { const requestData = {
...data, ...data,
id: detail?.id, id: detail?.id,
@ -158,7 +163,7 @@ export default function FormBlogUpdate() {
categoryId: selectedTarget, categoryId: selectedTarget,
slug: data.slug, slug: data.slug,
metadata: data.metadata, metadata: data.metadata,
tags: "siap", tags: finalTags,
isDraft, isDraft,
}; };
@ -166,6 +171,30 @@ export default function FormBlogUpdate() {
console.log("Form Data Submitted:", requestData); console.log("Form Data Submitted:", requestData);
console.log("response", response); console.log("response", response);
if (response?.error) {
MySwal.fire("Error", response?.message, "error");
return;
}
const blogId = response?.data?.data.id;
if (blogId) {
if (thumbnail) {
const formMedia = new FormData();
formMedia.append("file", thumbnail); // Tambahkan file ke FormData
console.log("FormMedia:", formMedia.get("file"));
const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia);
if (responseThumbnail?.error) {
MySwal.fire("Error", responseThumbnail?.message, "error");
return;
}
} else {
console.log("No thumbnail to upload");
}
}
close();
MySwal.fire({ MySwal.fire({
title: "Sukses", title: "Sukses",
text: "Data berhasil disimpan.", text: "Data berhasil disimpan.",
@ -193,6 +222,17 @@ export default function FormBlogUpdate() {
}); });
}; };
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setThumbnail(file); // Simpan file ke state
setPreview(URL.createObjectURL(file)); // Buat URL untuk preview
console.log("Selected Thumbnail:", file);
} else {
console.log("No file selected");
}
};
const handlePublish = () => { const handlePublish = () => {
setIsDraft(false); setIsDraft(false);
}; };
@ -201,6 +241,29 @@ export default function FormBlogUpdate() {
setIsDraft(false); setIsDraft(false);
}; };
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && e.currentTarget.value.trim()) {
e.preventDefault();
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Tambahkan tag baru
if (inputRef.current) {
inputRef.current.value = ""; // Kosongkan input
}
}
}
};
const handleRemoveTag = (index: number) => {
setTags((prevTags) => prevTags.filter((_, i) => i !== index));
};
const handleEditTag = (index: number, newValue: string) => {
setTags((prevTags) =>
prevTags.map((tag, i) => (i === index ? newValue : tag))
);
};
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
{detail !== undefined ? ( {detail !== undefined ? (
@ -238,11 +301,9 @@ export default function FormBlogUpdate() {
control={control} control={control}
name="narration" name="narration"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<JoditEditor <CustomEditor
ref={editor}
value={detail.narration}
onChange={onChange} onChange={onChange}
className="dark:text-black" initialData={detail?.narration || value}
/> />
)} )}
/> />
@ -302,31 +363,39 @@ export default function FormBlogUpdate() {
</div> </div>
</Card> </Card>
<div className="w-4/12"> <div className="w-4/12">
<Card className=" h-[600px]"> <Card className=" h-[700px]">
<div className="px-3 py-3"> <div className="px-3 py-3">
<Label htmlFor="fileInput">Gambar Utama</Label> <Label htmlFor="fileInput">Gambar Utama</Label>
<Input <input
id="fileInput" id="fileInput"
type="file" type="file"
// onChange={(e) => { onChange={handleImageChange}
// const file = e.target.files[0];
// if (file) {
// console.log("Selected File:", file);
// // Tambahkan logika jika diperlukan, misalnya upload file ke server
// }
// }}
className=""
/> />
</div>
<div className="mt-3 px-3"> {preview ? (
<Label>Pratinjau Gambar Utama</Label> // Menampilkan pratinjau gambar yang baru dipilih
<Card className="mt-2"> <div className="mt-3 px-3">
<img <img
src={detail.thumbnailLink} src={preview}
alt="Thumbnail Gambar Utama" alt="Thumbnail Preview"
className="w-full h-auto rounded" className="w-full h-auto rounded"
/> />
</Card> </div>
) : (
// Menampilkan gambar dari `detail.thumbnailLink` jika tidak ada file yang dipilih
detail?.thumbnailLink && (
<div className="mt-3 px-3">
<Label>Pratinjau Gambar Utama</Label>
<Card className="mt-2">
<img
src={detail.thumbnailLink}
alt="Thumbnail Gambar Utama"
className="w-full h-auto rounded"
/>
</Card>
</div>
)
)}
</div> </div>
<div className="px-3 py-3 mt-6"> <div className="px-3 py-3 mt-6">
<label <label
@ -336,7 +405,7 @@ export default function FormBlogUpdate() {
Kategori Kategori
</label> </label>
<Controller <Controller
name="categoryName" name="categoryId"
control={control} control={control}
render={({ field }) => ( render={({ field }) => (
<Select <Select
@ -350,12 +419,9 @@ export default function FormBlogUpdate() {
<SelectValue placeholder="Pilih" /> <SelectValue placeholder="Pilih" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{categories.map((category) => ( {categories.map((category: any) => (
<SelectItem <SelectItem key={category.id} value={category.id}>
key={category.id} {category.categoryName}
value={category.categoryName}
>
{category.id}
</SelectItem> </SelectItem>
))} ))}
</SelectContent> </SelectContent>
@ -366,14 +432,34 @@ export default function FormBlogUpdate() {
<div className="px-3 py-3"> <div className="px-3 py-3">
<div className="space-y-2"> <div className="space-y-2">
<Label>Tag</Label> <Label>Tag</Label>
<div className="flex flex-wrap gap-2"> <Input
{detail?.tags?.split(",").map((tag, index) => ( type="text"
<Badge id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 flex flex-wrap gap-2">
{tags.map((tag, index) => (
<span
key={index} key={index}
className="border rounded-md px-2 py-2" className="flex items-center gap-2 px-2 py-1 rounded-lg bg-black text-white text-sm"
> >
{tag.trim()} <input
</Badge> type="text"
value={tag}
onChange={(e) => handleEditTag(index, e.target.value)}
className="bg-black text-white border-none focus:outline-none w-auto"
/>
<button
value={tag}
type="button"
onClick={() => handleRemoveTag(index)}
className="remove-tag-button text-white"
>
×
</button>
</span>
))} ))}
</div> </div>
</div> </div>

View File

@ -29,6 +29,9 @@ import {
listEnableCategory, listEnableCategory,
} from "@/service/content/content"; } from "@/service/content/content";
import { postBlog, uploadThumbnailBlog } from "@/service/blog/blog"; import { postBlog, uploadThumbnailBlog } from "@/service/blog/blog";
import dynamic from "next/dynamic";
import { error } from "console";
import { loading } from "@/lib/swal";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -37,7 +40,6 @@ const taskSchema = z.object({
narration: z narration: z
.string() .string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }), .min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
tags: z.string().min(1, { message: "Judul diperlukan" }),
}); });
type Category = { type Category = {
@ -64,6 +66,13 @@ const initialCategories: Category[] = [
}, },
]; ];
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormBlog() { export default function FormBlog() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -82,6 +91,7 @@ export default function FormBlog() {
const [isDraft, setIsDraft] = useState(false); const [isDraft, setIsDraft] = useState(false);
const [thumbnail, setThumbnail] = useState<File | null>(null); const [thumbnail, setThumbnail] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null); const [preview, setPreview] = useState<string | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
allUnit: false, allUnit: false,
@ -156,6 +166,8 @@ export default function FormBlog() {
// }; // };
const save = async (data: TaskSchema) => { const save = async (data: TaskSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = { const requestData = {
...data, ...data,
title: data.title, title: data.title,
@ -163,7 +175,7 @@ export default function FormBlog() {
categoryId: selectedTarget, categoryId: selectedTarget,
slug: data.slug, slug: data.slug,
metadata: data.meta, metadata: data.meta,
tags: data.tags, tags: finalTags,
isDraft, isDraft,
}; };
@ -177,19 +189,33 @@ export default function FormBlog() {
} }
const blogId = response?.data?.data.id; const blogId = response?.data?.data.id;
if (blogId && thumbnail) { if (blogId) {
const formMedia = new FormData(); if (thumbnail) {
formMedia.append("file", thumbnail); const formMedia = new FormData();
formMedia.append("file", thumbnail); // Tambahkan file ke FormData
console.log("FormMedia:", formMedia.get("file"));
const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia); const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia);
if (responseThumbnail?.error) { if (responseThumbnail?.error) {
MySwal.fire("Error", responseThumbnail?.message, "error"); MySwal.fire("Error", responseThumbnail?.message, "error");
return; return;
}
} else {
console.log("No thumbnail to upload");
} }
} }
close();
MySwal.fire("Sukses", "Data berhasil disimpan.", "success"); MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/contributor/blog");
});
}; };
const onSubmit = (data: TaskSchema) => { const onSubmit = (data: TaskSchema) => {
@ -198,8 +224,9 @@ export default function FormBlog() {
text: "Apakah Anda yakin ingin menyimpan data ini?", text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning", icon: "warning",
showCancelButton: true, showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan", confirmButtonText: "Simpan",
cancelButtonText: "Batal",
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
save(data); save(data);
@ -210,11 +237,11 @@ export default function FormBlog() {
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (file) { if (file) {
setThumbnail(file); setThumbnail(file); // Simpan file ke state
setPreview(URL.createObjectURL(file)); // Buat URL untuk preview
console.log("Selected Thumbnail:", file); console.log("Selected Thumbnail:", file);
} } else {
if (file) { console.log("No file selected");
setPreview(URL.createObjectURL(file));
} }
}; };
@ -226,6 +253,19 @@ export default function FormBlog() {
setIsDraft(true); setIsDraft(true);
}; };
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && e.currentTarget.value.trim()) {
e.preventDefault();
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Add new tag
if (inputRef.current) {
inputRef.current.value = ""; // Clear input field
}
}
}
};
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="flex lg:flex-row gap-10"> <div className="flex lg:flex-row gap-10">
@ -260,12 +300,7 @@ export default function FormBlog() {
control={control} control={control}
name="narration" name="narration"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<JoditEditor <CustomEditor onChange={onChange} initialData={value} />
ref={editor}
value={value}
onChange={onChange}
className="dark:text-black"
/>
)} )}
/> />
{errors.narration?.message && ( {errors.narration?.message && (
@ -324,10 +359,10 @@ export default function FormBlog() {
</div> </div>
</Card> </Card>
<div className="w-4/12"> <div className="w-4/12">
<Card className=" h-[550px]"> <Card className=" h-[600px]">
<div className="px-3 py-3"> <div className="px-3 py-3">
<Label htmlFor="fileInput">Gambar Utama</Label> <label htmlFor="fileInput">Gambar Utama</label>
<Input id="fileInput" type="file" onChange={handleImageChange} /> <input id="fileInput" type="file" onChange={handleImageChange} />
</div> </div>
{preview && ( {preview && (
<div className="mt-3 px-3"> <div className="mt-3 px-3">
@ -366,19 +401,30 @@ export default function FormBlog() {
</div> </div>
<div className="px-3 py-3"> <div className="px-3 py-3">
<Label>Tags</Label> <Label>Tags</Label>
<Controller <Input
control={control} type="text"
name="tags" id="tags"
render={({ field }) => ( placeholder="Add a tag and press Enter"
<Input onKeyDown={handleAddTag}
size="md" ref={inputRef}
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/> />
<div className="mt-3 ">
{tags.map((tag, index) => (
<span
key={index}
className=" px-1 py-1 rounded-lg bg-black text-white mr-2 text-sm font-sans"
>
{tag}{" "}
<button
type="button"
onClick={() => handleRemoveTag(index)}
className="remove-tag-button"
>
×
</button>
</span>
))}
</div>
{/* <div className="text-sm text-red-500"> {/* <div className="text-sm text-red-500">
{tags.length === 0 && "Please add at least one tag."} {tags.length === 0 && "Please add at least one tag."}
</div> </div>

View File

@ -20,6 +20,7 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Avatar, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarImage } from "@/components/ui/avatar";
import { import {
getEscalationDiscussion,
getTicketingDetail, getTicketingDetail,
getTicketingInternalDetail, getTicketingInternalDetail,
getTicketingInternalDiscussion, getTicketingInternalDiscussion,
@ -28,6 +29,8 @@ import {
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import { loading } from "@/lib/swal";
import { id } from "date-fns/locale";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -57,9 +60,24 @@ export default function FormDetailEscalation() {
const [detail, setDetail] = useState<any>(); const [detail, setDetail] = useState<any>();
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]); const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
const [replyVisible, setReplyVisible] = useState(false); const [replyVisible, setReplyVisible] = useState(false);
const [replyMessage, setReplyMessage] = useState(""); const [listDiscussion, setListDiscussion] = useState();
const [message, setMessage] = useState("");
const [selectedPriority, setSelectedPriority] = useState(""); const [selectedPriority, setSelectedPriority] = useState("");
const [selectedStatus, setSelectedStatus] = useState(""); const [replyMessage, setReplyMessage] = useState("");
const [replies, setReplies] = useState([
{
id: 1,
name: "Mabes Polri - Approver",
message: "test",
timestamp: "2024-12-20 00:56:10",
},
{
id: 2,
name: "Mabes Polri - Approver",
message: "balas",
timestamp: "2025-01-18 17:42:48",
},
]);
const { const {
control, control,
@ -90,53 +108,32 @@ export default function FormDetailEscalation() {
const handleReply = () => { const handleReply = () => {
setReplyVisible((prev) => !prev); // Toggle visibility setReplyVisible((prev) => !prev); // Toggle visibility
}; };
// useEffect(() => {
// async function initState() {
// if (id != undefined) {
// loading();
// const responseGet = await getEscalationDiscussion(id);
// close();
const handleSendReply = async () => { // console.log("escal data", responseGet?.data);
if (replyMessage.trim() === "") { // setListDiscussion(responseGet?.data?.data);
MySwal.fire({ // }
title: "Error", // }
text: "Pesan tidak boleh kosong!", // initState();
icon: "error", // }, [id]);
});
return;
}
const data = { const handleSendReply = () => {
ticketId: id, if (replyMessage.trim() === "") return;
const newReply = {
id: replies.length + 1,
name: "Mabes Polri - Approver", // Sesuaikan dengan data dinamis jika ada
message: replyMessage, message: replyMessage,
timestamp: new Date().toISOString().slice(0, 19).replace("T", " "),
}; };
try { setReplies([...replies, newReply]);
const response = await saveTicketInternalReply(data); setReplyMessage("");
// Tambahkan balasan baru ke daftar balasan
const newReply: replyDetail = {
id: response?.data?.id,
message: replyMessage,
createdAt: response?.data?.createdAt,
messageFrom: response?.data?.messageFrom,
messageTo: response?.data?.messageTo,
};
setTicketReply((prevReplies) => [newReply, ...prevReplies]);
MySwal.fire({
title: "Sukses",
text: "Pesan berhasil dikirim.",
icon: "success",
});
// Reset input dan sembunyikan form balasan
setReplyMessage("");
setReplyVisible(false);
} catch (error) {
MySwal.fire({
title: "Error",
text: "Gagal mengirim balasan.",
icon: "error",
});
console.error("Error sending reply:", error);
}
}; };
return ( return (
@ -244,26 +241,46 @@ export default function FormDetailEscalation() {
</p> </p>
)} */} )} */}
</div> </div>
<div className="mt-4 px-3 mb-3"> <div className="mx-3 my-3">
<Label htmlFor="replyMessage">Tulis Pesan</Label> <h3 className="text-gray-700 font-medium">Tanggapan</h3>
<div className="space-y-4">
{replies.map((reply) => (
<div key={reply.id} className="border-b pb-2">
<p className="font-semibold text-gray-800">{reply.name}</p>
<p className="text-gray-600">{reply.message}</p>
<p className="text-sm text-gray-400">{reply.timestamp}</p>
</div>
))}
</div>
</div>
<div className="mx-3">
<label
htmlFor="replyMessage"
className="block text-gray-700 font-medium mb-2"
>
Tulis Tanggapan Anda
</label>
<textarea <textarea
id="replyMessage" id="replyMessage"
className="w-full h-24 border rounded-md p-2" className="w-full h-24 border border-gray-300 rounded-md p-2"
value={replyMessage} value={replyMessage}
onChange={(e) => setReplyMessage(e.target.value)} onChange={(e) => setReplyMessage(e.target.value)}
placeholder="Tulis pesan di sini..." placeholder="Tulis tanggapan anda di sini..."
/> />
<div className="flex justify-end gap-3 mt-2"> <div className="flex justify-end gap-3 mt-2 mb-3">
<Button <button
onClick={() => setReplyVisible(false)} onClick={() => setReplyMessage("")}
color="default" className="px-4 py-2 bg-gray-200 text-gray-800 rounded-md"
variant="outline"
> >
Batal Batal
</Button> </button>
<Button onClick={handleSendReply} color="primary"> <button
Kirim onClick={handleSendReply}
</Button> className="px-4 py-2 bg-blue-600 text-white rounded-md"
>
Kirim Pesan
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -9,7 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod"; import * as z from "zod";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation"; import { useParams } from "next/navigation";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -55,6 +55,7 @@ import { getCookiesDecrypt } from "@/lib/utils";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { useRouter } from "@/i18n/routing";
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -683,7 +684,7 @@ export default function FormVideoDetail() {
<Icon icon="humbleicons:times" color="red" /> <Icon icon="humbleicons:times" color="red" />
</a> </a>
</div> </div>
{isUserMabesApprover && {isUserMabesApprover && (
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Checkbox <Checkbox
@ -760,7 +761,7 @@ export default function FormVideoDetail() {
</label> </label>
</div> </div>
</div> </div>
} )}
</div> </div>
</div> </div>
)) ))

View File

@ -17,7 +17,7 @@ import {
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CalendarIcon } from "lucide-react"; import { CalendarIcon, Clock1, MapPin, User2 } from "lucide-react";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { addDays, format, parseISO, setDate } from "date-fns"; import { addDays, format, parseISO, setDate } from "date-fns";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
@ -28,7 +28,19 @@ import MapHome from "@/components/maps/MapHome";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { detailSchedule, postSchedule } from "@/service/schedule/schedule"; import {
detailSchedule,
listScheduleNext,
listScheduleToday,
postSchedule,
} from "@/service/schedule/schedule";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { formatDate } from "@fullcalendar/core/index.js";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -56,6 +68,8 @@ export default function FormEventDetail() {
const [startTime, setStartTime] = useState("08:00"); const [startTime, setStartTime] = useState("08:00");
const [endTime, setEndTime] = useState("09:00"); const [endTime, setEndTime] = useState("09:00");
const [date, setDate] = useState<DateRange | undefined>(); const [date, setDate] = useState<DateRange | undefined>();
const [todayList, setTodayList] = useState([]);
const [nextDayList, setNextDayList] = useState([]);
const [detail, setDetail] = useState<Detail>(); const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false); const [refresh, setRefresh] = useState(false);
@ -72,6 +86,16 @@ export default function FormEventDetail() {
}, },
}); });
async function getDataByDate() {
const resToday = await listScheduleToday();
const today = resToday?.data?.data;
setTodayList(today);
const resNext = await listScheduleNext();
const next = resNext?.data?.data;
setNextDayList(next);
}
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
if (id) { if (id) {
@ -89,6 +113,7 @@ export default function FormEventDetail() {
setStartTime(details.startTime); setStartTime(details.startTime);
setEndTime(details.endTime); setEndTime(details.endTime);
} }
getDataByDate();
} }
} }
initState(); initState();
@ -315,7 +340,66 @@ export default function FormEventDetail() {
</div> </div>
</Card> </Card>
<Card className="w-full lg:w-3/12"> <Card className="w-full lg:w-3/12">
<div className="px-3 py-3">Jadwal Selanjutnya</div> <Accordion type="single" collapsible>
{/* Jadwal Hari Ini */}
<AccordionItem value="today">
<AccordionTrigger className="font-semibold">
Jadwal Hari Ini
</AccordionTrigger>
<AccordionContent>
{todayList?.length > 0 ? (
<ul className="list-disc ml-4">
{todayList.map((item: any, index) => (
<li key={index} className="py-1">
{item.name}
</li>
))}
</ul>
) : (
<p className="text-gray-500">Tidak ada jadwal hari ini</p>
)}
</AccordionContent>
</AccordionItem>
{/* Jadwal Selanjutnya */}
<AccordionItem value="next">
<AccordionTrigger className="font-semibold">
Jadwal Selanjutnya
</AccordionTrigger>
<AccordionContent>
{nextDayList?.length > 0 ? (
<div className="list-disc ">
{nextDayList.map((item: any, index) => (
<div key={index} className="">
<li className="text-base font-semibold">{item.title}</li>
<p className="text-sm ml-5 flex my-2">
<CalendarIcon size={20} />
{formatDate(item?.startDate)}-
{formatDate(item?.endDate)}
</p>
<p className="text-sm ml-5 flex my-2">
<Clock1 size={20} />
{item?.startTime}-{item?.endTime}
</p>
<p className="text-sm ml-5 flex items-center my-2">
<MapPin size={50} />
{item?.address}
</p>
<p className="text-sm ml-5">Disampaikan oleh:</p>
<p className="text-sm ml-5 flex my-2">
<User2 />
{item?.speakerTitle} {item?.speakerName}
</p>
</div>
// <p>{item.startDate}</p>
))}
</div>
) : (
<p className="text-gray-500">Tidak ada jadwal selanjutnya</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</Card> </Card>
</div> </div>
); );

View File

@ -9,7 +9,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod"; import * as z from "zod";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { useRouter } from "next/navigation";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -40,6 +39,7 @@ import { Textarea } from "@/components/ui/textarea";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { postSchedule } from "@/service/schedule/schedule"; import { postSchedule } from "@/service/schedule/schedule";
import { useRouter } from "@/i18n/routing";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),

View File

@ -17,7 +17,7 @@ import {
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CalendarIcon } from "lucide-react"; import { CalendarIcon, Clock1, MapPin, User2 } from "lucide-react";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { addDays, format, parseISO, setDate } from "date-fns"; import { addDays, format, parseISO, setDate } from "date-fns";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
@ -28,7 +28,12 @@ import MapHome from "@/components/maps/MapHome";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { detailSchedule, postSchedule } from "@/service/schedule/schedule"; import {
detailSchedule,
listScheduleNext,
listScheduleToday,
postSchedule,
} from "@/service/schedule/schedule";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -36,6 +41,13 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { formatDate } from "@fullcalendar/core/index.js";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -64,6 +76,8 @@ export default function FormDetailPressRillis() {
const [endTime, setEndTime] = useState("09:00"); const [endTime, setEndTime] = useState("09:00");
const [date, setDate] = useState<DateRange | undefined>(); const [date, setDate] = useState<DateRange | undefined>();
const [selectedTarget, setSelectedTarget] = useState("all"); const [selectedTarget, setSelectedTarget] = useState("all");
const [todayList, setTodayList] = useState([]);
const [nextDayList, setNextDayList] = useState([]);
const [detail, setDetail] = useState<Detail>(); const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false); const [refresh, setRefresh] = useState(false);
@ -80,6 +94,16 @@ export default function FormDetailPressRillis() {
}, },
}); });
async function getDataByDate() {
const resToday = await listScheduleToday();
const today = resToday?.data?.data;
setTodayList(today);
const resNext = await listScheduleNext();
const next = resNext?.data?.data;
setNextDayList(next);
}
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
if (id) { if (id) {
@ -97,6 +121,7 @@ export default function FormDetailPressRillis() {
setStartTime(details.startTime); setStartTime(details.startTime);
setEndTime(details.endTime); setEndTime(details.endTime);
} }
getDataByDate();
} }
} }
initState(); initState();
@ -340,7 +365,66 @@ export default function FormDetailPressRillis() {
</div> </div>
</Card> </Card>
<Card className="w-full lg:w-3/12"> <Card className="w-full lg:w-3/12">
<div className="px-3 py-3">Jadwal Selanjutnya</div> <Accordion type="single" collapsible>
{/* Jadwal Hari Ini */}
<AccordionItem value="today">
<AccordionTrigger className="font-semibold">
Jadwal Hari Ini
</AccordionTrigger>
<AccordionContent>
{todayList?.length > 0 ? (
<ul className="list-disc ml-4">
{todayList.map((item: any, index) => (
<li key={index} className="py-1">
{item.name}
</li>
))}
</ul>
) : (
<p className="text-gray-500">Tidak ada jadwal hari ini</p>
)}
</AccordionContent>
</AccordionItem>
{/* Jadwal Selanjutnya */}
<AccordionItem value="next">
<AccordionTrigger className="font-semibold">
Jadwal Selanjutnya
</AccordionTrigger>
<AccordionContent>
{nextDayList?.length > 0 ? (
<div className="list-disc ">
{nextDayList.map((item: any, index) => (
<div key={index} className="">
<li className="text-base font-semibold">{item.title}</li>
<p className="text-sm ml-5 flex my-2">
<CalendarIcon size={20} />
{formatDate(item?.startDate)}-
{formatDate(item?.endDate)}
</p>
<p className="text-sm ml-5 flex my-2">
<Clock1 size={20} />
{item?.startTime}-{item?.endTime}
</p>
<p className="text-sm ml-5 flex items-center my-2">
<MapPin size={50} />
{item?.address}
</p>
<p className="text-sm ml-5">Disampaikan oleh:</p>
<p className="text-sm ml-5 flex my-2">
<User2 />
{item?.speakerTitle} {item?.speakerName}
</p>
</div>
// <p>{item.startDate}</p>
))}
</div>
) : (
<p className="text-gray-500">Tidak ada jadwal selanjutnya</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</Card> </Card>
</div> </div>
); );

View File

@ -17,7 +17,7 @@ import {
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CalendarIcon } from "lucide-react"; import { CalendarIcon, Clock1, Locate, MapPin, User2 } from "lucide-react";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { addDays, format, parseISO, setDate } from "date-fns"; import { addDays, format, parseISO, setDate } from "date-fns";
import { DateRange } from "react-day-picker"; import { DateRange } from "react-day-picker";
@ -28,7 +28,20 @@ import MapHome from "@/components/maps/MapHome";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { error, loading } from "@/lib/swal"; import { error, loading } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { detailSchedule, postSchedule } from "@/service/schedule/schedule"; import {
detailSchedule,
listScheduleNext,
listScheduleToday,
postSchedule,
} from "@/service/schedule/schedule";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { formatDateToIndonesian } from "@/utils/globals";
import { formatDate } from "@fullcalendar/core/index.js";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -56,6 +69,8 @@ export default function FormDetailPressConference() {
const [startTime, setStartTime] = useState("08:00"); const [startTime, setStartTime] = useState("08:00");
const [endTime, setEndTime] = useState("09:00"); const [endTime, setEndTime] = useState("09:00");
const [date, setDate] = useState<DateRange | undefined>(); const [date, setDate] = useState<DateRange | undefined>();
const [todayList, setTodayList] = useState([]);
const [nextDayList, setNextDayList] = useState([]);
const [detail, setDetail] = useState<Detail>(); const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false); const [refresh, setRefresh] = useState(false);
@ -72,6 +87,16 @@ export default function FormDetailPressConference() {
}, },
}); });
async function getDataByDate() {
const resToday = await listScheduleToday();
const today = resToday?.data?.data;
setTodayList(today);
const resNext = await listScheduleNext();
const next = resNext?.data?.data;
setNextDayList(next);
}
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
if (id) { if (id) {
@ -89,6 +114,7 @@ export default function FormDetailPressConference() {
setStartTime(details.startTime); setStartTime(details.startTime);
setEndTime(details.endTime); setEndTime(details.endTime);
} }
getDataByDate();
} }
} }
initState(); initState();
@ -315,7 +341,66 @@ export default function FormDetailPressConference() {
</div> </div>
</Card> </Card>
<Card className="w-full lg:w-3/12"> <Card className="w-full lg:w-3/12">
<div className="px-3 py-3">Jadwal Selanjutnya</div> <Accordion type="single" collapsible>
{/* Jadwal Hari Ini */}
<AccordionItem value="today">
<AccordionTrigger className="font-semibold">
Jadwal Hari Ini
</AccordionTrigger>
<AccordionContent>
{todayList?.length > 0 ? (
<ul className="list-disc ml-4">
{todayList.map((item: any, index) => (
<li key={index} className="py-1">
{item.name}
</li>
))}
</ul>
) : (
<p className="text-gray-500">Tidak ada jadwal hari ini</p>
)}
</AccordionContent>
</AccordionItem>
{/* Jadwal Selanjutnya */}
<AccordionItem value="next">
<AccordionTrigger className="font-semibold">
Jadwal Selanjutnya
</AccordionTrigger>
<AccordionContent>
{nextDayList?.length > 0 ? (
<div className="list-disc ">
{nextDayList.map((item: any, index) => (
<div key={index} className="">
<li className="text-base font-semibold">{item.title}</li>
<p className="text-sm ml-5 flex my-2">
<CalendarIcon size={20} />
{formatDate(item?.startDate)}-
{formatDate(item?.endDate)}
</p>
<p className="text-sm ml-5 flex my-2">
<Clock1 size={20} />
{item?.startTime}-{item?.endTime}
</p>
<p className="text-sm ml-5 flex items-center my-2">
<MapPin size={50} />
{item?.address}
</p>
<p className="text-sm ml-5">Disampaikan oleh:</p>
<p className="text-sm ml-5 flex my-2">
<User2 />
{item?.speakerTitle} {item?.speakerName}
</p>
</div>
// <p>{item.startDate}</p>
))}
</div>
) : (
<p className="text-gray-500">Tidak ada jadwal selanjutnya</p>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
</Card> </Card>
</div> </div>
); );

View File

@ -9,7 +9,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod"; import * as z from "zod";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { useRouter } from "next/navigation";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { import {
Popover, Popover,
@ -30,6 +29,7 @@ import { Textarea } from "@/components/ui/textarea";
import { error } from "@/lib/swal"; import { error } from "@/lib/swal";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { postSchedule } from "@/service/schedule/schedule"; import { postSchedule } from "@/service/schedule/schedule";
import { useRouter } from "@/i18n/routing";
const taskSchema = z.object({ const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),

View File

@ -267,6 +267,7 @@ export default function FormTaskDetail() {
allUnit: false, allUnit: false,
mabes: false, mabes: false,
polda: false, polda: false,
satker: false,
polres: false, polres: false,
}); });
@ -389,6 +390,7 @@ export default function FormTaskDetail() {
mabes: outputSet.has(1), mabes: outputSet.has(1),
polda: outputSet.has(2), polda: outputSet.has(2),
polres: outputSet.has(3), polres: outputSet.has(3),
satker: outputSet.has(4),
}); });
} }
}, [detail?.fileTypeOutput]); }, [detail?.fileTypeOutput]);

View File

@ -147,6 +147,7 @@ export default function FormTaskEdit() {
mabes: false, mabes: false,
polda: false, polda: false,
polres: false, polres: false,
satker: false,
}); });
const [links, setLinks] = useState<string[]>([""]); const [links, setLinks] = useState<string[]>([""]);
@ -265,6 +266,7 @@ export default function FormTaskEdit() {
mabes: outputSet.has(1), mabes: outputSet.has(1),
polda: outputSet.has(2), polda: outputSet.has(2),
polres: outputSet.has(3), polres: outputSet.has(3),
satker: outputSet.has(4),
}); });
} }
}, [detail?.fileTypeOutput]); }, [detail?.fileTypeOutput]);
@ -299,6 +301,7 @@ export default function FormTaskEdit() {
mabes: "1", mabes: "1",
polda: "2", polda: "2",
polres: "3", polres: "3",
satker: "4",
}; };
const assignmentPurposeString = Object.keys(unitSelection) const assignmentPurposeString = Object.keys(unitSelection)
.filter((key) => unitSelection[key as keyof typeof unitSelection]) .filter((key) => unitSelection[key as keyof typeof unitSelection])

View File

@ -123,6 +123,7 @@ export default function FormTask() {
mabes: false, mabes: false,
polda: false, polda: false,
polres: false, polres: false,
satker: false,
}); });
const [links, setLinks] = useState<string[]>([""]); const [links, setLinks] = useState<string[]>([""]);
@ -149,6 +150,7 @@ export default function FormTask() {
try { try {
const response = await getUserLevelForAssignments(); const response = await getUserLevelForAssignments();
setListDest(response?.data?.data.list); setListDest(response?.data?.data.list);
console.log("polda", response?.data?.data?.list);
const initialExpandedState = response?.data?.data.list.reduce( const initialExpandedState = response?.data?.data.list.reduce(
(acc: any, polda: any) => { (acc: any, polda: any) => {
acc[polda.id] = false; acc[polda.id] = false;
@ -198,6 +200,7 @@ export default function FormTask() {
mabes: "1", mabes: "1",
polda: "2", polda: "2",
polres: "3", polres: "3",
satker: "4",
}; };
const assignmentPurposeString = Object.keys(unitSelection) const assignmentPurposeString = Object.keys(unitSelection)
.filter((key) => unitSelection[key as keyof typeof unitSelection]) .filter((key) => unitSelection[key as keyof typeof unitSelection])

View File

@ -1,4 +1,8 @@
import { httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service"; import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
export async function paginationBlog( export async function paginationBlog(
size: number, size: number,
@ -22,5 +26,13 @@ export async function getBlog(id: any) {
export async function uploadThumbnailBlog(id: any, data: any) { export async function uploadThumbnailBlog(id: any, data: any) {
const url = `blog/${id}/thumbnail`; const url = `blog/${id}/thumbnail`;
return httpPostInterceptor(url, data); const headers = {
"Content-Type": "multipart/form-data",
};
return httpPostInterceptor(url, data, headers);
}
export async function deleteBlog(id: any) {
const url = `blog?id=${id}`;
return httpDeleteInterceptor(url);
} }

View File

@ -115,3 +115,8 @@ export async function deleteQuestion(id: string | number) {
const url = `/question?id=${id}`; const url = `/question?id=${id}`;
return httpDeleteInterceptor(url); return httpDeleteInterceptor(url);
} }
export async function getEscalationDiscussion(id: any) {
const url = `ticketing/escalation/discussion?ticketId=${id}`;
return httpGetInterceptor(url);
}

View File

@ -1,4 +1,7 @@
import { httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service"; import {
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
import { httpGet } from "../http-config/http-base-service"; import { httpGet } from "../http-config/http-base-service";
export async function paginationSchedule( export async function paginationSchedule(
@ -24,21 +27,30 @@ export async function detailSchedule(id: any) {
export async function listScheduleTodayPublic(group = null) { export async function listScheduleTodayPublic(group = null) {
const url = `public/schedule/today?group=${group}`; const url = `public/schedule/today?group=${group}`;
return httpGet( url, null ); return httpGet(url, null);
} }
export async function listScheduleNextPublic(group = null) { export async function listScheduleNextPublic(group = null) {
const url = `public/schedule/next-activity?group=${group}`; const url = `public/schedule/next-activity?group=${group}`;
return httpGet( url, null ); return httpGet(url, null);
} }
export async function listSchedulePrevPublic(group = null) { export async function listSchedulePrevPublic(group = null) {
const url = `public/schedule/prev-activity?group=${group}`; const url = `public/schedule/prev-activity?group=${group}`;
return httpGet( url, null ); return httpGet(url, null);
} }
export async function listSchedule(group = null) { export async function listSchedule(group = null) {
const url = `public/schedule/list?group=${group}`; const url = `public/schedule/list?group=${group}`;
return httpGet( url, null ); return httpGet(url, null);
} }
export async function listScheduleToday() {
const url = "schedule/today";
return httpGetInterceptor(url);
}
export async function listScheduleNext() {
const url = "schedule/next-activity";
return httpGetInterceptor(url);
}