feat:schedule, detail konten,task,blog
This commit is contained in:
parent
95860da6d6
commit
e643eda2c6
|
|
@ -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)}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 = () => {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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" }),
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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" }),
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue