feat: update agenda settings
This commit is contained in:
parent
aa48619491
commit
6dcdeacf08
|
|
@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import ExternalDraggingevent from "./dragging-events";
|
import ExternalDraggingevent from "./dragging-events";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { EventContentArg } from "@fullcalendar/core";
|
import { EventContentArg } from "@fullcalendar/core";
|
||||||
|
|
@ -19,6 +19,12 @@ import { getAgendaSettingsList } from "@/service/agenda-setting/agenda-setting";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { CalendarCategory } from "./data";
|
import { CalendarCategory } from "./data";
|
||||||
|
import { error } from "@/config/swal";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
|
||||||
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
interface CalendarViewProps {
|
interface CalendarViewProps {
|
||||||
|
|
@ -26,6 +32,21 @@ interface CalendarViewProps {
|
||||||
categories: CalendarCategory[];
|
categories: CalendarCategory[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CalendarEvent = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
start: Date;
|
||||||
|
end: Date;
|
||||||
|
createBy: string;
|
||||||
|
createdByName: string;
|
||||||
|
allDay: boolean;
|
||||||
|
extendedProps: {
|
||||||
|
calendar: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const INITIAL_YEAR = dayjs().format("YYYY");
|
const INITIAL_YEAR = dayjs().format("YYYY");
|
||||||
const INITIAL_MONTH = dayjs().format("M");
|
const INITIAL_MONTH = dayjs().format("M");
|
||||||
|
|
||||||
|
|
@ -42,6 +63,33 @@ export interface AgendaSettingsAPIResponse {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdById: number | null;
|
createdById: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface YearlyData {
|
||||||
|
january?: Event[];
|
||||||
|
february?: Event[];
|
||||||
|
march?: Event[];
|
||||||
|
april?: Event[];
|
||||||
|
may?: Event[];
|
||||||
|
june?: Event[];
|
||||||
|
july?: Event[];
|
||||||
|
august?: Event[];
|
||||||
|
september?: Event[];
|
||||||
|
october?: Event[];
|
||||||
|
november?: Event[];
|
||||||
|
december?: Event[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MonthCardProps {
|
||||||
|
monthId: keyof YearlyData;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ListItemProps {
|
||||||
|
item: any;
|
||||||
|
text: string
|
||||||
|
createdBy: string;
|
||||||
|
bgColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface APIResponse {
|
interface APIResponse {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
|
|
@ -63,6 +111,8 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
// event canvas state
|
// event canvas state
|
||||||
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
const [sheetOpen, setSheetOpen] = useState<boolean>(false);
|
||||||
const [date, setDate] = React.useState<Date>(new Date());
|
const [date, setDate] = React.useState<Date>(new Date());
|
||||||
|
const [activeView, setActiveView] = useState("dayGridMonth");
|
||||||
|
const [yearlyData, setYearlyData] = useState();
|
||||||
|
|
||||||
const [dragEvents] = useState([
|
const [dragEvents] = useState([
|
||||||
{ title: "New Event Planning", id: "101", tag: "business" },
|
{ title: "New Event Planning", id: "101", tag: "business" },
|
||||||
|
|
@ -75,6 +125,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCalendarEvents();
|
getCalendarEvents();
|
||||||
|
getYearlyEvents();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCalendarEvents = async () => {
|
const getCalendarEvents = async () => {
|
||||||
|
|
@ -82,6 +133,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
console.log("API Response:", res);
|
console.log("API Response:", res);
|
||||||
|
|
||||||
if (res?.error) {
|
if (res?.error) {
|
||||||
|
error(res?.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +157,15 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
console.log("event", events);
|
console.log("event", events);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getYearlyEvents = async () => {
|
||||||
|
const res = await getAgendaSettingsList(INITIAL_YEAR, '', '');
|
||||||
|
if (res?.error) {
|
||||||
|
error(res.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setYearlyData(res?.data?.data);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedCategory(categories?.map((c) => c.value));
|
setSelectedCategory(categories?.map((c) => c.value));
|
||||||
}, [categories]);
|
}, [categories]);
|
||||||
|
|
@ -212,11 +273,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
|
|
||||||
// event click
|
// event click
|
||||||
const handleEventClick = (arg: any) => {
|
const handleEventClick = (arg: any) => {
|
||||||
|
console.log("Event Click ", arg);
|
||||||
setSelectedEventDate(null);
|
setSelectedEventDate(null);
|
||||||
setSheetOpen(true);
|
setSheetOpen(true);
|
||||||
setApiEvents(arg);
|
setApiEvents(arg);
|
||||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle close modal
|
// handle close modal
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setSheetOpen(false);
|
setSheetOpen(false);
|
||||||
|
|
@ -225,6 +288,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
};
|
};
|
||||||
const handleDateClick = (arg: any) => {
|
const handleDateClick = (arg: any) => {
|
||||||
setSheetOpen(true);
|
setSheetOpen(true);
|
||||||
|
console.log("Date :", arg);
|
||||||
setSelectedEventDate(arg);
|
setSelectedEventDate(arg);
|
||||||
setApiEvents([]);
|
setApiEvents([]);
|
||||||
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||||
|
|
@ -266,6 +330,142 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDateChange = (startDate: string, endDate: string) => {
|
||||||
|
console.log(startDate + " - " + endDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewChange = (viewType: string) => {
|
||||||
|
console.log(viewType);
|
||||||
|
setActiveView(viewType);
|
||||||
|
};
|
||||||
|
|
||||||
|
const months: Array<{ id: keyof YearlyData; label: string }> = [
|
||||||
|
{ id: 'january', label: 'Januari' },
|
||||||
|
{ id: 'february', label: 'Februari' },
|
||||||
|
{ id: 'march', label: 'Maret' },
|
||||||
|
{ id: 'april', label: 'April' },
|
||||||
|
{ id: 'may', label: 'Mei' },
|
||||||
|
{ id: 'june', label: 'Juni' },
|
||||||
|
{ id: 'july', label: 'Juli' },
|
||||||
|
{ id: 'august', label: 'Agustus' },
|
||||||
|
{ id: 'september', label: 'September' },
|
||||||
|
{ id: 'october', label: 'Oktober' },
|
||||||
|
{ id: 'november', label: 'November' },
|
||||||
|
{ id: 'december', label: 'Desember' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEventColor = (type: Event['type']): string => {
|
||||||
|
const colors: Record<Event['type'], string> = {
|
||||||
|
mabes: 'bg-yellow-500',
|
||||||
|
polda: 'bg-blue-400',
|
||||||
|
polres: 'bg-slate-400',
|
||||||
|
international: 'bg-green-400'
|
||||||
|
};
|
||||||
|
return colors[type];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickListItem = (item: any) => {
|
||||||
|
const formattedEvent: CalendarEvent = {
|
||||||
|
id: item.id.toString(),
|
||||||
|
title: item.title,
|
||||||
|
start: new Date(item.startDate),
|
||||||
|
end: new Date(item.endDate),
|
||||||
|
createBy: "Mabes Polri - Approver", // Sesuaikan dengan data yang sebenarnya jika ada
|
||||||
|
createdByName: item.createdByName,
|
||||||
|
allDay: true,
|
||||||
|
extendedProps: {
|
||||||
|
calendar: item.agendaType,
|
||||||
|
description: item.description
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const finalEvent: any = {
|
||||||
|
event: formattedEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Event click custom : ", finalEvent);
|
||||||
|
|
||||||
|
setSelectedEventDate(null);
|
||||||
|
setSheetOpen(true);
|
||||||
|
setApiEvents(finalEvent);
|
||||||
|
wait().then(() => (document.body.style.pointerEvents = "auto"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem: React.FC<ListItemProps> = ({ item, text, createdBy, bgColor }) => (
|
||||||
|
<div className={`w-full p-1 mb-2 rounded-md text-white text-sm ${bgColor}`} onClick={() => handleClickListItem(item)}>
|
||||||
|
<p className="ml-1">{text}</p>
|
||||||
|
<p className="ml-1 text-xs text-start mt-2">
|
||||||
|
Create By: {createdBy}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const MonthCard: React.FC<MonthCardProps> = (props: any) => {
|
||||||
|
const { monthId, label } = props;
|
||||||
|
const events: any = yearlyData?.[monthId];
|
||||||
|
const displayedEvents = events?.slice(0, 3);
|
||||||
|
const hasMoreEvents = events?.length > 3;
|
||||||
|
return (
|
||||||
|
<div className="flex-1 bg-white rounded-lg shadow-sm border border-gray-200 pb-3 mr-1">
|
||||||
|
<div className="py-3">
|
||||||
|
<h4 className="font-bold text-center">{label}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-2">
|
||||||
|
{events?.length === 0 ? (
|
||||||
|
<div className="mt-1 py-2 rounded-lg bg-white border border-black">
|
||||||
|
<p className="text-center">Belum ada data</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{displayedEvents?.map((event: any, index: number) => (
|
||||||
|
<ListItem
|
||||||
|
key={index}
|
||||||
|
item={event}
|
||||||
|
text={event.title}
|
||||||
|
createdBy={event.createdByName}
|
||||||
|
bgColor={getEventColor(event.agendaType)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{hasMoreEvents && (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button className="w-full" size="sm">
|
||||||
|
Lihat lebih banyak
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
side="top"
|
||||||
|
className="p-0 overflow-hidden bg-transparent shadow-none border-none"
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="bg-default p-2 rounded-t-lg">
|
||||||
|
<CardTitle className="text-default-foreground text-base py-0">{label}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="p-2 text-sm">
|
||||||
|
<div className="max-h-[300px] overflow-y-auto border rounded-md border-gray-300 p-2">
|
||||||
|
{events.map((event: any, index: number) => (
|
||||||
|
<ListItem
|
||||||
|
key={index}
|
||||||
|
item={event}
|
||||||
|
text={event.title}
|
||||||
|
createdBy={event.createdByName}
|
||||||
|
bgColor={getEventColor(event.agendaType)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
<div className="grid grid-cols-12 gap-6 divide-x divide-border">
|
||||||
|
|
@ -344,7 +544,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
headerToolbar={{
|
headerToolbar={{
|
||||||
left: "prev,next today",
|
left: "prev,next today",
|
||||||
center: "title",
|
center: "title",
|
||||||
right: "dayGridMonth,listWeek,",
|
right: "listYear,dayGridMonth,listMonth",
|
||||||
|
}}
|
||||||
|
views={{
|
||||||
|
listYear: {
|
||||||
|
type: "dayGridMonth",
|
||||||
|
buttonText: "Year",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
events={displayedEvents}
|
events={displayedEvents}
|
||||||
editable={true}
|
editable={true}
|
||||||
|
|
@ -358,9 +564,47 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
eventClassNames={handleClassName}
|
eventClassNames={handleClassName}
|
||||||
dateClick={handleDateClick}
|
dateClick={handleDateClick}
|
||||||
eventClick={handleEventClick}
|
eventClick={handleEventClick}
|
||||||
initialView="dayGridMonth"
|
initialView="listYear"
|
||||||
eventContent={renderEventContent}
|
eventContent={renderEventContent}
|
||||||
|
viewDidMount={(info) => handleViewChange(info.view.type)}
|
||||||
|
datesSet={(info: any) => {
|
||||||
|
console.log(info);
|
||||||
|
handleDateChange(info.view.currentStart, info.view.currentEnd);
|
||||||
|
handleViewChange(info.view.type);
|
||||||
|
}}
|
||||||
|
viewClassNames={activeView === "listYear" ? "hide-calendar-grid" : ""}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{activeView === "listYear" && (
|
||||||
|
<div className="custom-ui">
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(0, 3).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Second Row */}
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(3, 6).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Third Row */}
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(6, 9).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Fourth Row */}
|
||||||
|
<div className="flex gap-1 mt-1">
|
||||||
|
{months.slice(9, 12).map(month => (
|
||||||
|
<MonthCard key={month.id} monthId={month.id} label={month.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -376,17 +620,3 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CalendarView;
|
export default CalendarView;
|
||||||
|
|
||||||
export type CalendarEvent = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
start: Date;
|
|
||||||
end: Date;
|
|
||||||
createBy: string;
|
|
||||||
createdByName: string;
|
|
||||||
allDay: boolean;
|
|
||||||
extendedProps: {
|
|
||||||
calendar: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,8 @@ const EventModal = ({
|
||||||
}
|
}
|
||||||
fetchPoldaPolres();
|
fetchPoldaPolres();
|
||||||
console.log("Event", event);
|
console.log("Event", event);
|
||||||
|
|
||||||
|
console.log("Event click modal : ", event);
|
||||||
}, [agendaType]);
|
}, [agendaType]);
|
||||||
|
|
||||||
const handleCheckboxChange = (levelId: number) => {
|
const handleCheckboxChange = (levelId: number) => {
|
||||||
|
|
@ -299,6 +301,7 @@ const EventModal = ({
|
||||||
const file = new File([blob], "voiceNote.webm", { type: "audio/webm" });
|
const file = new File([blob], "voiceNote.webm", { type: "audio/webm" });
|
||||||
setAudioFile(file);
|
setAudioFile(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteAudio = () => {
|
const handleDeleteAudio = () => {
|
||||||
// Remove the audio file by setting state to null
|
// Remove the audio file by setting state to null
|
||||||
setAudioFile(null);
|
setAudioFile(null);
|
||||||
|
|
|
||||||
|
|
@ -577,4 +577,9 @@ html[dir="rtl"] .react-select .select__loading-indicator {
|
||||||
|
|
||||||
.ck-editor__editable_inline {
|
.ck-editor__editable_inline {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide FullCalendar grid elements */
|
||||||
|
.fc-view-harness:has(.hide-calendar-grid) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue