From 0e338fa921c14d190023ad7319d9d18c726b4d0e Mon Sep 17 00:00:00 2001 From: Muhammad Eko Date: Tue, 1 Oct 2024 15:47:20 +0700 Subject: [PATCH] progress --- src/app/(dashboard)/absensi/page.tsx | 106 +++++++++++++++ src/app/(dashboard)/karyawan/page.tsx | 67 ++++++++++ src/app/(dashboard)/layout.tsx | 134 +++++++++++++++++++ src/app/(dashboard)/turnover/page.tsx | 179 ++++++++++++++++++++++++++ 4 files changed, 486 insertions(+) create mode 100644 src/app/(dashboard)/absensi/page.tsx create mode 100644 src/app/(dashboard)/karyawan/page.tsx create mode 100644 src/app/(dashboard)/layout.tsx create mode 100644 src/app/(dashboard)/turnover/page.tsx diff --git a/src/app/(dashboard)/absensi/page.tsx b/src/app/(dashboard)/absensi/page.tsx new file mode 100644 index 0000000..5bcfc9e --- /dev/null +++ b/src/app/(dashboard)/absensi/page.tsx @@ -0,0 +1,106 @@ +"use client" +import { useAppSelector } from "@/lib/hooks"; +import { useGetAttendanceRangeQuery, useGetMonthlyAttendanceQuery, useGetOrganizationAttendanceQuery } from "@/services/api"; +import { ExclamationCircleIcon } from "@heroicons/react/24/outline"; +import { BarChart, ChartsTooltip, LineChart, pieArcClasses, pieArcLabelClasses, PieChart } from "@mui/x-charts"; +import { ChartsNoDataOverlay } from "@mui/x-charts/ChartsOverlay"; +import { formatDate } from "date-fns"; + +export default function AbsensiPage() { + const filter = useAppSelector(state => state.filter.filter); + const {data: attendanceSummary} = useGetOrganizationAttendanceQuery(filter); + const {data: attendanceRange} = useGetAttendanceRangeQuery(filter); + const {data: montlyAttendance} = useGetMonthlyAttendanceQuery(filter); + + return ( +
+
+
Kehadiran Staff
+
+ {attendanceRange && `${v.label} HK`, + data: attendanceRange.map(e => ({label: e.range, value: e.count})), + innerRadius: "50%", + valueFormatter: (v) => v.value+" employees" + }, + ]} colors={[ + "#E65550","#8A5FB1","#69C9C9","#F08431","#F6C421" + ]} + slotProps={{ + legend: { + direction: "row", + position: {vertical: "bottom", horizontal: "middle"} + }, + pieArcLabel: { + classes: { + root: "text-white fill-current" + } + }, + }} + sx={{ + [`& .${pieArcLabelClasses.root}`]: { + color:"white", + fontWeight: "bold" + }}} + margin={{top: 20, right: 20, bottom: 20, left: 20}} + slots={{ + noDataOverlay: (props) => + }} + />} +
+
+
+
Kehadiran Non Staff
+
+ Data belum tersedia +
+
+
+
+
Kehadiran Non Staff
+
+ Data belum tersedia +
+
+
+
Kehadiran Non Staff
+
+ Data belum tersedia +
+
+
+
+
Data Kehadiran Karyawan Setiap Perusahaan
+
+ {attendanceSummary && v === "tooltip" ? "Attendance" : undefined!, color: "#2385DE", stack: "attendance", stackOffset: "expand", valueFormatter: (v) => v + " mandays"}, + {dataKey: "absent", label: (v) => v === "tooltip" ? "Absent" : undefined!, color: "#F7B500", stack: "attendance", stackOffset: "expand", valueFormatter: (v) => v + " mandays"}, + ]} xAxis={[ + {dataKey: "organization_code", label: "Nama Perusahaan", scaleType: "band", valueFormatter: (v, context) => context.location === "tooltip" ? attendanceSummary.find(e => e.organization_code === v)?.organization_name : v} + ]} + yAxis={[ + {dataKey: "count",scaleType: "linear", valueFormatter: (v) => (v*100)+"%"} + ]} + slots={{ + noDataOverlay: ()=> , + }} + />} +
+
+
+
Data Karyawan Perbulan
+
+ {montlyAttendance && "Kehadiran" , area: false, color: "#F7CAA9"}, + {dataKey: "workdays", label: (v) => "Mandays", area: false, color: "#2385DE"}, + ]} xAxis={[ + {dataKey: "date", label: "Bulan", scaleType: "band", valueFormatter: (v, context) => formatDate(new Date(v), context.location === "tooltip" ? "MMMM yyyy" : "MMM")} + ]} + />} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/(dashboard)/karyawan/page.tsx b/src/app/(dashboard)/karyawan/page.tsx new file mode 100644 index 0000000..c53be00 --- /dev/null +++ b/src/app/(dashboard)/karyawan/page.tsx @@ -0,0 +1,67 @@ +"use client" +import { useAppSelector } from "@/lib/hooks"; +import { useGetEmployeeSummaryQuery, useGetMonthlyEmployeeQuery } from "@/services/api"; +import { ExclamationCircleIcon } from "@heroicons/react/24/outline"; +import { BarChart, ChartsLegend, ChartsTooltip, ChartsXAxis, ChartsYAxis, LineChart } from "@mui/x-charts"; +import { formatDate } from "date-fns"; +import { useEffect } from "react"; + +export default function KaryawanPage() { + + const filter = useAppSelector(state => state.filter.filter); + const {data: employeeSummary} = useGetEmployeeSummaryQuery(filter); + const {data: montlyEmployee} = useGetMonthlyEmployeeQuery(filter); + + useEffect(() => { + console.log(filter); + }, [filter]); + + return ( +
+
+
Data Karyawan
+
+ {employeeSummary && v === "tooltip" ? "Jumlah Karyawan" : undefined!, color: "#2385DE"} + ]} xAxis={[ + {dataKey: "organization_code", label: "Nama Perusahaan", scaleType: "band", valueFormatter: (v, context) => context.location === "tooltip" ? employeeSummary.find(e => e.organization_code === v)?.organization_name : v} + ]} />} +
+
+
+
Data Karyawan Perbulan
+
+ {montlyEmployee && v === "tooltip" ? "Jumlah Karyawan" : undefined!, area: true, color: "#F7CAA9"} + ]} xAxis={[ + {dataKey: "date", label: "Bulan", scaleType: "band", valueFormatter: (v, context) => formatDate(new Date(v), context.location === "tooltip" ? "MMMM yyyy" : "MMM")} + ]} />} +
+
+
+
Pergerakan Karyawan
+
+ Data belum tersedia +
+
+
+
Ranking (Top 10) Total Pergerakan Karyawan Setiap Perusahaan
+
+ Data belum tersedia +
+
+
+
Penjatuhan Sanksi
+
+ Data belum tersedia +
+
+
+
Ranking (Top 10) Total Penjatuhan Sanksi Setiap Perusahaan
+
+ Data belum tersedia +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx new file mode 100644 index 0000000..fd3d284 --- /dev/null +++ b/src/app/(dashboard)/layout.tsx @@ -0,0 +1,134 @@ +"use client" +import { DocumentCurrencyDollarIcon, HomeIcon, UsersIcon } from "@heroicons/react/24/outline"; +import { InputLabel, MenuItem, Select, TextField } from "@mui/material"; +import { DatePicker } from "@mui/x-date-pickers"; +import { BarChart2, CircleDollarSign, LogOut, RefreshCcw } from "lucide-react"; +import Link from "next/link"; +import logo from "../../../public/images/logo.png"; +import midsuit from "../../../public/images/midsuit.png"; +import Image from "next/image"; +import { usePathname, useRouter } from "next/navigation"; +import React from "react"; +import { Filter } from "@/services/types"; +import { useAppDispatch, useAppSelector } from "@/lib/hooks"; +import { setFilter } from "@/lib/slice/filter"; +import { format } from "date-fns"; + + +export default function DashboardLayout({children}:{children: React.ReactNode}) { + + const pathname = usePathname(); + const dispatch = useAppDispatch(); + const filter = useAppSelector((state) => state.filter.filter); + + return ( +
+
+
+ logo +
+
+
+
Halaman
+ + + + + Data Karyawan + + + + + + Absensi + + + + + + Turn Over Rate + + + + + + Produktifitas Karyawan + + + + + + HR Cost + + + + + + Log Out + +
+ Powered by +
+ midsuit +
+
+
+
+
+
+
+ ID +
+ CN +
+
+ dispatch(setFilter({...filter, start_date: format(date ?? new Date(), "yyyy-MM-dd")}))} + /> + dispatch(setFilter({...filter, end_date: format(date ?? new Date(), "yyyy-MM-dd")}))} + /> +
+
+ + Semua Perusahaan + PT. A + PT. B + PT. C + + + Semua Lokasi + Lokasi 1 + Lokasi 2 + Lokasi 3 + +
+
+ dispatch(setFilter({...filter, job_name: e.target.value}))}> + Semua + Staff + Non Staff + Pemanen + Perawatan + +
+
+
+ {children} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/app/(dashboard)/turnover/page.tsx b/src/app/(dashboard)/turnover/page.tsx new file mode 100644 index 0000000..3f39f0b --- /dev/null +++ b/src/app/(dashboard)/turnover/page.tsx @@ -0,0 +1,179 @@ +"use client" +import { useAppSelector } from "@/lib/hooks"; +import { useDimensions } from "@/lib/hooks/dimension"; +import { useGetResignCategoryQuery, useGetResignReasonQuery, useGetResignSummaryQuery, useGetResignTypeQuery } from "@/services/api"; +import { BarChart, pieArcLabelClasses, PieChart } from "@mui/x-charts"; +import { ChartsNoDataOverlay } from "@mui/x-charts/ChartsOverlay"; +import { Loader, LucideLoader2 } from "lucide-react"; +import React from "react"; +import { useRef } from "react"; +import { Tooltip } from "react-tooltip"; + +export default function TurnoverPage() { + + const filter = useAppSelector(state => state.filter.filter); + const resignRef = useRef(null); + const {width, height} = useDimensions(resignRef); + const {data : resignSummary, isFetching: resignSummaryLoading} = useGetResignSummaryQuery(filter); + const {data : resignType, isFetching: resignTypeLoading} = useGetResignTypeQuery(filter); + const {data : resignCategory, isFetching: resignCategoryLoading} = useGetResignCategoryQuery(filter); + const {data : resignReason, isFetching: resignReasonLoading} = useGetResignReasonQuery(filter); + + const [turnOverRatio, setTurnOverRatio] = React.useState(0); + const [resign, setResign] = React.useState(0); + const [active, setActive] = React.useState(0); + + const [maxResignType, setMaxResignType] = React.useState(0); + + React.useEffect(() => { + if(resignSummary){ + const totalResign = resignSummary.reduce((acc, curr) => acc + curr.count, 0); + const totalActive = resignSummary.reduce((acc, curr) => acc + curr.active, 0); + setTurnOverRatio(Math.round(totalResign / totalActive * 100)); + setResign(totalResign); + setActive(totalActive); + } + }, [resignSummary]); + + React.useEffect(() => { + if(resignType){ + const max = resignType.reduce((acc, curr) => Math.max(acc, curr.count), 0); + setMaxResignType(max); + } + }, [resignType]); + + const getColorByCategory = (category: string) => { + switch(category){ + case "Whitelist": + return "#EAEAEA"; + case "Blacklist": + return "#3D3D3D"; + case "Yellowlist": + return "#F5C41F"; + default: + return undefined; + } + } + + return ( +
+
+
Karyawan Baru Seluruh Perusahaan
+
+ Data belum tersedia +
+
+
+
Man Power Planning per Perusahaan : Recruitment
+
+ Data belum tersedia +
+
+
+
Karyawan Resign Seluruh Perusahaan
+
+
+
+
+
+
Rasio Turn Over
+
{turnOverRatio} %
+
+
+
Karyawan Resign
+
{resign}
+
+
+
Karyawan Aktif
+
{active}
+
+
+
+
+
Resignment per Perusahaan : Jumlah Karyawan
+
+ {resignSummary && !resignSummaryLoading && v === "tooltip" ? "Resign" : undefined!, color: "#F7B500", stack: "resign", stackOffset: "expand"}, + {dataKey: "active", label: (v) => v === "tooltip" ? "Active" : undefined!, color: "#2385DE", stack: "resign", stackOffset: "expand"}, + ]} xAxis={[ + {dataKey: "organization_code", label: "Nama Perusahaan", scaleType: "band", valueFormatter: (v, context) => context.location === "tooltip" ? resignSummary.find(e => e.organization_code === v)?.organization_name : v} + ]} + yAxis={[ + {dataKey: "count",scaleType: "linear", valueFormatter: (v) => (v*100)+"%"} + ]} + slots={{ + noDataOverlay: ()=> , + }} + />} + {resignSummaryLoading && } +
+
+
+
Jenis Pemutusan Hubungan Kerja
+
+ {resignType && resignType.map((resign, index) => ( +
+
{resign.type}
+
+
+
+
+
+ ))} + {resignType && resignType.length === 0 && Data belum tersedia} + +
+
+
+
Kategory Resign
+
+ {resignCategory && `${v.label}`, + data: resignCategory.map(e => ({label: e.category, value: e.count, color: getColorByCategory(e.category)})), + valueFormatter: (v) => v.value+" employees", + }, + ]} + slotProps={{ + legend: { + direction: "row", + position: {vertical: "bottom", horizontal: "middle"} + }, + pieArcLabel: { + classes: { + root: "hidden" + } + }, + }} + sx={{ + [`& .${pieArcLabelClasses.root}`]: { + color:"white", + fontWeight: "bold" + }}} + margin={{top: 20, right: 20, bottom: 60, left: 20}} + slots={{ + noDataOverlay: (props) => + }} + />} +
+
+
+
Jenis Pemutusan Hubungan Kerja
+
+ {resignReason && resignReason.map((resign, index) => ( +
+
{resign.reason}
+
+
+
+
+
+ ))} + {resignReason && resignReason.length === 0 && Data belum tersedia} + +
+
+
+ ) +} \ No newline at end of file