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 { Label } from "@/components/ui/label";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { format } from "date-fns";
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -84,6 +84,7 @@ const EventModal = ({
|
|||
event: any;
|
||||
selectedDate: any;
|
||||
}) => {
|
||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||
const [detail, setDetail] = useState<any>();
|
||||
const [startDate, setStartDate] = useState<Date>(new Date());
|
||||
const [endDate, setEndDate] = useState<Date>(new Date());
|
||||
|
|
@ -1082,7 +1083,18 @@ const EventModal = ({
|
|||
</div>
|
||||
</div>
|
||||
<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 ? (
|
||||
<>
|
||||
<Loader2 className="me-2 h-4 w-4 animate-spin" />
|
||||
|
|
@ -1095,6 +1107,13 @@ const EventModal = ({
|
|||
)}
|
||||
</Button>
|
||||
<Button
|
||||
style={
|
||||
roleId > 11
|
||||
? {
|
||||
display: "none",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
className="flex-1 bg-red-500 text-white"
|
||||
type="button"
|
||||
onClick={() => handleDelete(event?.event?.id)}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ import {
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
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>[] = [
|
||||
{
|
||||
|
|
@ -84,6 +88,48 @@ const columns: ColumnDef<any>[] = [
|
|||
header: "Actions",
|
||||
enableHiding: false,
|
||||
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 (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -108,7 +154,10 @@ const columns: ColumnDef<any>[] = [
|
|||
Edit
|
||||
</DropdownMenuItem>
|
||||
</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" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ import { Button } from "@/components/ui/button";
|
|||
import InternalTable from "./internal/components/internal-table";
|
||||
|
||||
const CommunicationPage = () => {
|
||||
const [tab, setTab] = useState("Komunikasi");
|
||||
const [tab, setTab] = useState("Pertanyaan Internal");
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||
<div className="flex justify-between py-3">
|
||||
<p className="text-lg">{tab}</p>
|
||||
{tab === "Komunikasi" && (
|
||||
{tab === "Pertanyaan Internal" && (
|
||||
<Link href="/shared/communication/internal/create">
|
||||
<Button color="primary" size="md">
|
||||
<PlusIcon />
|
||||
|
|
@ -39,15 +39,15 @@ const CommunicationPage = () => {
|
|||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-5">
|
||||
<Button
|
||||
rounded="md"
|
||||
onClick={() => setTab("Komunikasi")}
|
||||
onClick={() => setTab("Pertanyaan Internal")}
|
||||
className={` hover:text-white
|
||||
${
|
||||
tab === "Komunikasi"
|
||||
tab === "Pertanyaan Internal"
|
||||
? "bg-black text-white "
|
||||
: "bg-white text-black "
|
||||
}`}
|
||||
>
|
||||
Komunikasi
|
||||
Pertanyaan Internal
|
||||
</Button>
|
||||
<Button
|
||||
rounded="md"
|
||||
|
|
@ -74,7 +74,7 @@ const CommunicationPage = () => {
|
|||
Kolaborasi
|
||||
</Button>
|
||||
</div>
|
||||
{tab === "Komunikasi" && <InternalTable />}
|
||||
{tab === "Pertanyaan Internal" && <InternalTable />}
|
||||
{tab === "Eskalasi" && <EscalationTable />}
|
||||
{tab === "Kolaborasi" && <CollaborationTable />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/components/ui/carousel";
|
||||
import { listCuratedContent } from "@/service/curated-content/curated-content";
|
||||
import { getListContent } from "@/service/landing/landing";
|
||||
import {
|
||||
formatDateToIndonesian,
|
||||
|
|
@ -21,10 +22,12 @@ const AudioSliderPage = () => {
|
|||
const [audioData, setAudioData] = useState<any>();
|
||||
const [displayAudio, setDisplayAudio] = useState<any[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [limit, setLimit] = React.useState(10);
|
||||
const [search, setSearch] = React.useState("");
|
||||
|
||||
useEffect(() => {
|
||||
initFetch();
|
||||
}, []);
|
||||
}, [page, limit, search]);
|
||||
|
||||
useEffect(() => {
|
||||
if (audioData?.length > 0) {
|
||||
|
|
@ -35,14 +38,12 @@ const AudioSliderPage = () => {
|
|||
}, [audioData]);
|
||||
|
||||
const initFetch = async () => {
|
||||
const response = await getListContent({
|
||||
page: page - 1,
|
||||
size: 12,
|
||||
sortBy: "createdAt",
|
||||
contentTypeId: "4",
|
||||
});
|
||||
const response = await listCuratedContent(search, limit, page - 1, 4, "1");
|
||||
console.log(response);
|
||||
setAudioData(response?.data?.data?.content);
|
||||
|
||||
const data = response?.data?.data;
|
||||
const contentData = data?.content;
|
||||
setAudioData(contentData);
|
||||
};
|
||||
|
||||
const shuffleAndSetVideos = () => {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const VideoSliderPage = () => {
|
|||
}, [allVideoData]);
|
||||
|
||||
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);
|
||||
|
||||
const data = response?.data?.data;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import {
|
|||
} from "@/service/content/content";
|
||||
import { getBlog, postBlog } from "@/service/blog/blog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const taskSchema = z.object({
|
||||
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() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -236,12 +244,7 @@ export default function FormBlogDetail() {
|
|||
control={control}
|
||||
name="narration"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={detail.narration}
|
||||
onChange={onChange}
|
||||
className="dark:text-black"
|
||||
/>
|
||||
<ViewEditor initialData={detail?.narration} />
|
||||
)}
|
||||
/>
|
||||
{errors.narration?.message && (
|
||||
|
|
@ -300,7 +303,7 @@ export default function FormBlogDetail() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-4/12">
|
||||
<Card className=" h-[600px]">
|
||||
<Card className=" h-[650px]">
|
||||
<div className="px-3 py-3">
|
||||
<Label htmlFor="fileInput">Gambar Utama</Label>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ import {
|
|||
getTagsBySubCategoryId,
|
||||
listEnableCategory,
|
||||
} 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 dynamic from "next/dynamic";
|
||||
import { loading } from "@/lib/swal";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -38,7 +40,7 @@ const taskSchema = z.object({
|
|||
narration: z
|
||||
.string()
|
||||
.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 = {
|
||||
|
|
@ -77,6 +79,13 @@ const initialCategories: Category[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function FormBlogUpdate() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -98,6 +107,9 @@ export default function FormBlogUpdate() {
|
|||
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
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({
|
||||
allUnit: false,
|
||||
|
|
@ -117,18 +129,6 @@ export default function FormBlogUpdate() {
|
|||
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) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
};
|
||||
|
|
@ -141,15 +141,20 @@ export default function FormBlogUpdate() {
|
|||
|
||||
setDetail(details);
|
||||
|
||||
// Set categoryId dari API ke form dan Select
|
||||
setValue("categoryName", details?.categoryName);
|
||||
setSelectedTarget(details?.categoryId); // Untuk dropdown
|
||||
if (details?.tags) {
|
||||
setTags(details.tags.split(",").map((tag: string) => tag.trim()));
|
||||
}
|
||||
|
||||
setValue("categoryId", details?.categoryName);
|
||||
setSelectedTarget(details?.categoryId);
|
||||
}
|
||||
}
|
||||
initState();
|
||||
}, [refresh, setValue]);
|
||||
|
||||
const save = async (data: TaskSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const requestData = {
|
||||
...data,
|
||||
id: detail?.id,
|
||||
|
|
@ -158,7 +163,7 @@ export default function FormBlogUpdate() {
|
|||
categoryId: selectedTarget,
|
||||
slug: data.slug,
|
||||
metadata: data.metadata,
|
||||
tags: "siap",
|
||||
tags: finalTags,
|
||||
isDraft,
|
||||
};
|
||||
|
||||
|
|
@ -166,6 +171,30 @@ export default function FormBlogUpdate() {
|
|||
console.log("Form Data Submitted:", requestData);
|
||||
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({
|
||||
title: "Sukses",
|
||||
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 = () => {
|
||||
setIsDraft(false);
|
||||
};
|
||||
|
|
@ -201,6 +241,29 @@ export default function FormBlogUpdate() {
|
|||
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 (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{detail !== undefined ? (
|
||||
|
|
@ -238,11 +301,9 @@ export default function FormBlogUpdate() {
|
|||
control={control}
|
||||
name="narration"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={detail.narration}
|
||||
<CustomEditor
|
||||
onChange={onChange}
|
||||
className="dark:text-black"
|
||||
initialData={detail?.narration || value}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -302,31 +363,39 @@ export default function FormBlogUpdate() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-4/12">
|
||||
<Card className=" h-[600px]">
|
||||
<Card className=" h-[700px]">
|
||||
<div className="px-3 py-3">
|
||||
<Label htmlFor="fileInput">Gambar Utama</Label>
|
||||
<Input
|
||||
<input
|
||||
id="fileInput"
|
||||
type="file"
|
||||
// onChange={(e) => {
|
||||
// const file = e.target.files[0];
|
||||
// if (file) {
|
||||
// console.log("Selected File:", file);
|
||||
// // Tambahkan logika jika diperlukan, misalnya upload file ke server
|
||||
// }
|
||||
// }}
|
||||
className=""
|
||||
onChange={handleImageChange}
|
||||
/>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
{preview ? (
|
||||
// Menampilkan pratinjau gambar yang baru dipilih
|
||||
<div className="mt-3 px-3">
|
||||
<img
|
||||
src={preview}
|
||||
alt="Thumbnail Preview"
|
||||
className="w-full h-auto rounded"
|
||||
/>
|
||||
</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 className="px-3 py-3 mt-6">
|
||||
<label
|
||||
|
|
@ -336,7 +405,7 @@ export default function FormBlogUpdate() {
|
|||
Kategori
|
||||
</label>
|
||||
<Controller
|
||||
name="categoryName"
|
||||
name="categoryId"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
|
|
@ -350,12 +419,9 @@ export default function FormBlogUpdate() {
|
|||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((category) => (
|
||||
<SelectItem
|
||||
key={category.id}
|
||||
value={category.categoryName}
|
||||
>
|
||||
{category.id}
|
||||
{categories.map((category: any) => (
|
||||
<SelectItem key={category.id} value={category.id}>
|
||||
{category.categoryName}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
|
@ -366,14 +432,34 @@ export default function FormBlogUpdate() {
|
|||
<div className="px-3 py-3">
|
||||
<div className="space-y-2">
|
||||
<Label>Tag</Label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{detail?.tags?.split(",").map((tag, index) => (
|
||||
<Badge
|
||||
<Input
|
||||
type="text"
|
||||
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}
|
||||
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()}
|
||||
</Badge>
|
||||
<input
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import {
|
|||
listEnableCategory,
|
||||
} from "@/service/content/content";
|
||||
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({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -37,7 +40,6 @@ const taskSchema = z.object({
|
|||
narration: z
|
||||
.string()
|
||||
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
|
||||
tags: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
});
|
||||
|
||||
type Category = {
|
||||
|
|
@ -64,6 +66,13 @@ const initialCategories: Category[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const CustomEditor = dynamic(
|
||||
() => {
|
||||
return import("@/components/editor/custom-editor");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function FormBlog() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
|
@ -82,6 +91,7 @@ export default function FormBlog() {
|
|||
const [isDraft, setIsDraft] = useState(false);
|
||||
const [thumbnail, setThumbnail] = useState<File | null>(null);
|
||||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [unitSelection, setUnitSelection] = useState({
|
||||
allUnit: false,
|
||||
|
|
@ -156,6 +166,8 @@ export default function FormBlog() {
|
|||
// };
|
||||
|
||||
const save = async (data: TaskSchema) => {
|
||||
loading();
|
||||
const finalTags = tags.join(", ");
|
||||
const requestData = {
|
||||
...data,
|
||||
title: data.title,
|
||||
|
|
@ -163,7 +175,7 @@ export default function FormBlog() {
|
|||
categoryId: selectedTarget,
|
||||
slug: data.slug,
|
||||
metadata: data.meta,
|
||||
tags: data.tags,
|
||||
tags: finalTags,
|
||||
isDraft,
|
||||
};
|
||||
|
||||
|
|
@ -177,19 +189,33 @@ export default function FormBlog() {
|
|||
}
|
||||
|
||||
const blogId = response?.data?.data.id;
|
||||
if (blogId && thumbnail) {
|
||||
const formMedia = new FormData();
|
||||
formMedia.append("file", thumbnail);
|
||||
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);
|
||||
const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia);
|
||||
|
||||
if (responseThumbnail?.error) {
|
||||
MySwal.fire("Error", responseThumbnail?.message, "error");
|
||||
return;
|
||||
if (responseThumbnail?.error) {
|
||||
MySwal.fire("Error", responseThumbnail?.message, "error");
|
||||
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) => {
|
||||
|
|
@ -198,8 +224,9 @@ export default function FormBlog() {
|
|||
text: "Apakah Anda yakin ingin menyimpan data ini?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "Simpan",
|
||||
cancelButtonText: "Batal",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
save(data);
|
||||
|
|
@ -210,11 +237,11 @@ export default function FormBlog() {
|
|||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setThumbnail(file);
|
||||
setThumbnail(file); // Simpan file ke state
|
||||
setPreview(URL.createObjectURL(file)); // Buat URL untuk preview
|
||||
console.log("Selected Thumbnail:", file);
|
||||
}
|
||||
if (file) {
|
||||
setPreview(URL.createObjectURL(file));
|
||||
} else {
|
||||
console.log("No file selected");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -226,6 +253,19 @@ export default function FormBlog() {
|
|||
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 (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex lg:flex-row gap-10">
|
||||
|
|
@ -260,12 +300,7 @@ export default function FormBlog() {
|
|||
control={control}
|
||||
name="narration"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<JoditEditor
|
||||
ref={editor}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className="dark:text-black"
|
||||
/>
|
||||
<CustomEditor onChange={onChange} initialData={value} />
|
||||
)}
|
||||
/>
|
||||
{errors.narration?.message && (
|
||||
|
|
@ -324,10 +359,10 @@ export default function FormBlog() {
|
|||
</div>
|
||||
</Card>
|
||||
<div className="w-4/12">
|
||||
<Card className=" h-[550px]">
|
||||
<Card className=" h-[600px]">
|
||||
<div className="px-3 py-3">
|
||||
<Label htmlFor="fileInput">Gambar Utama</Label>
|
||||
<Input id="fileInput" type="file" onChange={handleImageChange} />
|
||||
<label htmlFor="fileInput">Gambar Utama</label>
|
||||
<input id="fileInput" type="file" onChange={handleImageChange} />
|
||||
</div>
|
||||
{preview && (
|
||||
<div className="mt-3 px-3">
|
||||
|
|
@ -366,19 +401,30 @@ export default function FormBlog() {
|
|||
</div>
|
||||
<div className="px-3 py-3">
|
||||
<Label>Tags</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="tags"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
size="md"
|
||||
type="text"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
placeholder="Add a tag and press Enter"
|
||||
onKeyDown={handleAddTag}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<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">
|
||||
{tags.length === 0 && "Please add at least one tag."}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
} from "@/components/ui/select";
|
||||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
getEscalationDiscussion,
|
||||
getTicketingDetail,
|
||||
getTicketingInternalDetail,
|
||||
getTicketingInternalDiscussion,
|
||||
|
|
@ -28,6 +29,8 @@ import {
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { loading } from "@/lib/swal";
|
||||
import { id } from "date-fns/locale";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -57,9 +60,24 @@ export default function FormDetailEscalation() {
|
|||
const [detail, setDetail] = useState<any>();
|
||||
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
|
||||
const [replyVisible, setReplyVisible] = useState(false);
|
||||
const [replyMessage, setReplyMessage] = useState("");
|
||||
const [listDiscussion, setListDiscussion] = useState();
|
||||
const [message, setMessage] = 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 {
|
||||
control,
|
||||
|
|
@ -90,53 +108,32 @@ export default function FormDetailEscalation() {
|
|||
const handleReply = () => {
|
||||
setReplyVisible((prev) => !prev); // Toggle visibility
|
||||
};
|
||||
// useEffect(() => {
|
||||
// async function initState() {
|
||||
// if (id != undefined) {
|
||||
// loading();
|
||||
// const responseGet = await getEscalationDiscussion(id);
|
||||
// close();
|
||||
|
||||
const handleSendReply = async () => {
|
||||
if (replyMessage.trim() === "") {
|
||||
MySwal.fire({
|
||||
title: "Error",
|
||||
text: "Pesan tidak boleh kosong!",
|
||||
icon: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// console.log("escal data", responseGet?.data);
|
||||
// setListDiscussion(responseGet?.data?.data);
|
||||
// }
|
||||
// }
|
||||
// initState();
|
||||
// }, [id]);
|
||||
|
||||
const data = {
|
||||
ticketId: id,
|
||||
const handleSendReply = () => {
|
||||
if (replyMessage.trim() === "") return;
|
||||
|
||||
const newReply = {
|
||||
id: replies.length + 1,
|
||||
name: "Mabes Polri - Approver", // Sesuaikan dengan data dinamis jika ada
|
||||
message: replyMessage,
|
||||
timestamp: new Date().toISOString().slice(0, 19).replace("T", " "),
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await saveTicketInternalReply(data);
|
||||
|
||||
// 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);
|
||||
}
|
||||
setReplies([...replies, newReply]);
|
||||
setReplyMessage("");
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -244,26 +241,46 @@ export default function FormDetailEscalation() {
|
|||
</p>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="mt-4 px-3 mb-3">
|
||||
<Label htmlFor="replyMessage">Tulis Pesan</Label>
|
||||
<div className="mx-3 my-3">
|
||||
<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
|
||||
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}
|
||||
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">
|
||||
<Button
|
||||
onClick={() => setReplyVisible(false)}
|
||||
color="default"
|
||||
variant="outline"
|
||||
<div className="flex justify-end gap-3 mt-2 mb-3">
|
||||
<button
|
||||
onClick={() => setReplyMessage("")}
|
||||
className="px-4 py-2 bg-gray-200 text-gray-800 rounded-md"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button onClick={handleSendReply} color="primary">
|
||||
Kirim
|
||||
</Button>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSendReply}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-md"
|
||||
>
|
||||
Kirim Pesan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -55,6 +55,7 @@ import { getCookiesDecrypt } from "@/lib/utils";
|
|||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import { error } from "@/lib/swal";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -683,7 +684,7 @@ export default function FormVideoDetail() {
|
|||
<Icon icon="humbleicons:times" color="red" />
|
||||
</a>
|
||||
</div>
|
||||
{isUserMabesApprover &&
|
||||
{isUserMabesApprover && (
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
|
|
@ -760,7 +761,7 @@ export default function FormVideoDetail() {
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
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 { addDays, format, parseISO, setDate } from "date-fns";
|
||||
import { DateRange } from "react-day-picker";
|
||||
|
|
@ -28,7 +28,19 @@ import MapHome from "@/components/maps/MapHome";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
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({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -56,6 +68,8 @@ export default function FormEventDetail() {
|
|||
const [startTime, setStartTime] = useState("08:00");
|
||||
const [endTime, setEndTime] = useState("09:00");
|
||||
const [date, setDate] = useState<DateRange | undefined>();
|
||||
const [todayList, setTodayList] = useState([]);
|
||||
const [nextDayList, setNextDayList] = useState([]);
|
||||
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
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(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
|
|
@ -89,6 +113,7 @@ export default function FormEventDetail() {
|
|||
setStartTime(details.startTime);
|
||||
setEndTime(details.endTime);
|
||||
}
|
||||
getDataByDate();
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -315,7 +340,66 @@ export default function FormEventDetail() {
|
|||
</div>
|
||||
</Card>
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -40,6 +39,7 @@ import { Textarea } from "@/components/ui/textarea";
|
|||
import { error } from "@/lib/swal";
|
||||
import Cookies from "js-cookie";
|
||||
import { postSchedule } from "@/service/schedule/schedule";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
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 { addDays, format, parseISO, setDate } from "date-fns";
|
||||
import { DateRange } from "react-day-picker";
|
||||
|
|
@ -28,7 +28,12 @@ import MapHome from "@/components/maps/MapHome";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
import Cookies from "js-cookie";
|
||||
import { detailSchedule, postSchedule } from "@/service/schedule/schedule";
|
||||
import {
|
||||
detailSchedule,
|
||||
listScheduleNext,
|
||||
listScheduleToday,
|
||||
postSchedule,
|
||||
} from "@/service/schedule/schedule";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -36,6 +41,13 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import { formatDate } from "@fullcalendar/core/index.js";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -64,6 +76,8 @@ export default function FormDetailPressRillis() {
|
|||
const [endTime, setEndTime] = useState("09:00");
|
||||
const [date, setDate] = useState<DateRange | undefined>();
|
||||
const [selectedTarget, setSelectedTarget] = useState("all");
|
||||
const [todayList, setTodayList] = useState([]);
|
||||
const [nextDayList, setNextDayList] = useState([]);
|
||||
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
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(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
|
|
@ -97,6 +121,7 @@ export default function FormDetailPressRillis() {
|
|||
setStartTime(details.startTime);
|
||||
setEndTime(details.endTime);
|
||||
}
|
||||
getDataByDate();
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -340,7 +365,66 @@ export default function FormDetailPressRillis() {
|
|||
</div>
|
||||
</Card>
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
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 { addDays, format, parseISO, setDate } from "date-fns";
|
||||
import { DateRange } from "react-day-picker";
|
||||
|
|
@ -28,7 +28,20 @@ import MapHome from "@/components/maps/MapHome";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { error, loading } from "@/lib/swal";
|
||||
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({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
@ -56,6 +69,8 @@ export default function FormDetailPressConference() {
|
|||
const [startTime, setStartTime] = useState("08:00");
|
||||
const [endTime, setEndTime] = useState("09:00");
|
||||
const [date, setDate] = useState<DateRange | undefined>();
|
||||
const [todayList, setTodayList] = useState([]);
|
||||
const [nextDayList, setNextDayList] = useState([]);
|
||||
|
||||
const [detail, setDetail] = useState<Detail>();
|
||||
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(() => {
|
||||
async function initState() {
|
||||
if (id) {
|
||||
|
|
@ -89,6 +114,7 @@ export default function FormDetailPressConference() {
|
|||
setStartTime(details.startTime);
|
||||
setEndTime(details.endTime);
|
||||
}
|
||||
getDataByDate();
|
||||
}
|
||||
}
|
||||
initState();
|
||||
|
|
@ -315,7 +341,66 @@ export default function FormDetailPressConference() {
|
|||
</div>
|
||||
</Card>
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||
import * as z from "zod";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -30,6 +29,7 @@ import { Textarea } from "@/components/ui/textarea";
|
|||
import { error } from "@/lib/swal";
|
||||
import Cookies from "js-cookie";
|
||||
import { postSchedule } from "@/service/schedule/schedule";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const taskSchema = z.object({
|
||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ export default function FormTaskDetail() {
|
|||
allUnit: false,
|
||||
mabes: false,
|
||||
polda: false,
|
||||
satker: false,
|
||||
polres: false,
|
||||
});
|
||||
|
||||
|
|
@ -389,6 +390,7 @@ export default function FormTaskDetail() {
|
|||
mabes: outputSet.has(1),
|
||||
polda: outputSet.has(2),
|
||||
polres: outputSet.has(3),
|
||||
satker: outputSet.has(4),
|
||||
});
|
||||
}
|
||||
}, [detail?.fileTypeOutput]);
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ export default function FormTaskEdit() {
|
|||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
satker: false,
|
||||
});
|
||||
|
||||
const [links, setLinks] = useState<string[]>([""]);
|
||||
|
|
@ -265,6 +266,7 @@ export default function FormTaskEdit() {
|
|||
mabes: outputSet.has(1),
|
||||
polda: outputSet.has(2),
|
||||
polres: outputSet.has(3),
|
||||
satker: outputSet.has(4),
|
||||
});
|
||||
}
|
||||
}, [detail?.fileTypeOutput]);
|
||||
|
|
@ -299,6 +301,7 @@ export default function FormTaskEdit() {
|
|||
mabes: "1",
|
||||
polda: "2",
|
||||
polres: "3",
|
||||
satker: "4",
|
||||
};
|
||||
const assignmentPurposeString = Object.keys(unitSelection)
|
||||
.filter((key) => unitSelection[key as keyof typeof unitSelection])
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ export default function FormTask() {
|
|||
mabes: false,
|
||||
polda: false,
|
||||
polres: false,
|
||||
satker: false,
|
||||
});
|
||||
const [links, setLinks] = useState<string[]>([""]);
|
||||
|
||||
|
|
@ -149,6 +150,7 @@ export default function FormTask() {
|
|||
try {
|
||||
const response = await getUserLevelForAssignments();
|
||||
setListDest(response?.data?.data.list);
|
||||
console.log("polda", response?.data?.data?.list);
|
||||
const initialExpandedState = response?.data?.data.list.reduce(
|
||||
(acc: any, polda: any) => {
|
||||
acc[polda.id] = false;
|
||||
|
|
@ -198,6 +200,7 @@ export default function FormTask() {
|
|||
mabes: "1",
|
||||
polda: "2",
|
||||
polres: "3",
|
||||
satker: "4",
|
||||
};
|
||||
const assignmentPurposeString = Object.keys(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(
|
||||
size: number,
|
||||
|
|
@ -22,5 +26,13 @@ export async function getBlog(id: any) {
|
|||
|
||||
export async function uploadThumbnailBlog(id: any, data: any) {
|
||||
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}`;
|
||||
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";
|
||||
|
||||
export async function paginationSchedule(
|
||||
|
|
@ -24,21 +27,30 @@ export async function detailSchedule(id: any) {
|
|||
|
||||
export async function listScheduleTodayPublic(group = null) {
|
||||
const url = `public/schedule/today?group=${group}`;
|
||||
return httpGet( url, null );
|
||||
return httpGet(url, null);
|
||||
}
|
||||
|
||||
export async function listScheduleNextPublic(group = null) {
|
||||
const url = `public/schedule/next-activity?group=${group}`;
|
||||
return httpGet( url, null );
|
||||
return httpGet(url, null);
|
||||
}
|
||||
|
||||
export async function listSchedulePrevPublic(group = null) {
|
||||
const url = `public/schedule/prev-activity?group=${group}`;
|
||||
return httpGet( url, null );
|
||||
return httpGet(url, null);
|
||||
}
|
||||
|
||||
export async function listSchedule(group = null) {
|
||||
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